Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 88 additions & 77 deletions .claude/references/deployment-devops.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
# GForce Deployment & DevOps

The pipeline implementation lives in
[shared-github-actions](https://github.com/Gforce-Innovation-Kft/shared-github-actions)
(`sf-pr-validate.yml` / `sf-release.yml` reusable workflows); this repo only
ships thin callers. Operational setup guide: [docs/CICD.md](../../docs/CICD.md).
If the pipeline needs something the shared workflows lack, raise a change
request there — never copy workflow logic into this repo.

---

## 1. Branching Strategy
## 1. Branching Strategy (trunk-based)

```
main ← production-ready; protected; requires PR + approval + CI pass
└── develop ← integration branch; auto-deploys to staging
└── feature/REQ-001-account-enhancements ← short-lived, per-ticket
└── feature/REQ-002-lwc-account-form
└── hotfix/critical-bug-fix ← branches from main, merges to main + develop
main ← protected by ruleset; every merge deploys via the devhub gate
└── feature/REQ-001-account-enhancements ← short-lived, per-ticket
└── feature/REQ-002-lwc-account-form
└── hotfix/critical-bug-fix ← same flow, expedited review
```

- `main` → production (deploy-production.yml, manual approval gate)
- `develop` → staging (deploy-staging.yml, auto on push)
- `feature/*` → scratch org only (validated by validate-pr.yml on PR to develop)
- `hotfix/*` → branches from `main`, merges to both `main` and `develop`
- `feature/*` → PR to `main` → validated by `pr-validate.yml` (`jest` +
`scratch-org` deploy/test) and `release.yml`'s `validate` job (delta
check-only deploy with selected Apex tests against the Dev Hub)
- merge to `main` → `release.yml` → **`devhub` GitHub Environment gate**
(required reviewer) → quick deploy of the PR-validated request
- No long-lived `develop` branch. Additional environments (integration/uat)
are added as extra gated jobs in `release.yml`, not extra branches.

---

Expand All @@ -36,104 +45,104 @@ Always reference the REQ number when one exists.

Every PR must:

- Pass CI (validate-pr.yml): scratch org creation + push + Apex tests + PMD/Code Analyzer
- Pass CI: `pr-validate.yml` → shared `sf-pr-validate.yml@v1` (`jest` +
`scratch-org` deploy/test), and `release.yml` → shared `sf-release.yml@v1`
(`validate`: delta check-only deploy with selected Apex tests against the
Dev Hub)
- Be **up to date with `main`** before merge (ruleset-enforced — this keeps the
validated deploy request identical to what merges, which is what makes quick
deploy safe)
- Have a completed PULL_REQUEST_TEMPLATE.md checklist
- Link to the relevant REQ-NNN.yaml or ticket
- Be reviewed by at least one other developer before merge
- Be reviewed (CODEOWNERS enforced)
- Not contain `System.debug` calls, hardcoded IDs, or `SeeAllData=true`
- Have test coverage ≥ 85% for changed classes

Squash merge into `develop`. No merge commits on `main`.
Squash or merge commits only — rebase-merges rewrite SHAs and break the
quick-deploy lookup (the fallback delta deploy covers it, but tests re-run).

---

## 4. Scratch Org Validation (CI)

The `validate-pr.yml` workflow:
## 4. PR Validation (CI)

1. Authenticates to Dev Hub via `DEVHUB_AUTH_URL` secret
2. Creates a scratch org using `config/scratch-orgs/ci.json`
3. Pushes all source (TestDataFactory is source-tracked in force-app — no package install needed)
4. Runs all Apex tests (`sf apex run test --test-level RunLocalTests --result-format json`)
5. Runs Salesforce Code Analyzer (PMD rules)
6. **Always** deletes the scratch org (even on failure) — prevents org leaks
Two workflows run on every PR:

Scratch org lifespan in CI: 1 day max. Always pass `--duration-days 1`.

---
`pr-validate.yml` calls the shared `sf-pr-validate.yml@v1`:

## 5. Staging Promotion
1. `jest` — runs `npm test` (skips with a notice if no test script exists)
2. `scratch-org` — creates a scratch org from `config/scratch-orgs/ci.json`
(`--duration-days 1`), pushes source, assigns permission sets, runs all
local tests with coverage, and **always** deletes the org

Push to `develop` triggers `deploy-staging.yml`:
`release.yml` calls the shared `sf-release.yml@v1` (`validate` job):

1. Authenticates via `STAGING_AUTH_URL` secret
2. Runs `sf project deploy start --target-org staging` (source format)
3. Runs smoke tests (manual or automated Apex test subset)

Staging = always green. If a deploy breaks staging, roll forward (fix) not roll back.
1. Generates a delta `package.xml` between the PR base and head
(sfdx-git-delta)
2. `sf-find-tests` selects the Apex tests covering the changed classes
(naming match + reference scan)
3. Runs a **check-only deploy of the delta against the Dev Hub**
(auth via `DEVHUB_AUTH_URL`) with `RunSpecifiedTests` — falls back to
`RunLocalTests` when Apex changed but no tests matched, and runs no tests
at all for metadata-only deltas. The resulting validated deploy request is
the quick-deploy handle, shipped in the `sf-release-<run_number>` artifact

---

## 6. Production Promotion
## 5. Deployment (promotion)

Push to `main` triggers `deploy-production.yml`:
Merge to `main` triggers `release.yml` → shared `sf-release.yml@v1`
(`quick-deploy` job):

1. Requires manual approval (GitHub environment protection: `production`)
2. Authenticates via `PRODUCTION_AUTH_URL` secret
3. Runs `sf project deploy start --target-org production`
4. Posts Slack notification on success/failure
1. Waits at the **`devhub` GitHub Environment** for required-reviewer
approval
2. Creates a GitHub Deployment record (audit trail, links the Salesforce
deploy-request page)
3. **Quick deploys** the PR-validated request (no tests re-run); falls back to
a delta deploy (same recorded test plan), then a full deploy of all
package directories
4. Uploads the audit artifact: delta manifest, deploy result JSON, JUnit test
results, quick-deploy decision

Production deployments: business hours only (08:00–17:00 CET). No Friday deployments.
Bootstrap/re-baseline: `gh workflow run release.yml -f full-deploy=true`.
Production deployments: business hours only (08:00–17:00 CET). No Friday
deployments.

---

## 7. Secret Management

| Secret | Used in | Value source |
| --------------------- | --------------------- | --------------------------------------------------------- |
| `DEVHUB_AUTH_URL` | validate-pr.yml | `sf org display --target-org devhub --verbose --json` |
| `STAGING_AUTH_URL` | deploy-staging.yml | `sf org display --target-org staging --verbose --json` |
| `PRODUCTION_AUTH_URL` | deploy-production.yml | `sf org display --target-org production --verbose --json` |

Store in GitHub repo secrets (Settings → Secrets and variables → Actions). Never commit auth URLs.
## 6. Secret & Variable Management

To generate an auth URL:
| Name | Kind | Used by | Value source |
| ----------------- | ---------------------------------------- | -------------- | ------------------------------------------------------------------------------------- |
| `DEVHUB_AUTH_URL` | Repo secret | both workflows | `sf org display --target-org <alias> --verbose --json \| jq -r '.result.sfdxAuthUrl'` |
| `SF_ORG_ALIAS` | `devhub` environment variable (optional) | quick-deploy | CLI alias, defaults to the environment name |

```bash
sf org display --target-org <alias> --verbose --json | jq -r '.result.sfdxAuthUrl'
```
Never commit auth URLs. For real client projects, move the auth URL to an
**environment secret** behind the gate, one per target org.

---

## 8. Package Installation in CI
## 7. Package Installation in CI

TestDataFactory is source-tracked in `force-app/main/default/classes/` and deploys
with the regular source push — no package install step is needed for it.

If the project adds unlocked packages, install them like this after the source push:

```yaml
- name: Install packages
run: sf package install --package <04t-package-id> --target-org $SCRATCH_ORG_ALIAS --no-prompt --wait 10
```

For more than one package, add them to a `scripts/install-packages.sh` and call it from all workflow jobs that need them.
If the project adds unlocked packages, install them after the source push via a
`scripts/install-packages.sh` invoked from the caller workflow (or raise a
change request to add package-install inputs to the shared workflows).

---

## 9. Code Analyzer (PMD)
## 8. Code Analyzer (PMD) — local use only

Run the Salesforce Code Analyzer on every PR, using the project ruleset
(`config/pmd-ruleset.xml` — all standard Apex categories, with naming
conventions adjusted for given_when_then test methods and fflib PascalCase
Application factories):
Code Analyzer is **not** wired into CI (dropped when the callers were rebuilt
on the shared `sf-pr-validate.yml` / `sf-release.yml` workflows — it is not
one of the PR gates). Run it locally before opening a PR — the project
ruleset lives at `config/pmd-ruleset.xml`:

```bash
sf scanner run --target force-app/ --pmdconfig config/pmd-ruleset.xml --format table --severity-threshold 2
```

Severity 1–2: block the PR. Severity 3: warn but allow merge with justification.

Key PMD rules enforced:

- `ApexSOQLInjection` — all SOQL through selectors with bind vars
Expand All @@ -143,12 +152,14 @@ Key PMD rules enforced:

---

## 10. Quality Gates Summary

| Gate | Trigger | Must Pass |
| ------------------------ | -------------------- | --------------------- |
| Lint + Prettier | Pre-commit (Husky) | Yes |
| Apex tests (scratch org) | PR to develop/main | Yes (≥85% coverage) |
| PMD Code Analyzer | PR to develop/main | Severity 1–2 = block |
| Manual PR review | PR to develop/main | ≥1 approval |
| Manual approval | Deploy to production | Required (GitHub env) |
## 9. Quality Gates Summary

| Gate | Trigger | Must Pass |
| ------------------------------------ | ------------------ | --------------------------- |
| Lint + Prettier | Pre-commit (Husky) | Yes |
| `jest` | PR to main | Yes |
| Delta check-only deploy (`validate`) | PR to main | Yes |
| Apex tests (`scratch-org`) | PR to main | Yes (≥85% coverage) |
| Branch up to date with main | PR merge (ruleset) | Yes |
| Manual PR review | PR to main | CODEOWNERS approval |
| Manual approval | Deploy | `devhub` GitHub Environment |
42 changes: 0 additions & 42 deletions .github/workflows/deploy-production.yml

This file was deleted.

41 changes: 0 additions & 41 deletions .github/workflows/deploy-staging.yml

This file was deleted.

22 changes: 22 additions & 0 deletions .github/workflows/pr-validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Thin caller — all validation logic lives in shared-github-actions.
# See docs/CICD.md for the pipeline overview and required setup.
name: PR Validate
on:
pull_request:
branches: [main]

concurrency:
group: pr-validate-${{ github.event.pull_request.number }}
cancel-in-progress: true

permissions:
contents: read

jobs:
validate:
uses: Gforce-Innovation-Kft/shared-github-actions/.github/workflows/sf-pr-validate.yml@v1
with:
# Temporary until gforceinnovation/sf-ci is published on Docker Hub
container-image: gforceinnovation/sf-ci:latest
secrets:
sfdx-auth-url: ${{ secrets.DEVHUB_AUTH_URL }}
34 changes: 34 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Thin caller — delta validation on PRs, gated quick deploy on merge.
# The `devhub` GitHub Environment holds the required-reviewer gate.
# Bootstrap a fresh org with: gh workflow run release.yml -f full-deploy=true
name: Release
on:
pull_request:
branches: [main]
push:
branches: [main]
workflow_dispatch:
inputs:
full-deploy:
description: "Deploy every package directory (bootstrap / re-baseline)"
type: boolean
default: false

concurrency:
group: release-${{ github.event_name == 'pull_request' && github.event.pull_request.number || 'main' }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

permissions:
contents: read
actions: read

jobs:
release:
uses: Gforce-Innovation-Kft/shared-github-actions/.github/workflows/sf-release.yml@v1
with:
environment: devhub
# Temporary until gforceinnovation/sf-ci is published on Docker Hub
container-image: gforceinnovation/sf-ci:latest
full-deploy: ${{ github.event_name == 'workflow_dispatch' && inputs.full-deploy }}
secrets:
sfdx-auth-url: ${{ secrets.DEVHUB_AUTH_URL }}
Loading
Loading