Checklist
This checklist follows the Setup Guide in chronological order, with the initialization merge request as the pivot: some items must be done before it, others only make sense after it.
Go through every item in order and tick the boxes. Any box you leave empty is either a deliberate choice for your project, or a gap to fix before you let the team work on the pipeline.
Sections that depend on a platform are grouped by platform: find your platform, then tick only its own items.
Before the first merge request
Everything in this phase must be in place before you open the initialization merge request, otherwise its control jobs cannot even run.
Git repository
- The repository exists and contains the sfdx-hardis project sources.
- One major branch exists for each major Salesforce org (for example
main,preprod,uat,integration). - The lowest major branch (usually
integration) is set as default branch. - All major branches are protected: they can only be updated through Merge Requests / Pull Requests.
- Allowed to merge is restricted to Release Managers / Maintainers on all major branches except the lowest one.
- Source branches are deleted after merge, and squash is enforced for User Story branches.
- The
cicdinitialization branch has been created, under the lowest major branch (usuallyintegration).
Salesforce orgs
See Configure Orgs
- Dev Hub is activated on the production org.
- Sandbox source tracking is activated on the Dev Hub, before creating or refreshing the sandboxes.
- Enable ExperienceBundle Metadata API is activated (Setup -> Digital Experiences).
- The sandbox of the lowest major branch (usually Integration) has been created or refreshed from production, so it matches production and has source tracking. It is the only major org you need at this stage.
- That sandbox is a developer sandbox, because you will clone it to create the developer sandboxes.
- Developer sandboxes are created from the Integration org, not from production.
- The Integration org has a dedicated CI user with the permissions needed to deploy (System Administrator profile or equivalent), pre-authorized on the External Client App.
The sandboxes of the other major branches (UAT, PreProd...) are not needed yet. Create or refresh them after the first merge request, once the pipeline works on the first level.
SFDX project
- The project was created with
sf hardis:project:create(not the default Salesforce command). -
manifest/package.xmlcontains the metadata to deploy, with a current Salesforce API version. -
manifest/destructiveChanges.xmlis empty, unless you deliberately want the initialization deployment to delete metadata from the orgs: if you removed useless metadata from the project during the setup and want it gone from the orgs too, list it here. -
config/.sfdx-hardis.ymlexists, withdevelopmentBranch,availableTargetBranchesand the branch/org mapping filled in. -
config/branches/.sfdx-hardis.<branch>.ymlexists for the lowest major branch, with the righttargetUsernameandinstanceUrl(the other major branches come later, with their org). - Installed packages are listed in
config/.sfdx-hardis.yml, withinstallDuringDeploymentsandinstallOnScratchOrgsset according to your needs. See Retrieve installed packages -
SFDX_DISABLE_FLOW_DIFFis set totruedirectly in the workflow file of your git provider (check-deploy.yml,.gitlab-ci.yml,azure-pipelines-checks.yml,bitbucket-pipelines.yml,Jenkinsfile), for the duration of the setup, to avoid flooding merge requests with comments. -
.gitignoreand.forceignoredo not exclude metadata that must be versioned, and do exclude local artifacts.
CI server authentication
-
sf hardis:project:configure:authhas been run for the lowest major branch (usuallyintegration): it is the only one required to run the initialization merge request. The other major branches are done later, when their sandbox exists. - An External Client App is present in the target org, with the certificate uploaded as Digital Signature and the CI user pre-authorized.
-
SFDX_CLIENT_ID_<ALIAS>is defined in the CI/CD variables,<ALIAS>being the uppercased branch name. -
SFDX_CLIENT_KEY_<ALIAS>(AES passphrase) and/orSFDX_CLIENT_CERT_<ALIAS>is defined, matching the certificate storage mode you selected. - All credential variables are masked / secret, and not restricted to protected branches only if your pipelines run on other branches.
- If you use scratch orgs, Dev Hub authentication is configured too (
sf hardis:project:configure:auth --devhubplus theSFDX_CLIENT_*_<DEVHUB_ALIAS>variables). - No
SFDX_AUTH_URL_*variable is used for a major org (it embeds a long-lived refresh token, reserve it for scratch orgs and Dev Hub). - Login works from the CI server: a pipeline job reaches the org without falling back to an interactive prompt.
Git provider and CI server
Find your platform below and tick only its items. Only what you have to do is listed: whatever the default sfdx-hardis workflow files already contain (job permissions, token passing, variable mapping for the default branch names, coding agent snippets, auto-fix/ branch skipping) is not repeated here.
- GitHub / GitHub Actions (see variables, integration)
- Folder
.github/workflowsis kept, the workflow files of the other providers are deleted. - Secrets are created in Settings -> Secrets and variables -> Actions.
- Any secret not already wired in the
envblocks of the templates is added there, since GitHub Actions does not expose secrets to jobs automatically. The templates wire theSFDX_CLIENT_*of the default branch names plusSLACK_*,NOTIF_EMAIL_ADDRESSandJIRA_*, so add for example your other branch aliases,MS_TEAMS_WEBHOOK_URL,GOOGLE_CHAT_WEBHOOK_URL,NOTIF_API_*, or the AI keys that are commented out. - Branch protection rules require the check deploy and MegaLinter status checks to pass before merge.
- Folder
- GitLab / GitLab CI (see variables, integration)
- Files
.gitlab-ci.ymlandgitlab-ci-config.ymlare kept, the workflow files of the other providers are deleted. - Variables are created in Settings -> CI/CD -> Variables, masked when secret and Protected variable unselected.
- In Settings -> General -> Merge requests, Pipelines must succeed is checked.
-
USE_SCRATCH_ORGSis set to"false"ingitlab-ci-config.ymlif you use sandboxes only. - A project access token with role Developer and scope api is stored in variable
CI_SFDX_HARDIS_GITLAB_TOKEN, so results are posted as Merge Request notes. - If you use a ticketing system: Merge Commit Message Template and Squash Commit Message Template include
%{issues}and%{all_commits}, so ticket references survive the merge.
- Files
- Azure DevOps / Azure Pipelines (see variables, integration)
- Files
azure-pipelines-checks.ymlandazure-pipelines-deployment.ymlare kept, the workflow files of the other providers are deleted. - Pipeline Check Pull Request is created from
azure-pipelines-checks.yml, with the continuous integration trigger disabled. - Pipeline Deploy to org is created from
azure-pipelines-deployment.yml, with continuous integration enabled and branch filters including all major branches. - Branch policies on the major branches include Build Validation with the Check Pull Request pipeline.
- Variables are defined on the pipelines (Edit -> Variables), and those not already wired in the templates are added to the YAML with
$(VARIABLE_NAME): the templates wireSFDX_CLIENT_*_INTEGRATIONonly, plusSLACK_*,NOTIF_EMAIL_ADDRESS,JIRA_*andOPENAI_API_KEY, so add for example your other branch aliases,MS_TEAMS_WEBHOOK_URL,GOOGLE_CHAT_WEBHOOK_URLorNOTIF_API_*. - Contribute and Contribute to Pull Requests are allowed on the Build Service, so the pipeline can post on Pull Requests.
- A Work Item named sfdx-hardis tech attachments exists (or
AZURE_ATTACHMENTS_WORK_ITEM_IDis defined), so Flow visual git diff images can be uploaded.
- Files
- Bitbucket / Bitbucket Pipelines (see variables, integration)
- File
bitbucket-pipelines.ymlis kept, the workflow files of the other providers are deleted. - Variables are created in Repository Settings -> Repository Variables, secured when secret.
- Merge checks require the pipeline to pass before merge.
- A repository access token with scopes
pullrequest,pullrequest:write,repository,repository:writeis stored in variableCI_SFDX_HARDIS_BITBUCKET_TOKEN, so results are posted as Pull Request comments.
- File
- Jenkins (see Jenkins setup)
- File
Jenkinsfileis kept, the workflow files of the other providers are deleted. - The job is a Multibranch Pipeline, so
CHANGE_IDis available and merge request comments can be posted. - Credentials are declared in Jenkins and exposed to the build as environment variables.
- The token of your git provider is set:
CI_SFDX_HARDIS_GITHUB_TOKEN,CI_SFDX_HARDIS_GITLAB_TOKEN,CI_SFDX_HARDIS_AZURE_TOKENorCI_SFDX_HARDIS_BITBUCKET_TOKEN. The other variables are derived from the Jenkins built-in ones.
- File
Pipelines
The default workflow files already declare the three jobs (check deploy, code quality, deployment), their triggers and their artifacts. What is left to you:
- The branch lists of the workflow files are updated with your major branch names, if they differ from the defaults (
integration,uat,preprod,main): each template has anAdd your major branches herecomment at the right place, and GitLab usesDEPLOY_BRANCHESingitlab-ci-config.yml. - The sfdx-hardis Docker image or plugin version used by the pipeline is the one you want (pin a version instead of
latestif you need reproducible runs). - The CI runner has enough minutes / capacity for the deployments.
-
SFDX_DEPLOY_WAIT_MINUTESis increased if your deployments need more than the default 120 minutes.
Project configuration
See Maintainer Guide and the full list of configuration properties
Overwrite management is the most important part of this section. Without manifest/package-no-overwrite.xml, every deployment overwrites the metadata that is maintained directly in the orgs: business users lose their Reports and Dashboards, and org-specific credentials and URLs are replaced by the ones of another environment. See Overwrite management
manifest/package-no-overwrite.xml- The file exists at the root of the
manifestfolder, and is committed. - Metadata holding org-specific values is protected with
*:ConnectedApp,ExtlClntAppGlobalOauthSettings,NamedCredential,ExternalCredential,RemoteSiteSetting,SamlSsoConfig. - Metadata managed by business users in production is protected with
*:Report,Dashboard, and theWave*types if you use CRM Analytics. -
ApprovalProcessis protected if your approval processes reference users of a specific org. -
FlexiPageandCustomApplicationitems that embed hardcoded dashboard or record IDs are listed by name. - Wildcards are used where they save maintenance, for example
*__dlmand*__dlm.*for Data Cloud objects and fields if they are maintained directly in production.
- The file exists at the root of the
Then the rest of the project configuration:
- Automated sources cleaning is configured (
autoCleanTypes), so User Story branches are cleaned before merge requests. - Apex test configuration matches your policy (test level, minimum coverage).
- New User Story options are set (
availableTargetBranches,availableTargetBranchesLabels,sharedDevSandboxes,allowedOrgTypes...) so contributors get the right prompts. - Delta deployments are NOT activated:
useDeltaDeploymentis absent fromconfig/.sfdx-hardis.yml, or set tofalse. The initialization merge request must deploy the full package. See Delta deployments
Notification channels
Overview: Configure integrations
At least one channel must be configured, otherwise nobody is told when a deployment to a major org fails. Configure the ones you use.
- Slack (see Slack integration)
- Slack app created, with scopes
chat-write,chat-write.customizeandchat-write.public. - Auth token stored in variable
SLACK_TOKEN. - Channel created, its ID stored in variable
SLACK_CHANNEL_ID. - The bot user is invited to the channel (
/invite @sfdx-hardis-bot).
- Slack app created, with scopes
- Microsoft Teams (see Teams integration)
- Workflow "Post to a channel when a webhook request is received" created on the channel.
- Webhook URL stored in variable
MS_TEAMS_WEBHOOK_URL.
- Google Chat (see Google Chat integration)
- Incoming webhook created on the space (needs a Google Workspace account).
- Webhook URL stored in variable
GOOGLE_CHAT_WEBHOOK_URL.
- Email (see Email integration)
- Recipients defined in variable
NOTIF_EMAIL_ADDRESS(comma separated). - Email deliverability of the CI user is set to Send through Salesforce.
- Recipients defined in variable
- API, for example Grafana (see API integration)
- Logs endpoint defined in
NOTIF_API_URL, with its auth variables (NOTIF_API_BASIC_AUTH_USERNAME/NOTIF_API_BASIC_AUTH_PASSWORDorNOTIF_API_BEARER_TOKEN). - Metrics endpoint defined in
NOTIF_API_METRICS_URL, with its own auth variables, if you want Prometheus metrics. - sfdx-hardis dashboards imported in Grafana.
- Logs endpoint defined in
Ticketing integration
- Jira (see Jira integration)
-
jiraHostis defined in.sfdx-hardis.yml, so the VS Code extension can use it too. - Authentication is configured as CI/CD secrets:
JIRA_EMAIL+JIRA_TOKEN(Basic Auth),JIRA_CLIENT_ID+JIRA_CLIENT_SECRET(OAuth2 service account withread:jira-workandwrite:jira-work), orJIRA_PAT(on-premise). -
jiraTicketRegexis tuned to your ticket format in.sfdx-hardis.yml, if the default expression catches too much or too little.
-
- Azure Boards (see Azure Boards integration)
-
SYSTEM_COLLECTIONURI,SYSTEM_ACCESSTOKEN,SYSTEM_TEAMPROJECTandBUILD_REPOSITORY_IDare available from the pipelines. - The team knows that Work Items must be linked to the Pull Requests to be detected.
-
- Any other ticketing tool (see Generic ticketing)
-
genericTicketingProviderRegexis defined in.sfdx-hardis.ymland tested against real ticket references. -
genericTicketingProviderUrlBuilderis defined in.sfdx-hardis.yml, with its{REF}segment.
-
AI integration
Optional, but it makes deployment errors much faster to solve. See Setup AI integration
- Agentforce (see With Agentforce)
- Agentforce is activated on the org used by the commands.
- Prompt template SfdxHardisGenericPrompt exists in the org (
sf hardis:org:configure:generic-promptdeploys it). - The CI user is assigned to permission set Prompt Template User.
-
useAgentforce: truein.sfdx-hardis.yml.
- LangChain (OpenAI, Anthropic, Gemini, Ollama) (see With LangChain)
-
useLangchainLlm: true,langchainLlmProviderandlangchainLlmModeldefined in.sfdx-hardis.yml, so all contributors share the same provider and model. -
LANGCHAIN_LLM_MODEL_API_KEYdefined as a masked secret (not needed for Ollama): API keys never go in.sfdx-hardis.yml.
-
- OpenAI directly
-
useOpenaiDirect: trueandopenaiModeldefined in.sfdx-hardis.yml. -
OPENAI_API_KEYdefined as a masked secret, or gateway authentication configured.
-
- Whatever the provider:
- API keys are stored as masked secrets, never committed in
.sfdx-hardis.yml. - The security implications have been reviewed with the client, and the cost settings are understood (
AI_MAXIMUM_CALL_NUMBER,MAX_DEPLOYMENT_TIPS_AI_CALLS).
- API keys are stored as masked secrets, never committed in
Coding agents auto-fix
Optional. Only if you want the pipeline to fix deployment errors and push fix branches. See Coding Agent Auto-Fix
The default workflow files already contain the coding agent install lines (commented out), the git remote set-url snippet and the auto-fix/ branch skipping, so only the following is up to you.
- If you do not use the sfdx-hardis Docker image: the install line of the coding agent CLI you want is uncommented in the workflow file.
- Auto-fix is enabled:
codingAgentAutoFix: truein.sfdx-hardis.yml. - The agent is chosen:
codingAgentin.sfdx-hardis.yml(claude,codex-cli,gemini-cli,copilot-cli). - The API key of the chosen agent is set as a masked secret (
ANTHROPIC_API_KEY,OPENAI_API_KEY/CODEX_API_KEY,GEMINI_API_KEY,COPILOT_GITHUB_TOKEN), unless it reusesLANGCHAIN_LLM_MODEL_API_KEY. - The token that lets the pipeline push branches and open merge requests is available:
- GitHub:
CI_SFDX_HARDIS_GITHUB_PUSH_TOKEN, eithersecrets.GITHUB_TOKENwithcontents: writeandpull-requests: write, or a fine-grained PAT withContentsandPull requestsread and write. - GitLab:
CI_SFDX_HARDIS_GITLAB_TOKENwith scopes api and write_repository. - Azure DevOps: Allow scripts to access OAuth token enabled in the pipeline settings, or a PAT mapped to
CI_SFDX_HARDIS_AZURE_TOKEN. - Bitbucket:
CI_SFDX_HARDIS_BITBUCKET_TOKEN.
- GitHub:
- Branch protection rules allow the bot to push
auto-fix/*branches and open merge requests.
The first merge request
This is the initialization merge request: it deploys the full package to your lowest major org. Expect several rounds of errors and configuration fixes before it goes green.
- The merge request has
cicdas source branch and the lowest major branch (usuallyintegration) as target. -
manifest/destructiveChanges.xmlis reviewed one last time before you merge: still empty, or containing exactly the metadata you decided to delete from the orgs, and nothing else. - The check deploy job is triggered by the merge request, and ends green. See Solve deployment errors
- The code quality (MegaLinter) job is green, or its remaining errors are known and accepted. See Handle MegaLinter errors
- The merge request has been merged, and the deployment job deployed the full package to the Integration org.
- The
cicdbranch is deleted.
After the first merge request
The pipeline works on the first level. Now verify what could not be verified before, then extend to the other major orgs.
Every remaining configuration change goes through a new branch and a new merge request. The
cicdbranch is gone and the major branches are protected, so from now on org authentication, delta deployments, cleaning options and any other.sfdx-hardis.ymlupdate follow the same contribution process as a User Story, with the control jobs running on them.
Integrations verification
Open a small test merge request with a real change and check what actually shows up.
- A deployment status comment is posted by the pipeline on the merge request, with the deployment errors and failing test classes when there are any.
- Quick Deploy is effective: after a successful check job, the deployment job reuses the validated deployment instead of running a full one (
SFDX_HARDIS_QUICK_DEPLOYis not set tofalse). See Smart Deployments - A real notification has been received on each configured channel, coming from an actual deployment job.
- Ticket references and links appear in merge request comments and in notifications.
- Tickets get a comment and a deployment tag once deployed in a major org (
DEPLOYED_TAG_TEMPLATEif you customized the tag). - A deployment error produces an AI assisted explanation in the comment, if you configured an LLM provider.
- Flow visual git diff works: once
SFDX_DISABLE_FLOW_DIFFis back tofalse, a Flow change in a merge request produces a diagram in the comment.
Delta deployments
- Delta deployments are activated only now that the initialization merge request is merged:
useDeltaDeployment: trueinconfig/.sfdx-hardis.yml. - The activation is committed in a new branch and merged with its own merge request.
- Delta deployments are enabled for the first level only (User Story branches to
integration). Between major orgs (integrationtouat,uattopreprod...), full deployments are used, as delta is not recommended there.
Other major orgs
Now that the first level works, set up the orgs of the upper major branches.
See Configure Orgs for the recommended sandbox types
- The sandbox of each remaining major branch (UAT, PreProd...) is created or refreshed from production, so it starts from a state close to production.
- Each of them has a dedicated CI user with the permissions needed to deploy, pre-authorized on the External Client App.
-
sf hardis:project:configure:authhas been run for each of these major branches, from a new branch, and its output (config/branches/.sfdx-hardis.<branch>.yml, encrypted key files) is merged with its own merge request. - Their
SFDX_CLIENT_ID_<ALIAS>andSFDX_CLIENT_KEY_<ALIAS>/SFDX_CLIENT_CERT_<ALIAS>variables are defined and masked. - The External Client App of the production org is in place, created manually if Apex test errors prevented the automated deployment. See CI Server Authentication
End to end validation
The setup is only complete when a change travels all the way to production.
- A User Story branch can be created with
sf hardis:work:new, and it creates or assigns the expected sandbox. -
sf hardis:work:saveruns successfully: it updatespackage.xml, applies the cleanings and prepares the merge request. - The check job on the merge request passes for a real change.
- After merge, the deployment job deploys to the matching org, and the change is visible in the Salesforce Setup.
- Overwrite management really protects the orgs: the items of
package-no-overwrite.xmlthat already exist in the target org are removed from the deployed package, and their version in the org is left untouched. Check it on a Report or a Named Credential of a major org. - The Apex tests actually pass on every major org.
- The same has been verified for every major branch, up to production. A pipeline that only works on
integrationis not a finished setup. - Deploying to production has been done at least once from the pipeline, not manually.
Setup clean up
Easy to forget, and it changes the daily experience of the team.
-
SFDX_DISABLE_FLOW_DIFFis set back tofalsein the workflow file where you set it totrue: it is only meant to betrueduring setup. - Temporary setup artifacts are removed from the repository (
packagexmlfull.xml, retrieve leftovers, unused workflow files of other git providers). -
server.key/server.crtare not committed in clear text, only the encrypted key file inconfig/branches/.jwt/if you chose that storage mode. - Leftover translations of deleted Dashboards and Reports are removed from
translations/*.xml. See Common issues - No credential, token or Salesforce URL with a session is present in the git history.
Team onboarding
- Every contributor installed the required tooling and the VS Code sfdx-hardis extension. See Installation guide
- Every contributor cloned the repository and can create a User Story branch. See Clone repository
- The team knows the contribution process: create a User Story, work on it, publish it, handle merge request results.
- Release Managers know how to validate a merge request and how to handle hotfixes.
- The team knows that custom Profiles deployed for the first time must be created manually in the target org, cloned from "Minimal Access".
- Someone owns the pipeline: they get the notifications and they know where the job logs and artifacts are.
Going further
Not part of the CI/CD pipeline itself, but usually set up right after.
- Org Monitoring is set up on a separate repository, with its own External Client App and its own notification variables.
- Project documentation is generated and hosted, so the team has an up to date functional documentation of the org.
- Deployment Agent is set up if you want assisted resolution of deployment errors.
- Sandbox refresh procedure is documented for the day a major sandbox is refreshed.