# Companion example, not the workflow for aialvi.me.
# Requires the application commands, deployment adapters, and environment
# configuration described in the article. It does not deploy by itself.
name: SaaS delivery

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read

concurrency:
  group: saas-${{ github.ref }}
  cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
  verify:
    runs-on: ubuntu-24.04
    timeout-minutes: 25
    outputs:
      archive-sha256: ${{ steps.package.outputs.sha256 }}
      release-artifact-id: ${{ steps.upload.outputs.artifact-id }}
    services:
      postgres:
        image: postgres:18.3
        env:
          POSTGRES_DB: saas_test
          POSTGRES_USER: saas_test
          POSTGRES_PASSWORD: local_test_only
        ports:
          - 5432:5432
        options: >-
          --health-cmd "pg_isready -U saas_test -d saas_test"
          --health-interval 5s
          --health-timeout 5s
          --health-retries 10
    env:
      CI: 'true'
      DATABASE_URL: postgres://saas_test:local_test_only@localhost:5432/saas_test
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          persist-credentials: false
      - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
        with:
          node-version-file: .node-version
          cache: npm
          cache-dependency-path: package-lock.json
      - run: npm ci
      - run: npm run lint
      - run: npm run typecheck
      - run: npm run test:unit
      - run: npm run db:migrate:test
      - run: npm run test:integration
      - run: npm run build
      - run: npx --no-install playwright install --with-deps chromium
      - run: npm run test:e2e
      - name: Package the verified build
        id: package
        if: github.event_name == 'push' && github.ref == 'refs/heads/main'
        run: |
          npm run release:pack -- --output release.tgz --revision "$GITHUB_SHA"
          test -s release.tgz
          checksum=$(sha256sum release.tgz | cut -d ' ' -f 1)
          echo "sha256=$checksum" >> "$GITHUB_OUTPUT"
      - name: Store release
        id: upload
        if: github.event_name == 'push' && github.ref == 'refs/heads/main'
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
        with:
          name: release-${{ github.sha }}-${{ github.run_attempt }}
          path: release.tgz
          if-no-files-found: error
          retention-days: 14
      - name: Store browser failure evidence
        if: failure()
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
        with:
          name: browser-failure-${{ github.run_id }}-${{ github.run_attempt }}
          path: |
            playwright-report/
            test-results/
          if-no-files-found: ignore
          retention-days: 7

  staging:
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    needs: verify
    runs-on: ubuntu-24.04
    timeout-minutes: 20
    environment: staging
    permissions:
      contents: read
      id-token: write
    env:
      RELEASE_SHA: ${{ github.sha }}
      EXPECTED_SHA256: ${{ needs.verify.outputs.archive-sha256 }}
      RELEASE_TARGET: ${{ vars.RELEASE_TARGET }}
      BASE_URL: ${{ vars.BASE_URL }}
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          persist-credentials: false
      - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
        with:
          node-version-file: .node-version
          package-manager-cache: false
      - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          artifact-ids: ${{ needs.verify.outputs.release-artifact-id }}
          merge-multiple: true
          path: release
      - name: Verify the release bytes
        run: printf '%s  %s\n' "$EXPECTED_SHA256" release/release.tgz | sha256sum --check --strict
      - run: node scripts/deploy.mjs --environment staging --archive release/release.tgz --revision "$RELEASE_SHA"
      - run: node scripts/smoke.mjs --base-url "$BASE_URL" --revision "$RELEASE_SHA"

  production:
    needs: [verify, staging]
    runs-on: ubuntu-24.04
    timeout-minutes: 20
    environment: production
    permissions:
      contents: read
      id-token: write
    env:
      RELEASE_SHA: ${{ github.sha }}
      EXPECTED_SHA256: ${{ needs.verify.outputs.archive-sha256 }}
      RELEASE_TARGET: ${{ vars.RELEASE_TARGET }}
      BASE_URL: ${{ vars.BASE_URL }}
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          persist-credentials: false
      - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
        with:
          node-version-file: .node-version
          package-manager-cache: false
      - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          artifact-ids: ${{ needs.verify.outputs.release-artifact-id }}
          merge-multiple: true
          path: release
      - name: Verify the release bytes
        run: printf '%s  %s\n' "$EXPECTED_SHA256" release/release.tgz | sha256sum --check --strict
      - run: node scripts/deploy.mjs --environment production --archive release/release.tgz --revision "$RELEASE_SHA"
      - run: node scripts/smoke.mjs --base-url "$BASE_URL" --revision "$RELEASE_SHA"
