From 10995570c28e5640c3f4538b2700da4a5006513e Mon Sep 17 00:00:00 2001 From: John Luo Date: Wed, 6 Dec 2023 22:53:19 -0500 Subject: [PATCH 1/9] Minimize PR overhead for non-code changes --- .github/workflows/continuous_integration.yml | 5 +- .../workflows/set-tugboat-tests-pending.yml | 76 +++++++++++++++++-- tests.yml | 2 +- 3 files changed, 75 insertions(+), 8 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 8ae74fb953..74d8836a00 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -1,5 +1,8 @@ name: Continuous Integration -on: [pull_request] +on: + pull_request: + paths-ignore: + - '**.md' permissions: pull-requests: write issues: write diff --git a/.github/workflows/set-tugboat-tests-pending.yml b/.github/workflows/set-tugboat-tests-pending.yml index dda9db6f8f..10781062d9 100644 --- a/.github/workflows/set-tugboat-tests-pending.yml +++ b/.github/workflows/set-tugboat-tests-pending.yml @@ -1,4 +1,4 @@ -name: Set Tugboat Tests Pending +name: Set Test Statuses on: - pull_request_target permissions: @@ -14,15 +14,79 @@ jobs: # for instance, a composer.lock merge conflict prevents the Tugboat # preview from successfully building. # - # This action sets these tests pending from an immediate GitHub Action - # so that we can trust our automated code review processes more. - set-tugboat-tests-pending: - name: Set Tugboat Tests Pending + # Additionally, CI tests are only run for code changes but they are + # required checks, even for documentation only changes. In these cases, + # the tests should be skipped since no functional changes have occured. + # + # To address these two issues, this action sets check statuses directly + # to the appropriate states: + # - For docs only changes, all required checks are set to 'success' + # - For code changes, Tugboat tests are set to 'pending' so that we can + # trust our automated code review processes more. + set-test-statuses: + name: Set Tests Statuses runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: Set status for Tugboat tasks. + - name: Check for documentation only changes + id: docs-only + uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1 + with: + script: | + const opts = github.rest.pulls.listFiles.endpoint.merge({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + }) + const files = await github.paginate( + opts, + (response) => response.data.map( + (file) => file.filename + ) + ) + + for (const file of files) { + console.log(`Checking PR file: ${file}`) + if (!file.endsWith('.md')) { + console.log(`Code change found in: ${file}`) + return "false" + } + } + + console.log(`No code change found.`) + return "true" + result-encoding: string + - name: Set status for documentation changes. + if: ${{ steps.docs-only.outputs.result == 'true' }} + run: | + test_names=( + va/tests/cypress + va/tests/phpunit + va/tests/content-build-gql + va/tests/status-error + 'Composer Validate' + 'Check Fields' + ESLint + Stylelint + PHPStan + PHPUnit + PHP_CodeSniffer + 'PHP Lint' + ) + for test_name in "${test_names[@]}"; do + gh api \ + --method POST \ + -H "Accept: application/vnd.github+json" \ + "/repos/${GITHUB_REPOSITORY}/statuses/${SHA}" \ + -f state='success' \ + -f context="${test_name}"; + done; + env: + SHA: ${{ github.event.pull_request.head.sha }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Set status for code changes. + if: ${{ steps.docs-only.outputs.result == 'false' }} run: | test_names=( va/tests/cypress diff --git a/tests.yml b/tests.yml index 45046ac1d6..53957e4d96 100644 --- a/tests.yml +++ b/tests.yml @@ -10,7 +10,7 @@ output: 'group' tasks: # Any changes to test names or additions or removals must be updated in - # .github/workflows/set-tugboat-tests-pending.yml as well for the + # .github/workflows/set-tests-statuses.yml as well for the # test to be required effectively. # The following is necessary to ensure that the tests are set to "pending" From d40c3478f5e50624e8a3cbbf95c2d4bb12657113 Mon Sep 17 00:00:00 2001 From: John Luo Date: Wed, 6 Dec 2023 22:53:52 -0500 Subject: [PATCH 2/9] Rename workflow --- .../{set-tugboat-tests-pending.yml => set-tests-statuses.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{set-tugboat-tests-pending.yml => set-tests-statuses.yml} (100%) diff --git a/.github/workflows/set-tugboat-tests-pending.yml b/.github/workflows/set-tests-statuses.yml similarity index 100% rename from .github/workflows/set-tugboat-tests-pending.yml rename to .github/workflows/set-tests-statuses.yml From 7bead955354ab00cad0b2382f056af6fc461c2fe Mon Sep 17 00:00:00 2001 From: John Luo Date: Fri, 8 Dec 2023 21:37:47 -0500 Subject: [PATCH 3/9] Replace Tugboat webhook integration with Tugboat API in GHA workflows --- .github/workflows/tugboat-pr-closed.yml | 38 ++++++++++++++++++++++ .github/workflows/tugboat-pr-opened.yml | 41 ++++++++++++++++++++++++ .github/workflows/tugboat-pr-updated.yml | 38 ++++++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 .github/workflows/tugboat-pr-closed.yml create mode 100644 .github/workflows/tugboat-pr-opened.yml create mode 100644 .github/workflows/tugboat-pr-updated.yml diff --git a/.github/workflows/tugboat-pr-closed.yml b/.github/workflows/tugboat-pr-closed.yml new file mode 100644 index 0000000000..729e990fcf --- /dev/null +++ b/.github/workflows/tugboat-pr-closed.yml @@ -0,0 +1,38 @@ +name: Delete Tugboat Preview +on: + pull_request: + types: + - closed + paths-ignore: + - '**.md' + +jobs: + tugboat_delete_preview: + runs-on: self-hosted + env: + NODE_EXTRA_CA_CERTS: /etc/ssl/certs/ca-certificates.crt + name: Delete Tugboat Preview + steps: + - name: Restore Preview ID + uses: actions/cache/restore@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 + with: + path: .tugboat_preview.txt + key: ${{ runner.os }}-tugboat-preview-id-pr-${{ github.event.pull_request.number }} + - name: Set Preview ID + run: | + if ! [ -f .tugboat_preview.txt ]; then + echo "Preview ID not found, please manually delete Tugboat Preview." + exit 1 + fi + PREVIEW_ID=$(cat .tugboat_preview.txt) + echo "Preview ID: ${PREVIEW_ID}" + echo "PREVIEW_ID=$PREVIEW_ID" >> $GITHUB_ENV + - name: Cleanup temporary file + run: rm .tugboat_preview.txt + - name: Delete Tugboat Preview + run: | + curl -H "Authorization: Bearer ${{ secrets.TUGBOAT_API_TOKEN }}" \ + -H "Content-Type: application/json" \ + -X DELETE \ + -d '{ "force": "false" }' \ + https://api.tugboat.vfs.va.gov/v3/previews/${{ env.PREVIEW_ID }} \ No newline at end of file diff --git a/.github/workflows/tugboat-pr-opened.yml b/.github/workflows/tugboat-pr-opened.yml new file mode 100644 index 0000000000..a9d8da55e2 --- /dev/null +++ b/.github/workflows/tugboat-pr-opened.yml @@ -0,0 +1,41 @@ +name: Create Tugboat Preview +on: + pull_request: + types: + - opened + - reopened + paths-ignore: + - '**.md' + +jobs: + tugboat_create_preview: + runs-on: self-hosted + env: + NODE_EXTRA_CA_CERTS: /etc/ssl/certs/ca-certificates.crt + name: Create Tugboat Preview + steps: + - name: Create Tugboat Preview + id: tugboat_pr_preview + run: | + curl -H "Authorization: Bearer ${{ secrets.TUGBOAT_API_TOKEN }}" \ + -H "Content-Type: application/json" \ + -X POST \ + -d '{ "repo": "${{ secrets.TUGBOAT_REPOSITORY }}", "ref": "${{ github.event.pull_request.number }}", "name": "${{ github.event.pull_request.title }}", "type": "pullrequest" }' \ + -o .tugboat_response.json \ + https://api.tugboat.vfs.va.gov/v3/previews + - name: Diagnostics + run: cat .tugboat_response.json + - name: Extract Preview ID + run: jq -r .preview .tugboat_response.json > .tugboat_preview.txt + - name: Delete Previous Preview ID + continue-on-error: true + run: | + gh extension install actions/gh-actions-cache + gh actions-cache delete "${{ runner.os }}-tugboat-preview-id-pr-${{ github.event.pull_request.number }}" --confirm + - name: Save Preview ID + uses: actions/cache/save@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 + with: + path: .tugboat_preview.txt + key: ${{ runner.os }}-tugboat-preview-id-pr-${{ github.event.pull_request.number }} + - name: Cleanup temporary file + run: rm .tugboat_preview.txt \ No newline at end of file diff --git a/.github/workflows/tugboat-pr-updated.yml b/.github/workflows/tugboat-pr-updated.yml new file mode 100644 index 0000000000..43139c82e3 --- /dev/null +++ b/.github/workflows/tugboat-pr-updated.yml @@ -0,0 +1,38 @@ +name: Rebuild Tugboat Preview +on: + pull_request: + types: + - synchronize + paths-ignore: + - '**.md' + +jobs: + tugboat_rebuild_preview: + runs-on: self-hosted + env: + NODE_EXTRA_CA_CERTS: /etc/ssl/certs/ca-certificates.crt + name: Rebuild Tugboat Preview + steps: + - name: Restore Preview ID + uses: actions/cache/restore@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 + with: + path: .tugboat_preview.txt + key: ${{ runner.os }}-tugboat-preview-id-pr-${{ github.event.pull_request.number }} + - name: Set Preview ID + run: | + if ! [ -f .tugboat_preview.txt ]; then + echo "Preview ID not found, please manually rebuild Tugboat Preview." + exit 1 + fi + PREVIEW_ID=$(cat .tugboat_preview.txt) + echo "Preview ID: ${PREVIEW_ID}" + echo "PREVIEW_ID=$PREVIEW_ID" >> $GITHUB_ENV + - name: Cleanup temporary file + run: rm .tugboat_preview.txt + - name: Rebuild Tugboat Preview + run: | + curl -H "Authorization: Bearer ${{ secrets.TUGBOAT_API_TOKEN }}" \ + -H "Content-Type: application/json" \ + -X POST \ + -d '{ "children": "false", "force": "false" }' \ + https://api.tugboat.vfs.va.gov/v3/previews/${{ env.PREVIEW_ID }}/rebuild \ No newline at end of file From 774c15695ceec5ba18084e6a5d881a2cb51fb42d Mon Sep 17 00:00:00 2001 From: John Luo Date: Fri, 8 Dec 2023 21:47:16 -0500 Subject: [PATCH 4/9] Fail workflow step if Tugboat API errors --- .github/workflows/tugboat-pr-closed.yml | 3 ++- .github/workflows/tugboat-pr-opened.yml | 3 ++- .github/workflows/tugboat-pr-updated.yml | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tugboat-pr-closed.yml b/.github/workflows/tugboat-pr-closed.yml index 729e990fcf..18463d0e19 100644 --- a/.github/workflows/tugboat-pr-closed.yml +++ b/.github/workflows/tugboat-pr-closed.yml @@ -31,7 +31,8 @@ jobs: run: rm .tugboat_preview.txt - name: Delete Tugboat Preview run: | - curl -H "Authorization: Bearer ${{ secrets.TUGBOAT_API_TOKEN }}" \ + curl --fail \ + -H "Authorization: Bearer ${{ secrets.TUGBOAT_API_TOKEN }}" \ -H "Content-Type: application/json" \ -X DELETE \ -d '{ "force": "false" }' \ diff --git a/.github/workflows/tugboat-pr-opened.yml b/.github/workflows/tugboat-pr-opened.yml index a9d8da55e2..a9d7af3081 100644 --- a/.github/workflows/tugboat-pr-opened.yml +++ b/.github/workflows/tugboat-pr-opened.yml @@ -17,7 +17,8 @@ jobs: - name: Create Tugboat Preview id: tugboat_pr_preview run: | - curl -H "Authorization: Bearer ${{ secrets.TUGBOAT_API_TOKEN }}" \ + curl --fail \ + -H "Authorization: Bearer ${{ secrets.TUGBOAT_API_TOKEN }}" \ -H "Content-Type: application/json" \ -X POST \ -d '{ "repo": "${{ secrets.TUGBOAT_REPOSITORY }}", "ref": "${{ github.event.pull_request.number }}", "name": "${{ github.event.pull_request.title }}", "type": "pullrequest" }' \ diff --git a/.github/workflows/tugboat-pr-updated.yml b/.github/workflows/tugboat-pr-updated.yml index 43139c82e3..2739d9ed4e 100644 --- a/.github/workflows/tugboat-pr-updated.yml +++ b/.github/workflows/tugboat-pr-updated.yml @@ -31,7 +31,8 @@ jobs: run: rm .tugboat_preview.txt - name: Rebuild Tugboat Preview run: | - curl -H "Authorization: Bearer ${{ secrets.TUGBOAT_API_TOKEN }}" \ + curl --fail \ + -H "Authorization: Bearer ${{ secrets.TUGBOAT_API_TOKEN }}" \ -H "Content-Type: application/json" \ -X POST \ -d '{ "children": "false", "force": "false" }' \ From 3c43d7952032f61a68de72bc51a80fef2cb3ff9f Mon Sep 17 00:00:00 2001 From: John Luo Date: Wed, 13 Dec 2023 12:19:07 -0500 Subject: [PATCH 5/9] Remove redundant checkout in workflow --- .github/workflows/set-tests-statuses.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/set-tests-statuses.yml b/.github/workflows/set-tests-statuses.yml index 10781062d9..7ecfc410e2 100644 --- a/.github/workflows/set-tests-statuses.yml +++ b/.github/workflows/set-tests-statuses.yml @@ -27,8 +27,6 @@ jobs: name: Set Tests Statuses runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Check for documentation only changes id: docs-only uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1 From cbade80fc49479505469848d288568243282105d Mon Sep 17 00:00:00 2001 From: John Luo Date: Thu, 14 Dec 2023 16:51:31 -0500 Subject: [PATCH 6/9] Add a Preview ID refresher workflow to prevent preview IDs from being lost --- .github/workflows/tugboat-refresh-cache.yml | 74 +++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 .github/workflows/tugboat-refresh-cache.yml diff --git a/.github/workflows/tugboat-refresh-cache.yml b/.github/workflows/tugboat-refresh-cache.yml new file mode 100644 index 0000000000..cd248385e8 --- /dev/null +++ b/.github/workflows/tugboat-refresh-cache.yml @@ -0,0 +1,74 @@ +name: Refresh Tugboat Preview ID Cache +on: + # UTC 0am, everyday. + schedule: + - cron: '0 0 * * *' +jobs: + # Collects the cache keys that need to be refreshed + collect_cache_keys: + name: Collect Tugboat Preview ID cache keys that need to be refreshed + outputs: + matrix: ${{ steps.cache-keys.outputs.result }} + runs-on: ubuntu-latest + steps: + - name: Cross reference open PRs against cache keys in repo + id: cache-keys + uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1 + with: + script: | + const prs = await github.paginate( + github.rest.pulls.list, + { + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + }, + (response) => response.data.map((pr) => pr.number) + ) + + for (const pr of prs) { + console.log(`PR: ${pr}`) + } + + const cacheKeys = await github.paginate( + github.rest.actions.getActionsCacheList, + { + owner: context.repo.owner, + repo: context.repo.repo, + }, + (response) => response.data.map((cache) => cache.key) + ) + + for (const key of cacheKeys) { + console.log(`Key: ${key}`) + } + + const toRefresh = [] + for (const pr of prs) { + if (cacheKeys.includes(`${{ runner.os }}-tugboat-preview-id-pr-${pr}`)) { + console.log(`Need to refresh: ${pr}`) + toRefresh.push(pr) + } + } + + const result = JSON.stringify(toRefresh) + console.log(`Refresh Keys: ${result}`) + return result + result-encoding: string + + # Refresh cache for given keys + refresh_cache: + name: Refresh cache for given keys + needs: [ collect_cache_keys ] + runs-on: ubuntu-latest + strategy: + matrix: + value: ${{fromJSON(needs.collect_cache_keys.outputs.matrix)}} + steps: + - name: Refresh Preview ID + uses: actions/cache/restore@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 + with: + path: .tugboat_preview.txt + key: ${{ runner.os }}-tugboat-preview-id-pr-${{ matrix.value }} + - name: Cleanup temporary file + run: rm .tugboat_preview.txt From d0e8fdc9ca19d178631bfb2d78dcbeda09f42f03 Mon Sep 17 00:00:00 2001 From: John Luo Date: Fri, 15 Dec 2023 11:01:31 -0500 Subject: [PATCH 7/9] Increase cache refresh frequency --- .github/workflows/tugboat-refresh-cache.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tugboat-refresh-cache.yml b/.github/workflows/tugboat-refresh-cache.yml index cd248385e8..bd1167fa11 100644 --- a/.github/workflows/tugboat-refresh-cache.yml +++ b/.github/workflows/tugboat-refresh-cache.yml @@ -1,8 +1,8 @@ name: Refresh Tugboat Preview ID Cache on: - # UTC 0am, everyday. + # Every 6 hours. schedule: - - cron: '0 0 * * *' + - cron: '0 */6 * * *' jobs: # Collects the cache keys that need to be refreshed collect_cache_keys: From 46c91081cea1b47846986298d4dd6029cb796454 Mon Sep 17 00:00:00 2001 From: John Luo Date: Fri, 15 Dec 2023 12:50:52 -0500 Subject: [PATCH 8/9] Add contact details when workflow fails --- .github/workflows/tugboat-pr-closed.yml | 2 +- .github/workflows/tugboat-pr-updated.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tugboat-pr-closed.yml b/.github/workflows/tugboat-pr-closed.yml index 18463d0e19..469afce94e 100644 --- a/.github/workflows/tugboat-pr-closed.yml +++ b/.github/workflows/tugboat-pr-closed.yml @@ -21,7 +21,7 @@ jobs: - name: Set Preview ID run: | if ! [ -f .tugboat_preview.txt ]; then - echo "Preview ID not found, please manually delete Tugboat Preview." + echo "Preview ID not found, please manually delete Tugboat Preview. Contact platform-cms-qa on Github or CMS QA Engineers in #cms-support on Slack for assistance." exit 1 fi PREVIEW_ID=$(cat .tugboat_preview.txt) diff --git a/.github/workflows/tugboat-pr-updated.yml b/.github/workflows/tugboat-pr-updated.yml index 2739d9ed4e..43b735445f 100644 --- a/.github/workflows/tugboat-pr-updated.yml +++ b/.github/workflows/tugboat-pr-updated.yml @@ -21,7 +21,7 @@ jobs: - name: Set Preview ID run: | if ! [ -f .tugboat_preview.txt ]; then - echo "Preview ID not found, please manually rebuild Tugboat Preview." + echo "Preview ID not found, please manually rebuild Tugboat Preview. Contact platform-cms-qa on Github or CMS QA Engineers in #cms-support on Slack for assistance." exit 1 fi PREVIEW_ID=$(cat .tugboat_preview.txt) From e91ef24e88e330a8bd49ab3285207acfaba8ff46 Mon Sep 17 00:00:00 2001 From: John Luo Date: Thu, 21 Dec 2023 17:24:01 -0600 Subject: [PATCH 9/9] Use Github Script instead of GH CLI --- .github/workflows/tugboat-pr-opened.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tugboat-pr-opened.yml b/.github/workflows/tugboat-pr-opened.yml index a9d7af3081..acb07e992b 100644 --- a/.github/workflows/tugboat-pr-opened.yml +++ b/.github/workflows/tugboat-pr-opened.yml @@ -30,9 +30,14 @@ jobs: run: jq -r .preview .tugboat_response.json > .tugboat_preview.txt - name: Delete Previous Preview ID continue-on-error: true - run: | - gh extension install actions/gh-actions-cache - gh actions-cache delete "${{ runner.os }}-tugboat-preview-id-pr-${{ github.event.pull_request.number }}" --confirm + uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1 + with: + script: | + await github.rest.actions.deleteActionsCacheByKey({ + owner: context.repo.owner, + repo: context.repo.repo, + key: `${{ runner.os }}-tugboat-preview-id-pr-${{ github.event.pull_request.number }}`, + }); - name: Save Preview ID uses: actions/cache/save@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 with: