Skip to content
Open
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
4 changes: 2 additions & 2 deletions .github/workflows/issue-branch-creation-handler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ jobs:
run: |
echo "${{ secrets.SALESFORCE_JWT_KEY }}" > server.key
npx sf auth jwt grant \
--client-id ${{ secrets.SALESFORCE_CLIENT_ID }} \
--client-id ${{ secrets.SALESFORCE_PROD_CLIENT_ID }} \
--jwt-key-file server.key \
--username ${{ secrets.SALESFORCE_DEVHUB_USERNAME }} \
--username ${{ secrets.SALESFORCE_PROD_USERNAME }} \
--set-default-dev-hub -a DevHub
rm server.key
- name: Extract Branch Name
Expand Down
73 changes: 47 additions & 26 deletions .github/workflows/push-to-main-handler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ jobs:
with:
node-version-file: ".nvmrc"

- name: Cache Node Modules
uses: actions/cache@v3.0.11
id: npm_cache_id
with:
path: node_modules
key: ${{ runner.os }}-npm-cache-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-cache-
${{ runner.os }}-

- name: Install Dependencies
if: steps.npm_cache_id.outputs.cache-hit != 'true'
run: npm ci

- name: Authorize external workspace
run: |
git config --global --add safe.directory /github/workspace
Expand All @@ -40,8 +54,7 @@ jobs:
echo "IS_RELEASE=true" >> "$GITHUB_OUTPUT"
fi

- name: Install profile decompose sf plugin # ERROR: Unable to convert this command; you must convert it manually.

- name: Install profile decompose sf plugin
if: steps.requires-deploy.outputs.changed == '1'
run: echo y | npx sf plugins install @rdietrick/sfdx-profile-decompose

Expand All @@ -51,7 +64,7 @@ jobs:
SALESFORCE_JWT_KEY: ${{ secrets.SALESFORCE_JWT_KEY }}
run: |
echo "${SALESFORCE_JWT_KEY}" > server.key
npx sf auth jwt grant --client-id ${{ secrets.SALESFORCE_CLIENT_ID }} --jwt-key-file server.key --username ${{ secrets.SALESFORCE_DEVHUB_USERNAME}} --set-default-dev-hub -a DevHub
npx sf auth jwt grant --client-id ${{ secrets.SALESFORCE_PROD_CLIENT_ID }} --jwt-key-file server.key --username ${{ secrets.SALESFORCE_PROD_USERNAME}} --set-default-dev-hub -a DevHub
npx sf org display --json -o DevHub > sfdx-auth.json

- name: Recompose profiles
Expand All @@ -64,6 +77,7 @@ jobs:
if: steps.branchFilter.outputs.matches == 'true' && steps.requires-deploy.outputs.changed == '1' && steps.check_first_deploy.outputs.FIRST_DEPLOY == 'true'
id: check_for_destructive_changes
run: |
DESTRUCTIVE_FILES_EXIST=true
if [ -f destructive-changes/destructiveChangesPre.xml ] && [ -f destructive-changes/destructiveChangesPost.xml ]
then
echo "DESTRUCTIVE_FILES=--predestructivechanges destructive-changes/destructiveChangesPre.xml --postdestructivechanges destructive-changes/destructiveChangesPost.xml" >> "$GITHUB_OUTPUT";
Expand All @@ -73,13 +87,20 @@ jobs:
elif [ -f destructive-changes/destructiveChangesPost.xml ]
then
echo "DESTRUCTIVE_FILES=--postdestructivechanges destructive-changes/destructiveChangesPost.xml" >> "$GITHUB_OUTPUT";
else
DESTRUCTIVE_FILES_EXIST=false
fi
if [ "$DESTRUCTIVE_FILES_EXIST" == "true" ]
then
RELEASE_DATE=$(TZ=America/Los_Angeles date +'%Y-%m-%d')
echo "DESTRUCTIVE_CHANGES_CLEANUP_BRANCH=clean-up-destructive-changes-$RELEASE_DATE" >> $GITHUB_OUTPUT;
fi

- name: Deploy to Production
if: steps.requires-deploy.outputs.changed == '1'
run: |
sudo npx sf project generate manifest -p force-app -n temp-deploy-manifest
sudo npx sf project deploy start -o DevHub -x temp-deploy-manifest.xml ${{ steps.check_for_destructive_changes.outputs.DESTRUCTIVE_FILES }} -w 200 -l RunLocalTests --ignore-conflicts
sudo npx sf project deploy start -o DevHub -x temp-deploy-manifest.xml ${{ steps.check_for_destructive_changes.outputs.DESTRUCTIVE_FILES }} -w ${{ vars.DEPLOYMENT_TIMEOUT }} -l RunLocalTests --ignore-conflicts

- name: Get Previous Tag
if: steps.check_release.outputs.IS_RELEASE == 'true' && steps.requires-deploy.outputs.changed == '1'
Expand Down Expand Up @@ -122,7 +143,7 @@ jobs:
if: steps.requires-deploy.outputs.changed == '1'
run: |
echo "${SALESFORCE_TEMPLATE_JWT_SECRET_KEY}" > template-server.key
sudo npx sf org login jwt --client-id ${{ secrets.SALESFORCE_TEMPLATE_CONSUMER_KEY }} --jwt-key-file template-server.key --username ${{ secrets.SALESFORCE_TEMPLATE_USERNAME}} --alias Template --instance-url https://test.salesforce.com
sudo npx sf org login jwt --client-id ${{ secrets.SALESFORCE_TEMPLATE_CLIENT_ID }} --jwt-key-file server.key --username ${{ secrets.SALESFORCE_TEMPLATE_USERNAME}} --alias Template --instance-url https://test.salesforce.com
Copy link

Copilot AI Mar 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The key file referenced for JWT authentication does not match the file created in a previous step ('template-server.key'); update the command to use 'template-server.key'.

Suggested change
sudo npx sf org login jwt --client-id ${{ secrets.SALESFORCE_TEMPLATE_CLIENT_ID }} --jwt-key-file server.key --username ${{ secrets.SALESFORCE_TEMPLATE_USERNAME}} --alias Template --instance-url https://test.salesforce.com
sudo npx sf org login jwt --client-id ${{ secrets.SALESFORCE_TEMPLATE_CLIENT_ID }} --jwt-key-file template-server.key --username ${{ secrets.SALESFORCE_TEMPLATE_USERNAME}} --alias Template --instance-url https://test.salesforce.com

Copilot uses AI. Check for mistakes.
sudo npx sf org display --json -o Template > sfdx-auth.json
env:
SALESFORCE_TEMPLATE_JWT_SECRET_KEY: ${{ secrets.SALESFORCE_TEMPLATE_JWT_SECRET_KEY }}
Expand All @@ -131,27 +152,27 @@ jobs:
if: steps.requires-deploy.outputs.changed == '1'
run: |
sudo npx sf project generate manifest -p force-app -n temp-deploy-manifest
sudo npx sf org display --json -o Template > sfdx-auth.json
npx sf project deploy start -o Template -x temp-deploy-manifest.xml ${{ steps.check_for_destructive_changes.outputs.DESTRUCTIVE_FILES }} -w ${{ vars.DEPLOYMENT_TIMEOUT }} -l RunLocalTests --ignore-conflicts

- name: Create PR with cleaned up destructive changes
if: steps.check_release.outputs.IS_RELEASE == 'true' && steps.requires-deploy.outputs.changed == '1'
- name: Remove Destructive Change Files
if: steps.check_for_destructive_changes.outputs.DESTRUCTIVE_CHANGES_CLEANUP_BRANCH != ''
run: |
git checkout -b ${{ steps.check_for_destructive_changes.outputs.DESTRUCTIVE_CHANGES_CLEANUP_BRANCH }}
git clean -fd
rm -rf destructive-changes
- name: Commit Destructive Change Cleanup
if: steps.check_for_destructive_changes.outputs.DESTRUCTIVE_CHANGES_CLEANUP_BRANCH != ''
uses: stefanzweifel/git-auto-commit-action@75802d269e7721b5146d08f6063ba3097c55ad2b
with:
branch: ${{ steps.check_for_destructive_changes.outputs.DESTRUCTIVE_CHANGES_CLEANUP_BRANCH }}
commit_message: Clean up destructive changes from release

- name: Create Destructive Change Cleanup Pull Request
if: steps.check_for_destructive_changes.outputs.DESTRUCTIVE_CHANGES_CLEANUP_BRANCH != ''
env:
PAT: ${{ secrets.SCOPED_PAT }}
USER_EMAIL: ${{ secrets.BOT_USER_EMAIL }}
USER_NAME: ${{ secrets.BOT_USER_NAME }}
DESTRUCTIVE_CHANGE_CLEANUP_BRANCH: ${{ steps.check_for_destructive_changes.outputs.DESTRUCTIVE_CHANGES_CLEANUP_BRANCH }}
GH_TOKEN: ${{ secrets.SCOPED_PAT }}
run: |
if [ -f destructive-changes/destructiveChanges.xml ] || [ -f destructive-changes/destructiveChangesPre.xml ] || [ -f destructive-changes/destructiveChangesPost.xml ]
then
RELEASE_DATE=$(date +'%Y-%m-%d')
echo $PAT | gh auth login --with-token
git config --global user.email "$USER_EMAIL"
git config --global user.name "$USER_NAME"

git checkout -b clean-up-destructive-changes-"$RELEASE_DATE" && git push --set-upstream origin clean-up-destructive-changes-"$RELEASE_DATE"
git rm -r destructive-changes
git commit -m "Clean up destructive changes from release" && git push

gh pr create --title "Clean up destructive changes" \
--body "Please merge \`clean-up-destructive-changes-$RELEASE_DATE\` into \`main\` and delete \`clean-up-destructive-changes-$RELEASE_DATE\`." \
-H clean-up-destructive-changes-"$RELEASE_DATE" -B main
fi
gh pr create --title "Clean up destructive changes" \
--body "Please merge \`$DESTRUCTIVE_CHANGE_CLEANUP_BRANCH\` into \`main\` and delete \`$DESTRUCTIVE_CHANGE_CLEANUP_BRANCH\`." \
-H "$DESTRUCTIVE_CHANGE_CLEANUP_BRANCH" -B main
16 changes: 6 additions & 10 deletions docs/Getting Started.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ These step-by-step instructions will walk you through your initial project setup

1. [Create a new repository](https://github.com/new?owner=&template_name=octoforce-actions&template_owner=github) from this repo. Check out your new repo locally.
2. If you haven't already, [enable DevHub](https://help.salesforce.com/s/articleView?id=sf.sfdx_setup_enable_devhub.htm&type=5) in your production Salesforce org. Workflows in this repo will use your org's DevHub to provision development and test sandboxes for your project.
3. Create (or repurpose an existing) an admin user in your production org that will be used for deployments and sandbox provisioning. Store the username of this user in a repo secret named `SALESFORCE_DEVHUB_USERNAME`.
3. Create (or repurpose an existing) an admin user in your production org that will be used for deployments and sandbox provisioning. Store the username of this user in a repo secret named `SALESFORCE_PROD_USERNAME`.
4. Create a [private key and certificate for use in the app you'll create in the next step](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_key_and_cert.htm)
5. Create a connected app in your production Salesforce org for the octoforce CI/CD application that will be provisioning sandboxes and deploying to production.
- Name your app 'octoforce' or something similar.
Expand All @@ -39,10 +39,10 @@ These step-by-step instructions will walk you through your initial project setup
- Grant the "system administrator" profile (or whichever profile the user designated in step 3 is assigned) access to the new connected app
- Store the following secrets in your repo:
- `SALESFORCE_JWT_KEY` - use the contents of the server.key file generated in step 4 above
- `SALESFORCE_CLIENT_ID` - use the Consumer Key of the connected app you just created
- `SALESFORCE_PROD_CLIENT_ID` - use the Consumer Key of the connected app you just created
6. Clone your production org to create a sandbox named "template". This is the org that will be cloned to create your dev and uat sandboxes.
7. When your newly created template sandbox is provisioned, configure it identically to how you did in step 5 above. You can use the same certificate and key pair or generate new ones. Create two repo secrets with the following values from your template sandbox:
- `SALESFORCE_TEMPLATE_CONSUMER_KEY` - use the consumer key of the octoforce connected app replicated from production to your template sandbox
- `SALESFORCE_TEMPLATE_CLIENT_ID` - use the consumer key of the octoforce connected app replicated from production to your template sandbox
- `SALESFORCE_TEMPLATE_JWT_SECRET_KEY` - use the value of the server.key file related to your template certificate
- `SALESFORCE_TEMPLATE_USERNAME` - use the username of the salesforce admin user replicated in your template sandbox
8. [Follow these directions](https://github.com/github/octoforce-actions/blob/add-setup-docs/docs/SFDX%20Auth%20URLs%20%26%20Encryption.md) to generate and store your age encryption keys. Be sure to store the generated private key in the `SFDX_AUTH_SECRET_KEY` repo secret and to replace the contents of `auth/public-key.txt`.
Expand Down Expand Up @@ -81,20 +81,16 @@ The following secrets are required to be set in the repository settings:
- This is a personal access token with the `repo` scope. This is used to checkout the repository and push any changes.
- `SALESFORCE_JWT_KEY`
- This is the private key used to generate the JWT token. This is used to authenticate with Salesforce production.
- `SALESFORCE_CLIENT_ID`
- `SALESFORCE_PROD_CLIENT_ID`
- This is the client ID used to generate the JWT token. This is used to authenticate with Salesforce production.
- `SALESFORCE_DEVHUB_USERNAME`
- `SALESFORCE_PROD_USERNAME`
- This is the username of the admin user of your DevHub/production org. This is used to authenticate with Salesforce production.
- `SALESFORCE_TEMPLATE_CONSUMER_KEY`
- `SALESFORCE_TEMPLATE_CLIENT_ID`
- This is the consumer key of the template org. This is used to authenticate with the template sandbox.
- `SALESFORCE_TEMPLATE_USERNAME`
- This is the username of the template org. This is used to authenticate with the template sandbox.
- `SALESFORCE_TEMPLATE_JWT_SECRET_KEY`
- This is the private key used to generate the JWT token. This is used to authenticate with the template sandbox.
- `BOT_USER_EMAIL`
- This is the email of the GitHub bot user. This is used to create pull requests against the main branch.
- `BOT_USER_NAME`
- This is the GitHub username of the bot user. This is used to create pull requests against the main branch.
- `SFDX_AUTH_SECRET_KEY`
- This is the age private key used to encrypt sfdx auth URLs. The encrypted sfdx auth URLs are required to deploy pull requests to UAT sandboxes and are generated by running `scripts/sandbox_auth`. Please see [SFDX Auth URLs & Encryption.md](SFDX%20Auth%20URLs%20%26%20Encryption.md) for more information.

Expand Down
2 changes: 1 addition & 1 deletion scripts/sandbox_auth
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ else
wait $PID
fi

echo "===> Generating encrypted sf auth URL file... "
echo "===> Generating encrypted login URL ... "
ORG_INFO="$(sf org display --target-org $UAT_ALIAS --verbose 2>/dev/null)"
LOGIN_URL=$(echo "$ORG_INFO" | awk -F "Sfdx Auth Url" '{print $2}' | xargs)
LOGIN_URL_FILE=auth/sandbox-login-url-${BRANCH_NAME}-uat.txt
Expand Down