Skip to content

Commit

Permalink
Merge pull request #8063 from Expensify/Rory-LoudFailAutomergeAction
Browse files Browse the repository at this point in the history
Use GH CLI instead of community actions

(cherry picked from commit 3af29ff)
  • Loading branch information
AndrewGable authored and OSBotify committed Mar 17, 2022
1 parent beb9906 commit da973c7
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 95 deletions.
41 changes: 21 additions & 20 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ We've found that the best way to avoid this pitfall is to always wrap any refere
1. Review all modifications to our workflows with extra scrutiny, it is important to get it correct the first time.
1. Test workflow changes in your own public fork, for example: https://github.com/Andrew-Test-Org/Public-Test-Repo
1. Only trusted users will be allowed write access to the repository, however, it's good to add logic checks in actions to prevent human error.
1. Do not add repo secrets to the environment at the workflow or job level. Only add them to the environment at the step level.

## Further Reading 📖
1. https://securitylab.github.com/research/github-actions-preventing-pwn-requests
Expand All @@ -57,33 +58,33 @@ We've found that the best way to avoid this pitfall is to always wrap any refere
The GitHub workflows require a large list of secrets to deploy, notify and test the code:
1. `LARGE_SECRET_PASSPHRASE` - decrypts secrets stored in various encrypted files stored in GitHub repository. To create updated versions of these encrypted files, refer to steps 1-4 of [this encrypted secrets help page](https://docs.github.com/en/actions/reference/encrypted-secrets#limits-for-secrets) using the `LARGE_SECRET_PASSPHRASE`.
1. `android/app/my-upload-key.keystore.gpg`
2. `android/app/android-fastlane-json-key.json.gpg`
3. `ios/chat_expensify_appstore.mobileprovision`
4. `ios/Certificates.p12.gpg`
2. `SLACK_WEBHOOK` - Sends Slack notifications via Slack WebHook https://expensify.slack.com/services/B01AX48D7MM
3. `OS_BOTIFY_TOKEN` - Personal access token for @OSBotify user in GitHub
4. `CLA_BOTIFY_TOKEN` - Personal access token for @CLABotify user in GitHub
5. `CSC_LINK` - Required to be set for desktop code signing: https://www.electron.build/code-signing.html#travis-appveyor-and-other-ci-servers
6. `CSC_KEY_PASSWORD` - Required to be set for desktop code signing: https://www.electron.build/code-signing.html#travis-appveyor-and-other-ci-servers
7. `APPLE_ID` - Required for notarizing desktop code in `desktop/notarize.js`
8. `APPLE_ID_PASSWORD` - Required for notarizing desktop code in `desktop/notarize.js`
9. `AWS_ACCESS_KEY_ID` - Required for hosting website and desktop compiled code
10. `AWS_SECRET_ACCESS_KEY` - Required for hosting website and desktop compiled code
11. `CLOUDFLARE_TOKEN` - Required for hosting website
12. `APPLE_CONTACT_EMAIL` - Email used for contact between Expensify and Apple for https://appstoreconnect.apple.com/
13. `APPLE_CONTACT_PHONE` - Phone number used for contact between Expensify and Apple for https://appstoreconnect.apple.com/
14. `APPLE_DEMO_EMAIL` - Demo account email used for https://appstoreconnect.apple.com/
15. `APPLE_DEMO_PASSWORD` - Demo account password used for https://appstoreconnect.apple.com/
1. `android/app/android-fastlane-json-key.json.gpg`
1. `ios/chat_expensify_appstore.mobileprovision`
1. `ios/Certificates.p12.gpg`
1. `SLACK_WEBHOOK` - Sends Slack notifications via Slack WebHook https://expensify.slack.com/services/B01AX48D7MM
1. `OS_BOTIFY_TOKEN` - Personal access token for @OSBotify user in GitHub
1. `CLA_BOTIFY_TOKEN` - Personal access token for @CLABotify user in GitHub
1. `CSC_LINK` - Required to be set for desktop code signing: https://www.electron.build/code-signing.html#travis-appveyor-and-other-ci-servers
1. `CSC_KEY_PASSWORD` - Required to be set for desktop code signing: https://www.electron.build/code-signing.html#travis-appveyor-and-other-ci-servers
1. `APPLE_ID` - Required for notarizing desktop code in `desktop/notarize.js`
1. `APPLE_ID_PASSWORD` - Required for notarizing desktop code in `desktop/notarize.js`
1. `AWS_ACCESS_KEY_ID` - Required for hosting website and desktop compiled code
1. `AWS_SECRET_ACCESS_KEY` - Required for hosting website and desktop compiled code
1. `CLOUDFLARE_TOKEN` - Required for hosting website
1. `APPLE_CONTACT_EMAIL` - Email used for contact between Expensify and Apple for https://appstoreconnect.apple.com/
1. `APPLE_CONTACT_PHONE` - Phone number used for contact between Expensify and Apple for https://appstoreconnect.apple.com/
1. `APPLE_DEMO_EMAIL` - Demo account email used for https://appstoreconnect.apple.com/
1. `APPLE_DEMO_PASSWORD` - Demo account password used for https://appstoreconnect.apple.com/

## Actions

All these _workflows_ are comprised of atomic _actions_. Most of the time, we can use pre-made and independently maintained actions to create powerful workflows that meet our needs. However, when we want to do something very specific or have a more complex or robust action in mind, we can create our own _actions_.

All our actions are stored in the neighboring directory [`.github/actions`](https://github.com/Expensify/App/tree/main/.github/actions). Each action is a module comprised of three parts:

1) An [action metadata file](https://docs.github.com/en/free-pro-team@latest/actions/creating-actions/creating-a-javascript-action#creating-an-action-metadata-file) called `action.yml`. This describes the action, gives it a name, and defines its inputs and outputs.
2) A Node.js script, whose name matches the module. This is where you can implement the custom logic for your action.
3) A compiled file called index.js. This is a compiled output of the file from (2) and should _NEVER_ be directly modified.
1. An [action metadata file](https://docs.github.com/en/free-pro-team@latest/actions/creating-actions/creating-a-javascript-action#creating-an-action-metadata-file) called `action.yml`. This describes the action, gives it a name, and defines its inputs and outputs.
1. A Node.js script, whose name matches the module. This is where you can implement the custom logic for your action.
1. A compiled file called index.js. This is a compiled output of the file from (2) and should _NEVER_ be directly modified.

### Why do actions need to be compiled?

Expand Down
81 changes: 35 additions & 46 deletions .github/workflows/cherryPick.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,67 +142,58 @@ jobs:

- name: Create Pull Request
id: createPullRequest
# Version: 2.4.3
uses: repo-sync/pull-request@65194d8015be7624d231796ddee1cd52a5023cb3
with:
source_branch: ${{ github.actor }}-cherry-pick-staging-${{ github.event.inputs.PULL_REQUEST_NUMBER }}
destination_branch: staging
github_token: ${{ secrets.OS_BOTIFY_TOKEN }}
pr_title: '🍒 Cherry pick PR #${{ github.event.inputs.PULL_REQUEST_NUMBER }} to staging 🍒'
pr_body: '🍒 Cherry pick https://github.com/Expensify/App/pull/${{ github.event.inputs.PULL_REQUEST_NUMBER }} to staging 🍒'
pr_label: automerge
run: |
gh pr create \
--title "🍒 Cherry pick PR #${{ github.event.inputs.PULL_REQUEST_NUMBER }} to staging 🍒" \
--body "🍒 Cherry pick https://github.com/Expensify/App/pull/${{ github.event.inputs.PULL_REQUEST_NUMBER }} to staging 🍒" \
--label "automerge" \
--base "staging"
echo "::set-output name=PR_NUMBER::$(gh pr view --json 'number' --jq '.number')"
env:
GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }}

- name: Check if ShortVersionString is up to date
id: isShortVersionStringUpdated
uses: Expensify/App/.github/actions/checkBundleVersionStringMatch@main

- name: Auto-assign PR if there are merge conflicts or if the bundle versions are mismatched
if: ${{ !fromJSON(steps.cherryPick.outputs.SHOULD_AUTOMERGE) || !fromJSON(steps.isShortVersionStringUpdated.outputs.BUNDLE_VERSIONS_MATCH) }}
uses: actions-ecosystem/action-add-labels@a8ae047fee0ca28235f9764e1c478d2136dc15c1
with:
number: ${{ steps.createPullRequest.outputs.pr_number }}
labels: |
Engineering
Hourly
run: gh pr edit --add-label "Engineering,Hourly"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Assign the PR to the deployer
if: ${{ !fromJSON(steps.cherryPick.outputs.SHOULD_AUTOMERGE) }}
uses: actions-ecosystem/action-add-assignees@a73fcabd82d847c5e7433fcfdd58ef9f7e8a3993
with:
number: ${{ steps.createPullRequest.outputs.pr_number }}
github_token: ${{ secrets.GITHUB_TOKEN }}
assignees: ${{ steps.getCPMergeCommit.outputs.MERGE_ACTOR }}
run: gh pr edit --add-assignee ${{ steps.getCPMergeCommit.outputs.MERGE_ACTOR }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: If PR has merge conflicts, comment with instructions for assignee
if: ${{ !fromJSON(steps.cherryPick.outputs.SHOULD_AUTOMERGE) }}
uses: actions-ecosystem/action-create-comment@cd098164398331c50e7dfdd0dfa1b564a1873fac
with:
github_token: ${{ secrets.OS_BOTIFY_TOKEN }}
number: ${{ steps.createPullRequest.outputs.pr_number }}
body: |
This pull request has merge conflicts and can not be automatically merged. :disappointed:
Please manually resolve the conflicts, push your changes, and then request another reviewer to review and merge.
**Important:** There may be conflicts that GitHub is not able to detect, so please _carefully_ review this pull request before approving.
run: |
gh pr comment --body \
"This pull request has merge conflicts and can not be automatically merged. :disappointed:
Please manually resolve the conflicts, push your changes, and then request another reviewer to review and merge.
**Important:** There may be conflicts that GitHub is not able to detect, so please _carefully_ review this pull request before approving."
env:
GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }}

- name: If PR has a bundle version mismatch, comment with the instructions for assignee
if: ${{ !fromJSON(steps.isShortVersionStringUpdated.outputs.BUNDLE_VERSIONS_MATCH) }}
uses: actions-ecosystem/action-create-comment@cd098164398331c50e7dfdd0dfa1b564a1873fac
with:
github_token: ${{ secrets.OS_BOTIFY_TOKEN }}
number: ${{ steps.createPullRequest.outputs.pr_number }}
body: |
The CFBundleShortVersionString value in this PR is not compatible with the CFBundleVersion, so cherry picking it will result in an iOS deploy failure.
Please manually resolve the mismatch, push your changes, and then request another reviewer to review and merge.
**Important:** This mismatch can be caused by a failed Update Protected Branch workflow followed by a manual CP, but please confirm the cause of the mismatch before updating any version numbers.
run: |
gh pr comment --body \
"The CFBundleShortVersionString value in this PR is not compatible with the CFBundleVersion, so cherry picking it will result in an iOS deploy failure.
Please manually resolve the mismatch, push your changes, and then request another reviewer to review and merge.
**Important:** This mismatch can be caused by a failed Update Protected Branch workflow followed by a manual CP, but please confirm the cause of the mismatch before updating any version numbers."
env:
GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }}

- name: Check for an auto approve
- name: Auto-approve the PR
# Important: only auto-approve if there was no merge conflict!
if: ${{ fromJSON(steps.cherryPick.outputs.SHOULD_AUTOMERGE) }}
# Version: 2.0.0
uses: hmarr/auto-approve-action@6a9ec7556f0a7fa5b49527a1eea4878b8a22d2e0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
pull-request-number: ${{ steps.createPullRequest.outputs.pr_number }}
run: gh pr review --approve
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Check if pull request is mergeable
id: isPullRequestMergeable
Expand All @@ -211,14 +202,12 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PULL_REQUEST_NUMBER: ${{ steps.createPullRequest.outputs.pr_number }}

- name: Check for an auto merge
- name: Auto-merge the PR
# Important: only auto-merge if there was no merge conflict!
if: ${{ fromJSON(steps.cherryPick.outputs.SHOULD_AUTOMERGE) && fromJSON(steps.isPullRequestMergeable.outputs.IS_MERGEABLE) }}
# Version: 0.12.0
uses: pascalgn/automerge-action@39d831e1bb389bd242626bc25d4060064a97181c
run: gh pr merge --merge --delete-branch
env:
GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }}
PULL_REQUEST: ${{ steps.createPullRequest.outputs.pr_number }}

# Create a local git tag on staging so that GitUtils.getPullRequestsMergedBetween can use `git log` to generate a
# list of pull requests that were merged between this version tag and another.
Expand Down
52 changes: 23 additions & 29 deletions .github/workflows/updateProtectedBranch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,19 +77,18 @@ jobs:
git checkout -b update-${{ github.event.inputs.TARGET_BRANCH }}-from-${{ env.SOURCE_BRANCH }}
git merge -Xtheirs ${{ env.SOURCE_BRANCH }}
git push --set-upstream origin update-${{ github.event.inputs.TARGET_BRANCH }}-from-${{ env.SOURCE_BRANCH }}
echo "SOURCE_BRANCH=update-${{ github.event.inputs.TARGET_BRANCH }}-from-${{ env.SOURCE_BRANCH }}" >> "$GITHUB_ENV"
- name: Create Pull Request
id: createPullRequest
# Version: 2.4.3
uses: repo-sync/pull-request@65194d8015be7624d231796ddee1cd52a5023cb3
with:
source_branch: ${{ env.SOURCE_BRANCH }}
destination_branch: ${{ github.event.inputs.TARGET_BRANCH }}
github_token: ${{ secrets.OS_BOTIFY_TOKEN }}
pr_title: Update version to ${{ env.NEW_VERSION }} on ${{ github.event.inputs.TARGET_BRANCH }}
pr_body: Update version to ${{ env.NEW_VERSION }}
pr_label: automerge
run: |
gh pr create \
--title "Update version to ${{ env.NEW_VERSION }} on ${{ github.event.inputs.TARGET_BRANCH }}" \
--body "Update version to ${{ env.NEW_VERSION }}" \
--label "automerge" \
--base ${{ github.event.inputs.TARGET_BRANCH }}
echo "::set-output name=PR_NUMBER::$(gh pr view --json 'number' --jq '.number')"
env:
GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }}

- name: Check changed files
if: ${{ github.event.inputs.TARGET_BRANCH == 'main' }}
Expand All @@ -98,46 +97,41 @@ jobs:
uses: umani/changed-files@1d252c611c64289d35243fc37ece7323ea5e93e1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
pr-number: ${{ steps.createPullRequest.outputs.pr_number }}
pr-number: ${{ steps.createPullRequest.outputs.PR_NUMBER }}

- name: Validate changed files
if: ${{ github.event.inputs.TARGET_BRANCH == 'main' && (steps.changedFiles.outputs.files_updated != 'android/app/build.gradle ios/NewExpensify/Info.plist ios/NewExpensifyTests/Info.plist package-lock.json package.json' || steps.changedFiles.outputs.files_created != '' || steps.changedFiles.outputs.files_deleted != '') }}
run: exit 1

- name: Check for an auto approve
# Version: 2.0.0
uses: hmarr/auto-approve-action@6a9ec7556f0a7fa5b49527a1eea4878b8a22d2e0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
pull-request-number: ${{ steps.createPullRequest.outputs.pr_number }}
- name: Auto-approve the PR
run: gh pr review --approve
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Check if pull request is mergeable
id: isPullRequestMergeable
uses: Expensify/App/.github/actions/isPullRequestMergeable@main
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PULL_REQUEST_NUMBER: ${{ steps.createPullRequest.outputs.pr_number }}
PULL_REQUEST_NUMBER: ${{ steps.createPullRequest.outputs.PR_NUMBER }}

- name: Leave comment if PR is not mergeable
if: ${{ !fromJSON(steps.isPullRequestMergeable.outputs.IS_MERGEABLE) }}
uses: actions-ecosystem/action-create-comment@cd098164398331c50e7dfdd0dfa1b564a1873fac
with:
github_token: ${{ secrets.OS_BOTIFY_TOKEN }}
number: ${{ steps.createPullRequest.outputs.pr_number }}
body: |
:bell: @Expensify/mobile-deployers :bell: - The Update Protected Branch workflow has failed because this PR was not mergable.
If you are the deployer this week, please resolve the error and merge this PR to continue the deploy process.
run: |
gh pr comment --body \
":bell: @Expensify/mobile-deployers :bell: - The Update Protected Branch workflow has failed because this PR was not mergable.
If you are the deployer this week, please resolve the error and merge this PR to continue the deploy process."
env:
GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }}

- name: Fail workflow if PR is not mergeable
if: ${{ steps.isPullRequestMergeable.outputs.IS_MERGEABLE == 'false' }}
run: exit 1

- name: Check for an auto merge
# Version: 0.12.0
uses: pascalgn/automerge-action@39d831e1bb389bd242626bc25d4060064a97181c
- name: Auto-merge the PR
run: gh pr merge --merge --delete-branch
env:
GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }}
PULL_REQUEST: ${{ steps.createPullRequest.outputs.pr_number }}

# This Slack step is duplicated in all workflows, if you make a change to this step, make sure to update all
# the other workflows with the same change
Expand Down

0 comments on commit da973c7

Please sign in to comment.