docs(reply-by-email): document SparkPost reply-by-email and dev limit… #435
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
| # Build container images and push to AWS ECR. | |
| # | |
| # On every push to main/production/dev/staging, builds the nginx and app | |
| # images in parallel and tags them with the branch name plus a unique | |
| # build tag (vX.Y.Z-<sha7>). On main, also computes the next semantic | |
| # version with commitizen, tags the image with that version (and :latest), | |
| # and — only after both image pushes succeed — commits and pushes the | |
| # version bump back to main so a failed build never strands a bump. | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - production | |
| - dev | |
| - staging | |
| - test | |
| workflow_dispatch: | |
| name: AWS ECR Build & Push | |
| env: | |
| ECR_REGISTRY: ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com | |
| permissions: | |
| contents: write | |
| id-token: write | |
| jobs: | |
| prepare-version: | |
| name: Prepare version | |
| runs-on: ubuntu-24.04-arm | |
| # Skip only when main is being pushed by its own auto-generated bump | |
| # commit — without this guard the bump push would recurse into another | |
| # build/bump cycle. Bump: commits propagated to other branches | |
| # (dev/production/staging) by ff-merge ARE a release on that environment | |
| # and must still build, so the skip is scoped to main. workflow_dispatch | |
| # never has a head_commit, so this expression is also safe under manual | |
| # triggers. | |
| if: ${{ github.event_name != 'push' || !(github.ref_name == 'main' && startsWith(github.event.head_commit.message, 'bump:')) }} | |
| outputs: | |
| version: ${{ steps.compute.outputs.version }} | |
| version_tag: ${{ steps.compute.outputs.version_tag }} | |
| build_tag: ${{ steps.compute.outputs.build_tag }} | |
| branch: ${{ steps.compute.outputs.branch }} | |
| app_env: ${{ steps.compute.outputs.app_env }} | |
| bumped: ${{ steps.bump.outputs.bumped }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ secrets.GH_PAT }} | |
| - name: Set up Python | |
| if: github.ref_name == 'main' | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - name: Compute next version with commitizen (main only) | |
| id: bump | |
| if: github.ref_name == 'main' | |
| env: | |
| COMMITIZEN_VERSION: "4.16.2" | |
| run: | | |
| python3 -m pip install --user "commitizen==${COMMITIZEN_VERSION}" | |
| CURRENT_VERSION=$(cat VERSION | tr -d '[:space:]') | |
| set +e | |
| NEXT_VERSION=$("$HOME/.local/bin/cz" bump --get-next 2>/dev/null) | |
| EXIT_CODE=$? | |
| set -e | |
| if [ "$EXIT_CODE" = "0" ] && [ -n "$NEXT_VERSION" ] && [ "$NEXT_VERSION" != "$CURRENT_VERSION" ]; then | |
| echo "bumped=true" >> "$GITHUB_OUTPUT" | |
| echo "next_version=$NEXT_VERSION" >> "$GITHUB_OUTPUT" | |
| echo "Will bump $CURRENT_VERSION -> $NEXT_VERSION" | |
| else | |
| # Exit 21 = NoCommitsFoundError; exit 0 with same version means | |
| # nothing semantic to bump (e.g. only chore: / docs: commits). | |
| echo "bumped=false" >> "$GITHUB_OUTPUT" | |
| echo "next_version=$CURRENT_VERSION" >> "$GITHUB_OUTPUT" | |
| echo "No bump needed (exit=$EXIT_CODE next=$NEXT_VERSION current=$CURRENT_VERSION)" | |
| fi | |
| - name: Compute version + build tag | |
| id: compute | |
| run: | | |
| if [ "${{ steps.bump.outputs.bumped }}" = "true" ]; then | |
| VERSION="${{ steps.bump.outputs.next_version }}" | |
| else | |
| VERSION=$(cat VERSION | tr -d '[:space:]') | |
| fi | |
| SHORT_SHA="$(git rev-parse --short=7 HEAD)" | |
| # Branch-prefix the build_tag on non-main branches so each branch | |
| # gets a uniquely-named docker tag — otherwise dev / staging / | |
| # production all built at the same SHA would race for the same | |
| # v<version>-<sha> tag and only the last push would keep it. | |
| # Main stays as v<version>-<sha> because it's the canonical owner | |
| # of the release version line; the branch name is communicated | |
| # via the existing :main, :v<version>, and :latest tags. | |
| if [ "${GITHUB_REF_NAME}" = "main" ]; then | |
| BUILD_TAG="v${VERSION}-${SHORT_SHA}" | |
| else | |
| BUILD_TAG="${GITHUB_REF_NAME}-v${VERSION}-${SHORT_SHA}" | |
| fi | |
| # Map branch -> APP_ENV for the env-aware nginx template overlay. | |
| # Known deployment branches map to their canonical env name; any | |
| # other branch (including main, feature branches) is passed | |
| # through verbatim — the Dockerfile gracefully skips when no | |
| # matching config/<env>/nginx/templates dir exists. | |
| case "${GITHUB_REF_NAME}" in | |
| production|staging|dev|test) APP_ENV="${GITHUB_REF_NAME}" ;; | |
| *) APP_ENV="${GITHUB_REF_NAME}" ;; | |
| esac | |
| echo "version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "version_tag=v${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "build_tag=${BUILD_TAG}" >> "$GITHUB_OUTPUT" | |
| echo "branch=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT" | |
| echo "app_env=${APP_ENV}" >> "$GITHUB_OUTPUT" | |
| build-nginx: | |
| name: Build and Push Nginx | |
| runs-on: ubuntu-24.04-arm | |
| needs: prepare-version | |
| permissions: | |
| id-token: write | |
| contents: read | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| token: ${{ secrets.GH_PAT }} | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
| aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
| aws-region: ${{ secrets.AWS_REGION }} | |
| - name: Login to Amazon ECR | |
| id: login-ecr | |
| uses: aws-actions/amazon-ecr-login@v2 | |
| with: | |
| mask-password: true | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Compute image tags | |
| id: tags | |
| env: | |
| REPO: ${{ env.ECR_REGISTRY }}/commons-wordpress-nginx | |
| BRANCH: ${{ needs.prepare-version.outputs.branch }} | |
| VERSION_TAG: ${{ needs.prepare-version.outputs.version_tag }} | |
| BUILD_TAG: ${{ needs.prepare-version.outputs.build_tag }} | |
| run: | | |
| tags="${REPO}:${BRANCH},${REPO}:${BUILD_TAG}" | |
| if [ "$BRANCH" = "main" ]; then | |
| tags="${tags},${REPO}:${VERSION_TAG},${REPO}:latest" | |
| fi | |
| echo "tags=${tags}" >> "$GITHUB_OUTPUT" | |
| - name: Build and push nginx image | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: Dockerfile.nginx | |
| push: true | |
| # Disable provenance/SBOM attestations — they get pushed as | |
| # separate OCI artifacts that show up as untagged images in | |
| # ECR (see aws/containers-roadmap#1596). | |
| provenance: false | |
| sbom: false | |
| tags: ${{ steps.tags.outputs.tags }} | |
| build-args: | | |
| APP_VERSION=${{ needs.prepare-version.outputs.version }} | |
| BUILD_TAG=${{ needs.prepare-version.outputs.build_tag }} | |
| GIT_SHA=${{ github.sha }} | |
| APP_BRANCH=${{ needs.prepare-version.outputs.branch }} | |
| APP_ENV=${{ needs.prepare-version.outputs.app_env }} | |
| cache-from: type=registry,ref=${{ env.ECR_REGISTRY }}/commons-wordpress-nginx:cache | |
| cache-to: type=registry,ref=${{ env.ECR_REGISTRY }}/commons-wordpress-nginx:cache,mode=max | |
| - name: Label nginx cache manifest | |
| # Buildkit only applies :cache to the cache config manifest, | |
| # which ECR renders as an untagged "Other" artifact in the | |
| # console. Re-put the same manifest with two build-unique | |
| # identifying tags (do-not-deploy-<id>, build-cache-<id>). | |
| # Buildkit's :cache tag is preserved untouched so cache-from | |
| # continues to resolve. Cache manifests accumulate one entry | |
| # per build — prune via an ECR lifecycle policy on the | |
| # do-not-deploy-*/build-cache-* prefixes if storage growth | |
| # becomes an issue. | |
| env: | |
| REPO: commons-wordpress-nginx | |
| BUILD_TAG: ${{ needs.prepare-version.outputs.build_tag }} | |
| run: | | |
| set -euo pipefail | |
| MANIFEST=$(aws ecr batch-get-image \ | |
| --repository-name "$REPO" \ | |
| --image-ids imageTag=cache \ | |
| --query 'images[0].imageManifest' \ | |
| --output text) | |
| MEDIA_TYPE=$(aws ecr batch-get-image \ | |
| --repository-name "$REPO" \ | |
| --image-ids imageTag=cache \ | |
| --query 'images[0].imageManifestMediaType' \ | |
| --output text) | |
| for tag in "do-not-deploy-${BUILD_TAG}" "build-cache-${BUILD_TAG}"; do | |
| aws ecr put-image \ | |
| --repository-name "$REPO" \ | |
| --image-manifest "$MANIFEST" \ | |
| --image-manifest-media-type "$MEDIA_TYPE" \ | |
| --image-tag "$tag" \ | |
| >/dev/null 2>&1 \ | |
| && echo " + applied tag :$tag to nginx cache" \ | |
| || echo " · tag :$tag already on this manifest (no-op)" | |
| done | |
| build-app: | |
| name: Build and Push App | |
| runs-on: ubuntu-24.04-arm | |
| needs: prepare-version | |
| permissions: | |
| id-token: write | |
| contents: read | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| token: ${{ secrets.GH_PAT }} | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
| aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
| aws-region: ${{ secrets.AWS_REGION }} | |
| - name: Login to Amazon ECR | |
| id: login-ecr | |
| uses: aws-actions/amazon-ecr-login@v2 | |
| with: | |
| mask-password: true | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Compute image tags | |
| id: tags | |
| env: | |
| REPO: ${{ env.ECR_REGISTRY }}/commons-wordpress-app | |
| BRANCH: ${{ needs.prepare-version.outputs.branch }} | |
| VERSION_TAG: ${{ needs.prepare-version.outputs.version_tag }} | |
| BUILD_TAG: ${{ needs.prepare-version.outputs.build_tag }} | |
| run: | | |
| tags="${REPO}:${BRANCH},${REPO}:${BUILD_TAG}" | |
| if [ "$BRANCH" = "main" ]; then | |
| tags="${tags},${REPO}:${VERSION_TAG},${REPO}:latest" | |
| fi | |
| echo "tags=${tags}" >> "$GITHUB_OUTPUT" | |
| - name: Build and push app image | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: Dockerfile.php | |
| target: cloud | |
| push: true | |
| # Disable provenance/SBOM attestations — they get pushed as | |
| # separate OCI artifacts that show up as untagged images in | |
| # ECR (see aws/containers-roadmap#1596). | |
| provenance: false | |
| sbom: false | |
| tags: ${{ steps.tags.outputs.tags }} | |
| build-args: | | |
| BASE_IMAGE=${{ env.ECR_REGISTRY }}/commons-wordpress-base:latest | |
| APP_VERSION=${{ needs.prepare-version.outputs.version }} | |
| BUILD_TAG=${{ needs.prepare-version.outputs.build_tag }} | |
| GIT_SHA=${{ github.sha }} | |
| APP_BRANCH=${{ needs.prepare-version.outputs.branch }} | |
| cache-from: type=registry,ref=${{ env.ECR_REGISTRY }}/commons-wordpress-app:cache | |
| cache-to: type=registry,ref=${{ env.ECR_REGISTRY }}/commons-wordpress-app:cache,mode=max | |
| - name: Label app cache manifest | |
| # See "Label nginx cache manifest" for rationale. | |
| env: | |
| REPO: commons-wordpress-app | |
| BUILD_TAG: ${{ needs.prepare-version.outputs.build_tag }} | |
| run: | | |
| set -euo pipefail | |
| MANIFEST=$(aws ecr batch-get-image \ | |
| --repository-name "$REPO" \ | |
| --image-ids imageTag=cache \ | |
| --query 'images[0].imageManifest' \ | |
| --output text) | |
| MEDIA_TYPE=$(aws ecr batch-get-image \ | |
| --repository-name "$REPO" \ | |
| --image-ids imageTag=cache \ | |
| --query 'images[0].imageManifestMediaType' \ | |
| --output text) | |
| for tag in "do-not-deploy-${BUILD_TAG}" "build-cache-${BUILD_TAG}"; do | |
| aws ecr put-image \ | |
| --repository-name "$REPO" \ | |
| --image-manifest "$MANIFEST" \ | |
| --image-manifest-media-type "$MEDIA_TYPE" \ | |
| --image-tag "$tag" \ | |
| >/dev/null 2>&1 \ | |
| && echo " + applied tag :$tag to app cache" \ | |
| || echo " · tag :$tag already on this manifest (no-op)" | |
| done | |
| push-bump: | |
| name: Commit and push version bump | |
| runs-on: ubuntu-24.04-arm | |
| needs: [prepare-version, build-nginx, build-app] | |
| # Only fires when both image builds succeeded AND prepare-version | |
| # decided a bump was warranted — guarantees we never leave a stranded | |
| # bump on main from a build that didn't actually produce images. | |
| if: needs.prepare-version.outputs.bumped == 'true' | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| ref: main | |
| token: ${{ secrets.GH_PAT }} | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - name: Bump and push | |
| env: | |
| COMMITIZEN_VERSION: "4.16.2" | |
| run: | | |
| python3 -m pip install --user "commitizen==${COMMITIZEN_VERSION}" | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| "$HOME/.local/bin/cz" bump --yes | |
| git push origin main --tags |