Skip to content

Appendix D: Centralized Configuration Repository Structure Reference

This appendix defines the recommended structure for the organization-level .github configuration repository described in Section 8. The repository serves as the authoritative source for workflow templates, shared configuration standards, and governance artifacts consumed by all Salesforce project repositories in the organization.

Repository Overview

The configuration repository is created as a repository named .github within the GitHub organization. Starter workflows placed in workflow-templates/ are surfaced to all repositories in the organization via the Actions UI, and reusable workflows stored in .github/workflows/ are callable via workflow_call from any repository in the organization.

The repository should be treated as a product: changes follow the same PR-based process as application code, the CODEOWNERS file restricts modifications to the governance team (@<my-org>/admin-team), and releases are tagged following Conventional Commits and SemVer conventions.

The structure below mirrors the reference implementation in Appendix A. In a consuming project repository, .github/actions/ and .github/workflows/ contain the composite actions and reusable workflows described there. At the organization-level .github repository, those same files appear under the inner .github/ directory and become callable from every repository in the organization.

.github/                                               # Organization-level configuration repository
├── .github/                                           # GitHub configuration for the template repo itself
│   ├── ISSUE_TEMPLATE/                                # Issue templates surfaced when opening an issue
│   │   ├── bug_report.md                              # Bug report template with steps to reproduce, environment, logs
│   │   └── feature_request.md                         # Feature request template for new workflows, actions, gates
│   │
│   ├── PULL_REQUEST_TEMPLATE.md                       # Default PR description template (summary, test plan, checklist)
│   │
│   ├── actions/                                       # Composite actions (reusable steps within a job)
│   │   ├── apex-list/
│   │   │   └── action.yml                             # Retrieves all ApexClass and ApexTrigger names from an org
│   │   ├── apex-test-report/
│   │   │   └── action.yml                             # Parses sf apex run test JSON into job summary + PR markdown
│   │   ├── post-pr-comment/
│   │   │   └── action.yml                             # Posts or updates a structured PR comment (finds by marker)
│   │   ├── setup-sf/
│   │   │   └── action.yml                             # Installs Node, npm ci, SF CLI, Code Analyzer, sfdx-git-delta
│   │   ├── sf-auth-jwt/
│   │   │   └── action.yml                             # Authenticates to an org via JWT bearer flow
│   │   ├── sf-auth-sfdx-url/
│   │   │   └── action.yml                             # Authenticates to an org via SFDX auth URL
│   │   └── sf-code-analyzer-report/
│   │       └── action.yml                             # Parses Code Analyzer JSON into job summary + PR markdown
│   │
│   ├── workflows/                                     # Caller workflows (triggered) and reusable workflows (workflow_call)
│   │   │
│   │   │ ── Caller workflows (one per trigger event) ──
│   │   ├── ci.yml                                     # Push baseline: runs on every push; triggers release-please on main
│   │   ├── pr-prod.yml                                # PR gates for pull requests targeting main (production auth)
│   │   ├── pr-staging.yml                             # PR gates for PRs targeting staging/uat/integration/full
│   │   ├── pr-dependabot.yml                          # Validation workflow for Dependabot-created PRs
│   │   ├── pr-mend-renovate.yml                       # Validation workflow for Renovate-created PRs
│   │   ├── scheduled-drift-detection.yml              # Weekly org surveillance (drift, compilation, full-org tests)
│   │   ├── scheduled-test-runner.yml                  # Nightly Apex test runner (compilation + full-org tests)
│   │   │
│   │   │ ── Reusable workflows (workflow_call targets) ──
│   │   ├── release-please.yml                         # Release Please changelog + tagged release management
│   │   ├── sf-apex-compilation.yml                    # Dry-run deploys all Apex with NoTestRun
│   │   ├── sf-apex-tests.yml                          # Full-org RunLocalTests, creates issue on failure
│   │   ├── sf-apex-tests-pr.yml                       # Delta-scoped Apex tests; RunSpecifiedTests or RunLocalTests
│   │   ├── sf-code-analyzer.yml                       # sf code-analyzer run; pr mode or ci mode
│   │   ├── sf-destructive-check.yml                   # Detects destructiveChanges.xml and labels the PR
│   │   ├── sf-drift-detection.yml                     # Compares org retrieve to repo state via git diff
│   │   ├── sf-generate-delta.yml                      # sfdx-git-delta delta package, uploaded as artifact
│   │   ├── sf-jest-tests.yml                          # LWC Jest with coverage, uploads to Codecov
│   │   ├── sf-notify-slack.yml                        # Slack incoming webhook notification
│   │   ├── sf-prettier-verify.yml                     # prettier --check on source files
│   │   ├── sf-secret-scan.yml                         # Placeholder for Gitleaks/TruffleHog/GHAS
│   │   ├── sf-semver-check.yml                        # Enforces Conventional Commits on PR title
│   │   └── sf-validate-deployment.yml                 # sf project deploy --dry-run against delta manifest
│   │
│   ├── CODEOWNERS                                     # Restricts governance files to admin-team
│   ├── codecov.yml                                    # Codecov configuration (coverage flags, thresholds)
│   ├── dependabot.yml                                 # Tracks github-actions and npm dependencies
│   └── renovate.json                                  # Extends org-wide Renovate preset, overrides where needed
├── workflow-templates/                                # Starter workflows surfaced in GitHub Actions UI
│   ├── sf-pr-checks.yml                               # Starter: full PR quality gate chain
│   ├── sf-pr-checks.properties.json
│   ├── sf-scheduled-checks.yml                        # Starter: nightly drift detection and full-org tests
│   ├── sf-scheduled-checks.properties.json
│   ├── sf-deploy-sandbox.yml                          # Starter: sandbox deployment workflow
│   ├── sf-deploy-sandbox.properties.json
│   ├── sf-deploy-production.yml                       # Starter: production deployment workflow
│   ├── sf-deploy-production.properties.json
│   ├── sf-notify-slack.yml                            # Starter: Slack notification workflow
│   └── sf-notify-slack.properties.json
├── config/                                            # Shared configuration standards
│   │                                                  # Ideally, these files reside in the root directory of
│   │                                                  # consuming repositories, as most tooling expects them
│   │                                                  # there by default. This directory is the canonical
│   │                                                  # source. When a consuming repository places files in
│   │                                                  # a non-root location, tools must be explicitly
│   │                                                  # pointed to that path.
│   ├── code-analyzer.yml                              # Canonical Code Analyzer configuration
│   ├── codecov.yml                                    # Canonical Codecov configuration
│   ├── pmd-ruleset.xml                                # Canonical PMD custom ruleset (see Appendix F)
│   ├── eslint.config.js                               # Canonical ESLint flat config for LWC
│   ├── jest.config.js                                 # Canonical Jest configuration with coverage thresholds
│   ├── .prettierrc                                    # Canonical Prettier configuration
│   │                                                  # (preferably published via npm and overridden locally)
│   ├── secret-scan-config                             # Canonical secret scanning configuration
│   │                                                  # (e.g. .gitleaks.toml for Gitleaks, or equivalent)
│   └── renovate.json                                  # Org-wide Renovate preset for consuming repositories
│                                                      # (extended by a local renovate.json file)
├── templates/                                         # Project scaffolding templates
│   ├── CODEOWNERS.template
│   ├── dependabot.yml.template
│   ├── .gitignore.template
│   ├── .forceignore.template
│   ├── sfdx-project.json.template
│   ├── release-please-config.json.template            # Release Please config: release type and tracked paths
│   └── .release-please-manifest.json.template         # Release Please manifest: current version per package.
│                                                      # Must be correctly initialised at project kickstart:
│                                                      # an incorrect initial version entry is the most common
│                                                      # cause of first-release failures.
├── rulesets/                                          # Exported GitHub Ruleset configurations
│   ├── integration-branch-protection.json
│   ├── staging-branch-protection.json
│   ├── production-branch-protection.json
│   └── required-workflows.json
├── docs/
│   ├── onboarding.md                                  # Guide for new projects adopting the configuration repository
│   ├── contributing.md                                # Contribution process for delivery teams
│   ├── versioning.md                                  # Versioning and upgrade policy
│   └── changelog.md                                   # Record of changes to standards and workflows
├── CODE_OF_CONDUCT.md                                 # Community standards and enforcement policy
├── CONTRIBUTING.md                                    # Guidelines for contributing to this repository
├── SECURITY.md                                        # Vulnerability disclosure and security policy
├── SUPPORT.md                                         # How to get help and report issues
└── README.md

The .github/workflows/ directory contains two categories of files. Caller workflows (ci.yml, pr-prod.yml, pr-staging.yml, scheduled-*.yml) are the trigger-bound entry points; they respond to GitHub events (push, pull_request, schedule) and compose reusable workflows into a pipeline. Reusable workflows (sf-*.yml, release-please.yml) declare on: workflow_call and implement exactly one quality gate or concern. Each reusable workflow lives in its own file, with the naming convention sf-<concern>.yml. This split keeps caller workflows thin and event-focused, while the gate logic remains testable and versionable in isolation.

Key Design Decisions

Starter workflows vs. reusable workflows serve distinct purposes. Starter workflows in workflow-templates/ are discovery and onboarding mechanisms - they appear in the GitHub Actions UI when creating a new workflow. Reusable workflows in .github/workflows/ are the actual callable implementations. A starter workflow is typically a thin wrapper that calls one or more reusable workflows, giving new projects a complete working pipeline immediately while keeping the implementation centralized.

Composite actions in .github/actions/ handle cross-cutting setup and utility concerns shared across multiple reusable workflows. The setup-sf action consolidates Node, SF CLI, Code Analyzer plugin, and sfdx-git-delta installation into a single step; the sf-auth-jwt and sf-auth-sfdx-url actions are called by every workflow requiring org connectivity; the post-pr-comment action standardizes PR comment posting and in-place update logic. Rather than duplicating these steps across every reusable workflow, the logic lives in one place and is referenced by all.

Configuration files in config/ are the canonical versions of all quality gate configuration. Consuming repositories reference these files directly where tooling supports remote configuration, or copy them into the project repository at onboarding and track updates via Renovate.

GitHub configuration repository as a complement: Maintaining a dedicated GitHub configuration repository - a separate repository marked as a template in GitHub's settings - allows new projects to be created from it with a single click, copying all scaffolding files automatically. The templates/ directory in the .github repository is the authoritative reviewed source; the GitHub configuration repository reflects its current state for consumption. As changes are made in templates/, those changes could be mirrored to the configuration repository - but this synchronization automation is advanced-level and should be addressed only once the manual pattern is stable.

CODEOWNERS configuration: The configuration repository's CODEOWNERS file protects all governance-critical paths:

/.github/workflows/         @<my-org>/admin-team
/.github/actions/           @<my-org>/admin-team
/workflow-templates/        @<my-org>/admin-team
/config/                    @<my-org>/admin-team
/rulesets/                  @<my-org>/admin-team
/docs/                      @<my-org>/admin-team
/README.md                  @<my-org>/admin-team

Versioning and Upgrade Policy

The configuration repository follows Semantic Versioning driven by Conventional Commits and automated via release-please (GitHub). Patch releases address bug fixes that do not change gate behavior; minor releases introduce new optional gates or backward-compatible enhancements; major releases introduce breaking changes requiring coordinated updates in consuming repositories.

Consuming repositories pin to a specific version tag for all reusable workflow references and update via Renovate PRs when new versions are published. For Renovate to automatically open upgrade PRs when new template versions are published, the organization must use Vendir GitHub and additional configuration, as explained in a blog post.

If template configuration files are bundled into a npm package, then Renovate can check for the package in an npm (public or private) registry and open updating pull requests as needed.

{
    "extends": ["config:recommended"],
    "packageRules": [
        {
            "matchManagers": ["npm"],
            "matchPackagePatterns": ["^<my-org>/"],
            "groupName": "Internal packages",
            "automerge": false
        }
    ]
}

Setting automerge: false for internal template updates is intentional - template version upgrades may introduce new required inputs or changed gate behavior and should be reviewed by the project technical lead before adoption.

The release-please-config.json template defines the release type and the paths Release Please should track. The .release-please-manifest.json maintains current version state per package and must be correctly initialized at project kickstart - an incorrect initial version entry is the most common cause of first-release failures and can produce unexpected version bumps that are difficult to reverse once tags have been pushed.

Repository Bootstrap Script

Rather than relying on manual steps to set up a new project repository, these operations can be codified into a bootstrap script that a delivery team runs once at project kickstart:

#!/bin/bash
# Bootstrap a new Salesforce project repository
# Usage: ./bootstrap.sh <new-repo-name> <github-org>

REPO_NAME=$1
ORG=$2
TEMPLATE_REPO=".github"

# Create repository from template
gh repo create "$ORG/$REPO_NAME" \
  --template "$ORG/$TEMPLATE_REPO" \
  --private

# Apply organization Rulesets
for ruleset in rulesets/*.json; do
  gh api \
    --method POST \
    -H "Accept: application/vnd.github+json" \
    "/orgs/$ORG/rulesets" \
    --input "$ruleset"
done

# Create branch topology
for branch in integration staging uat; do
  gh api \
    --method POST \
    -H "Accept: application/vnd.github+json" \
    "/repos/$ORG/$REPO_NAME/git/refs" \
    -f ref="refs/heads/$branch" \
    -f sha="$(gh api /repos/$ORG/$REPO_NAME/git/ref/heads/main --jq '.object.sha')"
done

# Create GitHub Environments
for env in staging uat production; do
  gh api \
    --method PUT \
    -H "Accept: application/vnd.github+json" \
    "/repos/$ORG/$REPO_NAME/environments/$env"
done

echo "Repository $REPO_NAME bootstrapped successfully."
echo "Next steps:"
echo "  1. Add Salesforce org credentials to each GitHub Environment"
echo "  2. Update sfdx-project.json with package structure"
echo "  3. Configure Renovate on the new repository"
echo "  4. Run initial scratch org creation to validate pipeline"

This script is a starting point. Production-grade bootstrap scripts for consulting organizations typically extend this pattern to include org authentication validation, Slack notifications, Jira project linking, and population of environment secrets from a vault. The bootstrap script itself should be stored in the configuration repository - versioned and maintained with the same discipline as the workflows and configuration files it deploys. The target structure for the reusable workflow refactoring described in Appendix A is the .github/workflows/ directory defined above - each job in the monolithic PR workflow maps directly to a named reusable workflow file callable via workflow_call.