diff --git a/.github/workflows/CD_production.yml b/.github/workflows/CD_production.yml index d6c1524c..e135876e 100644 --- a/.github/workflows/CD_production.yml +++ b/.github/workflows/CD_production.yml @@ -1,6 +1,19 @@ name: CD (Production) on: + # Reusable entry point: release-please calls this inline after it creates a + # release. Releases that release-please makes with the default GITHUB_TOKEN do + # NOT emit a `release: published` event that triggers other workflows + # (GitHub's recursive-trigger block), so the release-please workflow invokes + # this one directly via workflow_call. + workflow_call: + inputs: + tag_name: + description: Release tag to deploy (e.g. v1.1.0) + required: true + type: string + # Fallback for releases published manually (UI) or via a PAT, which DO emit a + # release event. release: types: [published] @@ -11,19 +24,21 @@ jobs: production-deploy: # Safety rail: only deploy when the release tag is version-shaped # (v*.*.*, v*.*.*-*, v*.*.*[a-z]*). startsWith() is a cheap pre-filter; - # the "Validate release tag" step enforces the strict regex. - if: startsWith(github.event.release.tag_name, 'v') + # the "Validate release tag" step enforces the strict regex. The tag comes + # from the workflow_call input or, for the release event, the payload. + if: ${{ startsWith((github.event_name == 'workflow_call' && inputs.tag_name) || github.event.release.tag_name, 'v') }} runs-on: ubuntu-latest environment: production + env: + DEPLOY_TAG: ${{ (github.event_name == 'workflow_call' && inputs.tag_name) || github.event.release.tag_name }} + steps: - name: Validate release tag matches version pattern - env: - TAG: ${{ github.event.release.tag_name }} run: | - if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-.+|[a-z].*)?$ ]]; then - echo "Release tag '$TAG' does not match the v*.*.* pattern. Refusing to deploy." + if [[ ! "$DEPLOY_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-.+|[a-z].*)?$ ]]; then + echo "Release tag '$DEPLOY_TAG' does not match the v*.*.* pattern. Refusing to deploy." exit 1 fi @@ -33,7 +48,7 @@ jobs: fetch-depth: 0 # Fully-qualified tag ref avoids ambiguity if a branch is ever # created with the same name as the release tag. - ref: refs/tags/${{ github.event.release.tag_name }} + ref: refs/tags/${{ env.DEPLOY_TAG }} - name: Install uv in container uses: astral-sh/setup-uv@v8.2.0 @@ -82,7 +97,7 @@ jobs: - name: Render App Engine configs env: - APP_VERSION: ${{ github.event.release.tag_name }} + APP_VERSION: ${{ env.DEPLOY_TAG }} ENVIRONMENT: "production" CLOUD_SQL_INSTANCE_NAME: "${{ secrets.CLOUD_SQL_INSTANCE_NAME }}" CLOUD_SQL_DATABASE: "${{ vars.CLOUD_SQL_DATABASE }}" diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index ea86f71d..f6cd5bb7 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -13,9 +13,27 @@ permissions: jobs: release-please: runs-on: ubuntu-latest + outputs: + release_created: ${{ steps.release.outputs.release_created }} + tag_name: ${{ steps.release.outputs.tag_name }} steps: - - uses: googleapis/release-please-action@v5 + - id: release + uses: googleapis/release-please-action@v5 with: config-file: release-please-config.json manifest-file: .release-please-manifest.json target-branch: ${{ github.ref_name }} + + # When release-please actually cuts a release, deploy it. The release is + # created with GITHUB_TOKEN, whose events don't trigger other workflows, so we + # invoke the production deploy inline (same run) instead of relying on the + # `release: published` event reaching CD_production. + deploy-production: + needs: release-please + if: ${{ needs.release-please.outputs.release_created == 'true' }} + permissions: + contents: read + uses: ./.github/workflows/CD_production.yml + with: + tag_name: ${{ needs.release-please.outputs.tag_name }} + secrets: inherit