From 0a94e6353a13ca1edc2a884532f79579b2e6dbb7 Mon Sep 17 00:00:00 2001 From: Ricardo Pinto Date: Wed, 9 Apr 2025 12:02:43 +0100 Subject: [PATCH] Added ephemeral staging environments closes https://linear.app/ghost/issue/AP-976 - Create ephemeral staging environment on PR labels *.ghost.is --- .github/workflows/build.yml | 523 +++++++++++++++++++++--------------- 1 file changed, 307 insertions(+), 216 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a629099b6..f70458b8b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,242 +12,333 @@ permissions: contents: read jobs: - lint: - name: Lint - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup Biome - uses: biomejs/setup-biome@v2 + # lint: + # name: Lint + # runs-on: ubuntu-latest + # steps: + # - name: Checkout + # uses: actions/checkout@v4 + # + # - name: Setup Biome + # uses: biomejs/setup-biome@v2 + # + # - name: Run Biome + # run: biome ci . + # + # check-yarn-lock: + # runs-on: ubuntu-latest + # steps: + # - name: Checkout repository + # uses: actions/checkout@v4 + # + # - name: Set up Node.js + # uses: actions/setup-node@v4 + # with: + # node-version: "lts/*" + # cache: "yarn" + # + # - name: Check yarn.lock + # run: yarn install --frozen-lockfile - - name: Run Biome - run: biome ci . - - check-yarn-lock: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 + # build-test-push: + # name: Build, Test and Push + # environment: staging + # runs-on: ubuntu-latest + # needs: [lint, check-yarn-lock] + # outputs: + # migrations_docker_version: ${{ steps.migrations-docker-metadata.outputs.version }} + # activitypub_docker_version: ${{ steps.activitypub-docker-metadata.outputs.version }} + # steps: + # - name: "Checkout" + # uses: actions/checkout@v4 + # + # - name: "ActivityPub Docker meta" + # id: activitypub-docker-metadata + # uses: docker/metadata-action@v5 + # with: + # images: | + # europe-docker.pkg.dev/ghost-activitypub/activitypub/activitypub + # tags: | + # ${{ github.ref == 'refs/heads/main' && 'type=edge,branch=main' || '' }} + # ${{ github.event_name == 'pull_request' && format('type=raw,value=pr-{0}', github.event.pull_request.number) || '' }} + # type=raw,value=${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} + # type=semver,pattern={{version}} + # type=semver,pattern={{major}}.{{minor}} + # type=semver,pattern={{major}} + # type=sha,priority=1100 + # + # - name: "Migrations Docker meta" + # id: migrations-docker-metadata + # uses: docker/metadata-action@v5 + # with: + # images: | + # europe-docker.pkg.dev/ghost-activitypub/activitypub/migrations + # tags: | + # ${{ github.ref == 'refs/heads/main' && 'type=edge,branch=main' || '' }} + # ${{ github.event_name == 'pull_request' && format('type=raw,value=pr-{0}', github.event.pull_request.number) || '' }} + # type=raw,value=${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} + # type=semver,pattern={{version}} + # type=semver,pattern={{major}}.{{minor}} + # type=semver,pattern={{major}} + # type=sha,priority=1100 + # + # - name: "Build Docker Image for ActivityPub" + # uses: docker/build-push-action@v6 + # with: + # context: . + # load: true + # tags: ${{ steps.activitypub-docker-metadata.outputs.tags }} + # + # - name: "Build Docker Image for Migrations" + # uses: docker/build-push-action@v6 + # with: + # context: migrate + # load: true + # tags: ${{ steps.migrations-docker-metadata.outputs.tags }} + # + # - name: "Run Tests" + # run: yarn test + # + # - name: "Authenticate with GCP" + # id: gcp-auth + # uses: google-github-actions/auth@v2 + # with: + # token_format: access_token + # workload_identity_provider: projects/687476608778/locations/global/workloadIdentityPools/github-oidc-activitypub/providers/github-provider-activitypub + # service_account: stg-activitypub-cicd@ghost-activitypub.iam.gserviceaccount.com + # + # - name: "Login to GCP Artifact Registry" + # uses: docker/login-action@v3 + # with: + # registry: europe-docker.pkg.dev + # username: oauth2accesstoken + # password: ${{ steps.gcp-auth.outputs.access_token }} + # + # - name: "Push ActivityPub Docker Image" + # uses: docker/build-push-action@v6 + # with: + # context: . + # push: true + # tags: ${{ steps.activitypub-docker-metadata.outputs.tags }} + # + # - name: "Push Migrations Docker Image" + # uses: docker/build-push-action@v6 + # with: + # context: migrate + # push: true + # tags: ${{ steps.migrations-docker-metadata.outputs.tags }} + # + # - uses: tryghost/actions/actions/slack-build@main + # if: failure() && github.event_name == 'push' && github.ref == 'refs/heads/main' + # with: + # status: ${{ job.status }} + # env: + # SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: "lts/*" - cache: "yarn" - - - name: Check yarn.lock - run: yarn install --frozen-lockfile - - build-test-push: - name: Build, Test and Push + deploy-pr: + if: github.event_name == 'pull_request' && (github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.action == 'reopened' || github.event.action == 'labeled') + name: (ephemeral staging) Deploy environment: staging runs-on: ubuntu-latest - needs: [lint, check-yarn-lock] - outputs: - migrations_docker_version: ${{ steps.migrations-docker-metadata.outputs.version }} - activitypub_docker_version: ${{ steps.activitypub-docker-metadata.outputs.version }} + #needs: [build-test-push] steps: - - name: "Checkout" - uses: actions/checkout@v4 + - name: "Check if any label matches *.ghost.is" + id: check-labels + env: + LABELS: ${{ toJson(github.event.pull_request.labels) }} + run: | + export LABEL_NAMES=$(echo "$LABELS" | jq -r '.[] | select(.name | test("\\.ghost\\.is$")) | .name') + echo "Label names: $LABEL_NAMES" + if [ $LABEL_NAMES != "" ]; then + echo "Label matching *.ghost.is found." + echo "is_ephemeral_staging=true" >> "$GITHUB_OUTPUT" + else + echo "No label matching .*.ghost.is found." + echo "is_ephemeral_staging=false" >> "$GITHUB_OUTPUT" + fi - - name: "ActivityPub Docker meta" - id: activitypub-docker-metadata - uses: docker/metadata-action@v5 - with: - images: | - europe-docker.pkg.dev/ghost-activitypub/activitypub/activitypub - tags: | - ${{ github.ref == 'refs/heads/main' && 'type=edge,branch=main' || '' }} - ${{ github.event_name == 'pull_request' && format('type=raw,value=pr-{0}', github.event.pull_request.number) || '' }} - type=raw,value=${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - type=sha,priority=1100 - - - name: "Migrations Docker meta" - id: migrations-docker-metadata - uses: docker/metadata-action@v5 + - name: "Checkout activitypub-infra repo" + if: ${{ steps.check-labels.outputs.is_ephemeral_staging == 'true' }} + uses: actions/checkout@v3 with: - images: | - europe-docker.pkg.dev/ghost-activitypub/activitypub/migrations - tags: | - ${{ github.ref == 'refs/heads/main' && 'type=edge,branch=main' || '' }} - ${{ github.event_name == 'pull_request' && format('type=raw,value=pr-{0}', github.event.pull_request.number) || '' }} - type=raw,value=${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - type=sha,priority=1100 - - - name: "Build Docker Image for ActivityPub" - uses: docker/build-push-action@v6 + repository: TryGhost/activitypub-infra + ssh-key: ${{ secrets.ACTIVITYPUB_INFRA_DEPLOY_KEY }} + path: activitypub-infra + + - name: "Checkout terraform repo" + if: ${{ steps.check-labels.outputs.is_ephemeral_staging == 'true' }} + uses: actions/checkout@v3 with: - context: . - load: true - tags: ${{ steps.activitypub-docker-metadata.outputs.tags }} + repository: TryGhost/terraform + ssh-key: ${{ secrets.TERRAFORM_DEPLOY_KEY }} + path: terraform + + - name: "Get terraform version" + if: ${{ steps.check-labels.outputs.is_ephemeral_staging == 'true' }} + id: terraform-version + run: | + echo "terraform_version=$(cat activitypub-infra/infrastructure/activitypub-staging-environments/.terraform-version)" >> "$GITHUB_OUTPUT" - - name: "Build Docker Image for Migrations" - uses: docker/build-push-action@v6 + - name: "Setup terraform" + if: ${{ steps.check-labels.outputs.is_ephemeral_staging == 'true' }} + uses: hashicorp/setup-terraform@v3 with: - context: migrate - load: true - tags: ${{ steps.migrations-docker-metadata.outputs.tags }} + terraform_version: ${{ steps.terraform-version.outputs.terraform_version }} - - name: "Run Tests" - run: yarn test + - name: "Change github.com url in modules to local directories and add backend prefix" + if: ${{ steps.check-labels.outputs.is_ephemeral_staging == 'true' }} + run: | + cd activitypub-infra/infrastructure/activitypub-staging-environments + sed -i 's/github\.com\/TryGhost/\.\.\/\.\.\/\.\./gI' main.tf + sed -i 's/\?ref=main//g' main.tf + sed -i 's/REPLACE_ME/${{ github.event.pull_request.number }}/g' terraform.tf - name: "Authenticate with GCP" - id: gcp-auth uses: google-github-actions/auth@v2 with: token_format: access_token workload_identity_provider: projects/687476608778/locations/global/workloadIdentityPools/github-oidc-activitypub/providers/github-provider-activitypub - service_account: stg-activitypub-github-cicd@ghost-activitypub.iam.gserviceaccount.com + service_account: stg-activitypub-cicd-stg-envs@ghost-activitypub.iam.gserviceaccount.com - - name: "Login to GCP Artifact Registry" - uses: docker/login-action@v3 - with: - registry: europe-docker.pkg.dev - username: oauth2accesstoken - password: ${{ steps.gcp-auth.outputs.access_token }} - - - name: "Push ActivityPub Docker Image" - uses: docker/build-push-action@v6 - with: - context: . - push: true - tags: ${{ steps.activitypub-docker-metadata.outputs.tags }} + - name: "Terraform init" + if: ${{ steps.check-labels.outputs.is_ephemeral_staging == 'true' }} + run: | + cd activitypub-infra/infrastructure/activitypub-staging-environments + terraform init - - name: "Push Migrations Docker Image" - uses: docker/build-push-action@v6 - with: - context: migrate - push: true - tags: ${{ steps.migrations-docker-metadata.outputs.tags }} + - name: "Terraform apply" + if: ${{ steps.check-labels.outputs.is_ephemeral_staging == 'true' }} + run: | + cd activitypub-infra/infrastructure/activitypub-staging-environments + export TF_VAR_github_pr_number=${{ github.event.pull_request.number }} + export TF_VAR_primary_region_name=netherlands + export TF_VAR_migrations_image=europe-docker.pkg.dev/ghost-activitypub/activitypub/migrations:edge + export TF_VAR_api_image=europe-docker.pkg.dev/ghost-activitypub/activitypub/activitypub:edge + export TF_VAR_queue_image=europe-docker.pkg.dev/ghost-activitypub/activitypub/activitypub:edge + terraform apply -auto-approve - - uses: tryghost/actions/actions/slack-build@main - if: failure() && github.event_name == 'push' && github.ref == 'refs/heads/main' - with: - status: ${{ job.status }} - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - deploy-staging: - if: github.ref == 'refs/heads/main' - name: (staging) Deploy - environment: staging - runs-on: ubuntu-latest - needs: [build-test-push] - strategy: - matrix: - region: [europe-west4, europe-west3] - include: - - region: europe-west4 - region_name: netherlands - - region: europe-west3 - region_name: frankfurt - steps: - - name: "Authenticate with GCP" - id: gcp-auth - uses: google-github-actions/auth@v2 - with: - token_format: access_token - workload_identity_provider: projects/687476608778/locations/global/workloadIdentityPools/github-oidc-activitypub/providers/github-provider-activitypub - service_account: stg-activitypub-github-cicd@ghost-activitypub.iam.gserviceaccount.com + # ${{ needs.build-test-push.outputs.migrations_docker_version }} - - name: "Deploy Migrations to Cloud Run" - if: ${{ matrix.region == 'europe-west4' }} - uses: google-github-actions/deploy-cloudrun@v2 - with: - image: europe-docker.pkg.dev/ghost-activitypub/activitypub/migrations:${{ needs.build-test-push.outputs.migrations_docker_version }} - region: ${{ matrix.region }} - job: stg-${{ matrix.region_name }}-activitypub-migrations - flags: --wait --execute-now - skip_default_labels: true - labels: |- - commit-sha=${{ github.sha }} - - - name: "Deploy ActivityPub Queue to Cloud Run" - uses: google-github-actions/deploy-cloudrun@v2 - with: - image: europe-docker.pkg.dev/ghost-activitypub/activitypub/activitypub:${{ needs.build-test-push.outputs.activitypub_docker_version }} - region: ${{ matrix.region }} - service: stg-${{ matrix.region_name }}-activitypub-queue - skip_default_labels: true - labels: |- - commit-sha=${{ github.sha }} - - - name: "Deploy ActivityPub API to Cloud Run" - uses: google-github-actions/deploy-cloudrun@v2 - with: - image: europe-docker.pkg.dev/ghost-activitypub/activitypub/activitypub:${{ needs.build-test-push.outputs.activitypub_docker_version }} - region: ${{ matrix.region }} - service: stg-${{ matrix.region_name }}-activitypub-api - skip_default_labels: true - labels: |- - commit-sha=${{ github.sha }} - - deploy-production: - if: github.ref == 'refs/heads/main' - name: (production) Deploy - environment: production - runs-on: ubuntu-latest - needs: [build-test-push, deploy-staging] - strategy: - matrix: - region: [europe-west4, europe-west3] - include: - - region: europe-west4 - region_name: netherlands - - region: europe-west3 - region_name: frankfurt - steps: - - name: "Authenticate with GCP" - id: gcp-auth - uses: google-github-actions/auth@v2 - with: - token_format: access_token - workload_identity_provider: projects/687476608778/locations/global/workloadIdentityPools/github-oidc-activitypub/providers/github-provider-activitypub - service_account: prd-activitypub-github-cicd@ghost-activitypub.iam.gserviceaccount.com + - name: "Update Load Balancer" + if: ${{ steps.check-labels.outputs.is_ephemeral_staging == 'true' }} + run: | + gcloud compute url-maps export stg-activitypub --global --project ghost-activitypub >> current-url-maps.yaml + cat current-url-maps.yaml - - name: "Deploy Migrations to Cloud Run" - if: ${{ matrix.region == 'europe-west4' }} - uses: google-github-actions/deploy-cloudrun@v2 - with: - image: europe-docker.pkg.dev/ghost-activitypub/activitypub/migrations:${{ needs.build-test-push.outputs.migrations_docker_version }} - region: ${{ matrix.region }} - job: prd-${{ matrix.region_name }}-activitypub-migrations - flags: --wait --execute-now - skip_default_labels: true - labels: |- - commit-sha=${{ github.sha }} - - - name: "Deploy ActivityPub Queue to Cloud Run" - uses: google-github-actions/deploy-cloudrun@v2 - with: - image: europe-docker.pkg.dev/ghost-activitypub/activitypub/activitypub:${{ needs.build-test-push.outputs.activitypub_docker_version }} - region: ${{ matrix.region }} - service: prd-${{ matrix.region_name }}-activitypub-queue - skip_default_labels: true - labels: |- - commit-sha=${{ github.sha }} - - - name: "Deploy ActivityPub API to Cloud Run" - uses: google-github-actions/deploy-cloudrun@v2 - with: - image: europe-docker.pkg.dev/ghost-activitypub/activitypub/activitypub:${{ needs.build-test-push.outputs.activitypub_docker_version }} - region: ${{ matrix.region }} - service: prd-${{ matrix.region_name }}-activitypub-api - skip_default_labels: true - labels: |- - commit-sha=${{ github.sha }} - - - uses: tryghost/actions/actions/slack-build@main - if: failure() && github.event_name == 'push' && github.ref == 'refs/heads/main' - with: - status: ${{ job.status }} - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + # deploy-staging: + # if: github.ref == 'refs/heads/main' + # name: (staging) Deploy + # environment: staging + # runs-on: ubuntu-latest + # needs: [build-test-push] + # strategy: + # matrix: + # region: [europe-west4, europe-west3] + # include: + # - region: europe-west4 + # region_name: netherlands + # - region: europe-west3 + # region_name: frankfurt + # steps: + # - name: "Authenticate with GCP" + # id: gcp-auth + # uses: google-github-actions/auth@v2 + # with: + # token_format: access_token + # workload_identity_provider: projects/687476608778/locations/global/workloadIdentityPools/github-oidc-activitypub/providers/github-provider-activitypub + # service_account: stg-activitypub-cicd@ghost-activitypub.iam.gserviceaccount.com + # + # - name: "Deploy Migrations to Cloud Run" + # if: ${{ matrix.region == 'europe-west4' }} + # uses: google-github-actions/deploy-cloudrun@v2 + # with: + # image: europe-docker.pkg.dev/ghost-activitypub/activitypub/migrations:${{ needs.build-test-push.outputs.migrations_docker_version }} + # region: ${{ matrix.region }} + # job: stg-${{ matrix.region_name }}-activitypub-migrations + # flags: --wait --execute-now + # skip_default_labels: true + # labels: |- + # commit-sha=${{ github.sha }} + # + # - name: "Deploy ActivityPub Queue to Cloud Run" + # uses: google-github-actions/deploy-cloudrun@v2 + # with: + # image: europe-docker.pkg.dev/ghost-activitypub/activitypub/activitypub:${{ needs.build-test-push.outputs.activitypub_docker_version }} + # region: ${{ matrix.region }} + # service: stg-${{ matrix.region_name }}-activitypub-queue + # skip_default_labels: true + # labels: |- + # commit-sha=${{ github.sha }} + # + # - name: "Deploy ActivityPub API to Cloud Run" + # uses: google-github-actions/deploy-cloudrun@v2 + # with: + # image: europe-docker.pkg.dev/ghost-activitypub/activitypub/activitypub:${{ needs.build-test-push.outputs.activitypub_docker_version }} + # region: ${{ matrix.region }} + # service: stg-${{ matrix.region_name }}-activitypub-api + # skip_default_labels: true + # labels: |- + # commit-sha=${{ github.sha }} + # + # deploy-production: + # if: github.ref == 'refs/heads/main' + # name: (production) Deploy + # environment: production + # runs-on: ubuntu-latest + # needs: [build-test-push, deploy-staging] + # strategy: + # matrix: + # region: [europe-west4, europe-west3] + # include: + # - region: europe-west4 + # region_name: netherlands + # - region: europe-west3 + # region_name: frankfurt + # steps: + # - name: "Authenticate with GCP" + # id: gcp-auth + # uses: google-github-actions/auth@v2 + # with: + # token_format: access_token + # workload_identity_provider: projects/687476608778/locations/global/workloadIdentityPools/github-oidc-activitypub/providers/github-provider-activitypub + # service_account: prd-activitypub-cicd@ghost-activitypub.iam.gserviceaccount.com + # + # - name: "Deploy Migrations to Cloud Run" + # if: ${{ matrix.region == 'europe-west4' }} + # uses: google-github-actions/deploy-cloudrun@v2 + # with: + # image: europe-docker.pkg.dev/ghost-activitypub/activitypub/migrations:${{ needs.build-test-push.outputs.migrations_docker_version }} + # region: ${{ matrix.region }} + # job: prd-${{ matrix.region_name }}-activitypub-migrations + # flags: --wait --execute-now + # skip_default_labels: true + # labels: |- + # commit-sha=${{ github.sha }} + # + # - name: "Deploy ActivityPub Queue to Cloud Run" + # uses: google-github-actions/deploy-cloudrun@v2 + # with: + # image: europe-docker.pkg.dev/ghost-activitypub/activitypub/activitypub:${{ needs.build-test-push.outputs.activitypub_docker_version }} + # region: ${{ matrix.region }} + # service: prd-${{ matrix.region_name }}-activitypub-queue + # skip_default_labels: true + # labels: |- + # commit-sha=${{ github.sha }} + # + # - name: "Deploy ActivityPub API to Cloud Run" + # uses: google-github-actions/deploy-cloudrun@v2 + # with: + # image: europe-docker.pkg.dev/ghost-activitypub/activitypub/activitypub:${{ needs.build-test-push.outputs.activitypub_docker_version }} + # region: ${{ matrix.region }} + # service: prd-${{ matrix.region_name }}-activitypub-api + # skip_default_labels: true + # labels: |- + # commit-sha=${{ github.sha }} + # + # - uses: tryghost/actions/actions/slack-build@main + # if: failure() && github.event_name == 'push' && github.ref == 'refs/heads/main' + # with: + # status: ${{ job.status }} + # env: + # SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}