Docs Preview workflow (scikit-hep/awkward)
The Docs Preview workflow from scikit-hep/awkward, explained and optimized by Latchkey.
A
CI health: A - excellent
Point runs-on at Latchkey and get job timeouts, self-healing for flaky steps, and up to 58% lower cost, applied automatically.
What it does
This is the Docs Preview workflow from the scikit-hep/awkward repository, a real project running GitHub Actions. It is shown here with attribution under its BSD-3-Clause license.
Below, Latchkey shows a faster, safer version produced by its optimization engine.
The workflow
workflow (.yml)
name: Docs Preview
on: # zizmor: ignore[dangerous-triggers] This privileged workflow only deploys artifacts from a completed CI run.
workflow_run:
workflows: [CI]
types:
- completed
concurrency:
group: docs-preview-${{ github.event.workflow_run.id }}
cancel-in-progress: true
permissions:
contents: read
jobs:
branch-preview:
runs-on: ubuntu-24.04
name: Deploy Branch Preview
if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request' }}
permissions:
id-token: write # Required to assume the AWS deployment role through OIDC.
contents: read
pull-requests: write # Required to comment with the preview URL.
env:
S3_BUCKET: "preview.awkward-array.org"
DEPLOY_URL: "http://preview.awkward-array.org.s3-website.us-east-1.amazonaws.com"
environment:
name: docs
url: "${{ env.DEPLOY_URL }}/PR${{ steps.pr_number.outputs.pr_number }}"
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@254c19bd240aabef8777f48595e9d2d7b972184b # v6.2.1
with:
aws-region: eu-west-2
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_DEPLOY_ROLE }}
- name: Download rendered docs
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
let docsArtifact = allArtifacts.data.artifacts.filter((artifact) => {
return artifact.name == "docs"
})[0];
let PRNumberArtifact = allArtifacts.data.artifacts.filter((artifact) => {
return artifact.name == "pr_number"
})[0];
let downloadDocs = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: docsArtifact.id,
archive_format: 'zip',
});
let downloadPRNumber = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: PRNumberArtifact.id,
archive_format: 'zip',
});
const fs = require('fs');
const path = require('path');
const temp = path.join(process.env.RUNNER_TEMP, 'artifacts');
if (!fs.existsSync(temp)){
fs.mkdirSync(temp);
}
fs.writeFileSync(path.join(temp, 'docs.zip'), Buffer.from(downloadDocs.data));
fs.writeFileSync(path.join(temp, 'pr_number.zip'), Buffer.from(downloadPRNumber.data));
- name: Unzip artifacts
run: |
unzip "${RUNNER_TEMP}/artifacts/docs.zip" -d "${RUNNER_TEMP}/artifacts"
unzip "${RUNNER_TEMP}/artifacts/pr_number.zip" -d "${RUNNER_TEMP}/artifacts"
- name: Read PR number
id: pr_number
run: |
echo "pr_number=$(cat "${RUNNER_TEMP}/artifacts/pr_number.txt")" >> "${GITHUB_OUTPUT}"
rm "${RUNNER_TEMP}/artifacts/pr_number.txt"
rm "${RUNNER_TEMP}/artifacts/docs.zip"
rm "${RUNNER_TEMP}/artifacts/pr_number.zip"
- name: Sync artifacts
run: |
aws s3 sync "${RUNNER_TEMP}/artifacts/" "s3://${S3_BUCKET}/PR${STEPS_PR_NUMBER_OUTPUTS_PR_NUMBER}"
env:
STEPS_PR_NUMBER_OUTPUTS_PR_NUMBER: ${{ steps.pr_number.outputs.pr_number }}
- name: Try to find previous bot comment
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4.0.0
id: fc
with:
issue-number: ${{ steps.pr_number.outputs.pr_number }}
comment-author: 'github-actions[bot]'
body-includes: The documentation preview is ready to be viewed
- name: Create comment with preview link
if: steps.fc.outputs.comment-id == ''
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
PR_NUMBER: ${{ steps.pr_number.outputs.pr_number }}
PREVIEW_URL: ${{ env.DEPLOY_URL }}/PR${{ steps.pr_number.outputs.pr_number }}
run: gh pr comment "${PR_NUMBER}" --repo "${REPO}" --body "The documentation preview is ready to be viewed at <${PREVIEW_URL}>"
The same workflow, on Latchkey
Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.
name: Docs Preview on: # zizmor: ignore[dangerous-triggers] This privileged workflow only deploys artifacts from a completed CI run. workflow_run: workflows: [CI] types: - completed concurrency: group: docs-preview-${{ github.event.workflow_run.id }} cancel-in-progress: true permissions: contents: read jobs: branch-preview: timeout-minutes: 30 runs-on: latchkey-small name: Deploy Branch Preview if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request' }} permissions: id-token: write # Required to assume the AWS deployment role through OIDC. contents: read pull-requests: write # Required to comment with the preview URL. env: S3_BUCKET: "preview.awkward-array.org" DEPLOY_URL: "http://preview.awkward-array.org.s3-website.us-east-1.amazonaws.com" environment: name: docs url: "${{ env.DEPLOY_URL }}/PR${{ steps.pr_number.outputs.pr_number }}" steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@254c19bd240aabef8777f48595e9d2d7b972184b # v6.2.1 with: aws-region: eu-west-2 role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_DEPLOY_ROLE }} - name: Download rendered docs uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ owner: context.repo.owner, repo: context.repo.repo, run_id: context.payload.workflow_run.id, }); let docsArtifact = allArtifacts.data.artifacts.filter((artifact) => { return artifact.name == "docs" })[0]; let PRNumberArtifact = allArtifacts.data.artifacts.filter((artifact) => { return artifact.name == "pr_number" })[0]; let downloadDocs = await github.rest.actions.downloadArtifact({ owner: context.repo.owner, repo: context.repo.repo, artifact_id: docsArtifact.id, archive_format: 'zip', }); let downloadPRNumber = await github.rest.actions.downloadArtifact({ owner: context.repo.owner, repo: context.repo.repo, artifact_id: PRNumberArtifact.id, archive_format: 'zip', }); const fs = require('fs'); const path = require('path'); const temp = path.join(process.env.RUNNER_TEMP, 'artifacts'); if (!fs.existsSync(temp)){ fs.mkdirSync(temp); } fs.writeFileSync(path.join(temp, 'docs.zip'), Buffer.from(downloadDocs.data)); fs.writeFileSync(path.join(temp, 'pr_number.zip'), Buffer.from(downloadPRNumber.data)); - name: Unzip artifacts run: | unzip "${RUNNER_TEMP}/artifacts/docs.zip" -d "${RUNNER_TEMP}/artifacts" unzip "${RUNNER_TEMP}/artifacts/pr_number.zip" -d "${RUNNER_TEMP}/artifacts" - name: Read PR number id: pr_number run: | echo "pr_number=$(cat "${RUNNER_TEMP}/artifacts/pr_number.txt")" >> "${GITHUB_OUTPUT}" rm "${RUNNER_TEMP}/artifacts/pr_number.txt" rm "${RUNNER_TEMP}/artifacts/docs.zip" rm "${RUNNER_TEMP}/artifacts/pr_number.zip" - name: Sync artifacts run: | aws s3 sync "${RUNNER_TEMP}/artifacts/" "s3://${S3_BUCKET}/PR${STEPS_PR_NUMBER_OUTPUTS_PR_NUMBER}" env: STEPS_PR_NUMBER_OUTPUTS_PR_NUMBER: ${{ steps.pr_number.outputs.pr_number }} - name: Try to find previous bot comment uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4.0.0 id: fc with: issue-number: ${{ steps.pr_number.outputs.pr_number }} comment-author: 'github-actions[bot]' body-includes: The documentation preview is ready to be viewed - name: Create comment with preview link if: steps.fc.outputs.comment-id == '' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} REPO: ${{ github.repository }} PR_NUMBER: ${{ steps.pr_number.outputs.pr_number }} PREVIEW_URL: ${{ env.DEPLOY_URL }}/PR${{ steps.pr_number.outputs.pr_number }} run: gh pr comment "${PR_NUMBER}" --repo "${REPO}" --body "The documentation preview is ready to be viewed at <${PREVIEW_URL}>"
What changed
- Run on Latchkey managed runners with one line (
runs-on), which apply the fixes below automatically and self-heal transient failures. This example useslatchkey-small; pick the runner size that fits the job. - Add a job timeout so a hung step cannot burn hours of runner time.
This workflow runs 1 job per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.