Deploy project site v0.1.22 7520b1ee-8304-4d58-aac6-301c161e4911 #48
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Deploy project site | |
| run-name: Deploy project site ${{ inputs.release_tag || github.ref_name }} ${{ inputs.dispatch_nonce || github.sha }} | |
| on: | |
| push: | |
| branches: [main, master] | |
| paths: | |
| - "apps/site/**" | |
| - "scripts/check-release-publication.mjs" | |
| - "scripts/deploy-site.mjs" | |
| - "scripts/dispatch-site-deployment.mjs" | |
| - "scripts/generate-release-manifest.mjs" | |
| - "scripts/inspect-linux-update.mjs" | |
| - "scripts/read-bounded-response.mjs" | |
| - "scripts/reconcile-release-assets.mjs" | |
| - "scripts/wait-for-release-assets.mjs" | |
| - "package.json" | |
| - "pnpm-lock.yaml" | |
| - ".github/workflows/deploy-site.yml" | |
| workflow_dispatch: | |
| inputs: | |
| release_tag: | |
| description: Published release tag whose immutable source must be deployed. | |
| required: true | |
| type: string | |
| dispatch_nonce: | |
| description: Unique release-workflow dispatch identity. | |
| required: true | |
| type: string | |
| permissions: | |
| contents: read | |
| id-token: write | |
| concurrency: | |
| group: t4code-net-production | |
| cancel-in-progress: false | |
| jobs: | |
| deploy: | |
| if: ${{ github.event_name != 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/') }} | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 50 | |
| environment: | |
| name: production | |
| url: https://t4code.net | |
| steps: | |
| - name: Check out trusted workflow source | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| ref: ${{ github.sha }} | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Read trusted workflow source version | |
| id: source | |
| shell: bash | |
| env: | |
| TRUSTED_SHA: ${{ github.sha }} | |
| run: | | |
| set -euo pipefail | |
| version=$(node -p "require('./package.json').version") | |
| printf 'version=%s\ntrusted_sha=%s\n' "$version" "$TRUSTED_SHA" >> "$GITHUB_OUTPUT" | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 | |
| with: | |
| version: 11.10.0 | |
| - name: Install Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: 24.13.1 | |
| - name: Confirm the published release from the release workflow | |
| id: published_release | |
| if: ${{ github.event_name == 'workflow_dispatch' }} | |
| env: | |
| RELEASE_VERSION: ${{ steps.source.outputs.version }} | |
| run: node scripts/wait-for-release-assets.mjs --version "$RELEASE_VERSION" --timeout-ms 2400000 --interval-ms 15000 | |
| - name: Classify the stable release referenced by an ordinary main push | |
| id: release_state | |
| if: ${{ github.event_name == 'push' }} | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| RELEASE_VERSION: ${{ steps.source.outputs.version }} | |
| run: node scripts/check-release-publication.mjs --version "$RELEASE_VERSION" --github-output "$GITHUB_OUTPUT" | |
| - name: Confirm assets for an existing stable release | |
| id: existing_release | |
| if: ${{ github.event_name == 'push' && steps.release_state.outputs.state == 'published' }} | |
| env: | |
| RELEASE_VERSION: ${{ steps.source.outputs.version }} | |
| run: node scripts/wait-for-release-assets.mjs --version "$RELEASE_VERSION" --timeout-ms 15000 --interval-ms 3000 | |
| - name: Defer a release-version site update until publication | |
| if: ${{ github.event_name == 'push' && steps.release_state.outputs.state == 'not-published' }} | |
| run: echo "The referenced release is not public yet; the release workflow will deploy this site after publication." | |
| - name: Resolve immutable deployment source | |
| id: immutable_source | |
| if: ${{ steps.published_release.outcome == 'success' || steps.existing_release.outcome == 'success' }} | |
| shell: bash | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| GH_TOKEN: ${{ github.token }} | |
| TRUSTED_SHA: ${{ steps.source.outputs.trusted_sha }} | |
| REQUESTED_RELEASE_TAG: ${{ inputs.release_tag }} | |
| TRUSTED_VERSION: ${{ steps.source.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| expected_tag="v${TRUSTED_VERSION}" | |
| release_tag="$expected_tag" | |
| if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then | |
| if [[ "$REQUESTED_RELEASE_TAG" != "$expected_tag" ]]; then | |
| echo "release_tag must be the current release ${expected_tag}" >&2 | |
| exit 1 | |
| fi | |
| if [[ "$GITHUB_REF" != "refs/tags/${expected_tag}" ]]; then | |
| echo "workflow_dispatch must run from the immutable release tag ${expected_tag}" >&2 | |
| exit 1 | |
| fi | |
| release_tag="$REQUESTED_RELEASE_TAG" | |
| fi | |
| release_flags=$(gh api "repos/${GITHUB_REPOSITORY}/releases/tags/${release_tag}" --jq '[.draft, .prerelease] | @tsv') | |
| if [[ "$release_flags" != $'false\tfalse' ]]; then | |
| echo "release_tag must name a published, non-prerelease GitHub release" >&2 | |
| exit 1 | |
| fi | |
| git fetch --force origin "refs/tags/${release_tag}:refs/tags/${release_tag}" | |
| source_sha=$(git rev-parse "${release_tag}^{commit}") | |
| tag_version=$(git show "${source_sha}:package.json" | jq -er '.version') | |
| if [[ "$tag_version" != "$TRUSTED_VERSION" ]]; then | |
| echo "release tag package version ${tag_version} does not match trusted source ${TRUSTED_VERSION}" >&2 | |
| exit 1 | |
| fi | |
| if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then | |
| if [[ "$source_sha" != "$TRUSTED_SHA" ]]; then | |
| echo "release tag moved after this immutable deployment was dispatched" >&2 | |
| exit 1 | |
| fi | |
| elif ! git merge-base --is-ancestor "$source_sha" "$TRUSTED_SHA"; then | |
| echo "release tag source is not reachable from the trusted workflow source" >&2 | |
| exit 1 | |
| fi | |
| printf 'release_tag=%s\nsource_sha=%s\n' "$release_tag" "$source_sha" >> "$GITHUB_OUTPUT" | |
| - name: Check out immutable deployment source | |
| if: ${{ steps.published_release.outcome == 'success' || steps.existing_release.outcome == 'success' }} | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| ref: ${{ steps.immutable_source.outputs.source_sha }} | |
| persist-credentials: false | |
| - name: Install dependencies | |
| if: ${{ steps.published_release.outcome == 'success' || steps.existing_release.outcome == 'success' }} | |
| run: pnpm install --frozen-lockfile | |
| - name: Authenticate to AWS with GitHub OIDC | |
| if: ${{ steps.published_release.outcome == 'success' || steps.existing_release.outcome == 'success' }} | |
| uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4 | |
| with: | |
| role-to-assume: ${{ vars.AWS_ROLE_ARN }} | |
| aws-region: us-east-1 | |
| - name: Build and deploy static site | |
| if: ${{ steps.published_release.outcome == 'success' || steps.existing_release.outcome == 'success' }} | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| T4_SITE_BUCKET: ${{ vars.T4_SITE_BUCKET }} | |
| T4_CLOUDFRONT_DISTRIBUTION_ID: ${{ vars.T4_CLOUDFRONT_DISTRIBUTION_ID }} | |
| run: pnpm deploy:site |