Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minimize PR overhead for non-code changes #16279

Merged
merged 9 commits into from
Dec 22, 2023
5 changes: 4 additions & 1 deletion .github/workflows/continuous_integration.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
name: Continuous Integration
on: [pull_request]
on:
pull_request:
paths-ignore:
- '**.md'
permissions:
pull-requests: write
issues: write
Expand Down
105 changes: 105 additions & 0 deletions .github/workflows/set-tests-statuses.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
name: Set Test Statuses
on:
- pull_request_target
permissions:
pull-requests: write
checks: write
contents: write
statuses: write
jobs:
# Tugboat tests are not automatically set pending, even though they are
# required in branch protection rules (see #10553).
#
# Therefore, a PR can inappropriately appear to be ready to merge if,
# for instance, a composer.lock merge conflict prevents the Tugboat
# preview from successfully building.
#
# 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: 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')) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Currently we use the rule that any non-.md file is a code change, which is probably the safest method for now.

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
va/tests/phpunit
va/tests/content-build-gql
va/tests/status-error
)
for test_name in "${test_names[@]}"; do
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
"/repos/${GITHUB_REPOSITORY}/statuses/${SHA}" \
-f state='pending' \
-f context="${test_name}";
done;
env:
SHA: ${{ github.event.pull_request.head.sha }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
43 changes: 0 additions & 43 deletions .github/workflows/set-tugboat-tests-pending.yml

This file was deleted.

39 changes: 39 additions & 0 deletions .github/workflows/tugboat-pr-closed.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
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. 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)
echo "Preview ID: ${PREVIEW_ID}"
echo "PREVIEW_ID=$PREVIEW_ID" >> $GITHUB_ENV
- name: Cleanup temporary file
JunTaoLuo marked this conversation as resolved.
Show resolved Hide resolved
run: rm .tugboat_preview.txt
- name: Delete Tugboat Preview
run: |
curl --fail \
-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 }}
47 changes: 47 additions & 0 deletions .github/workflows/tugboat-pr-opened.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
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 --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" }' \
Copy link
Contributor

@ndouglas ndouglas Dec 15, 2023

Choose a reason for hiding this comment

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

If this is all of the information that we need to provide, this is fantastic! It seems like it'll resolve an issue we've had where the TIC interprets inline code (e.g. "Fixed a bug in some_function()") as an attempt to inject code and silently rejects it, causing the Tugboat build to fail.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fascinating. That was probably pretty hard to find haha. Though I suppose commit messages and even branch names theoretically can be unsafe if we used it in some shell commands. Hence

  • checked for any injection of user content into protected contexts?

-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
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:
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
39 changes: 39 additions & 0 deletions .github/workflows/tugboat-pr-updated.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
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. 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)
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 --fail \
-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
74 changes: 74 additions & 0 deletions .github/workflows/tugboat-refresh-cache.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Refresh Tugboat Preview ID Cache
on:
# Every 6 hours.
schedule:
- cron: '0 */6 * * *'
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
2 changes: 1 addition & 1 deletion tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Loading