chore(deps): bump the go-deps group across 1 directory with 19 updates #151
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: CI | |
| # CI runs automatically on pushes to main and on PRs targeting main. | |
| # The fast gates (build, lint, unit tests, compose/deploy config validation, | |
| # docs) run on every PR. The heavy/real-infra deploy smokes (helm-kind, | |
| # fly-staging, prod-compose) are OPT-IN: they only run on manual dispatch or on | |
| # a PR carrying the `deploy-smoke` label, so ordinary PRs never spin up clusters, | |
| # deploy to Fly, or require the full prod secret set. | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| run_helm_kind_smoke: | |
| description: "Run the full Helm kind deploy smoke test" | |
| required: false | |
| default: "true" | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| changes: | |
| name: Detect changes | |
| runs-on: ubuntu-latest | |
| outputs: | |
| go: ${{ steps.filter.outputs.go }} | |
| python: ${{ steps.filter.outputs.python }} | |
| typescript: ${{ steps.filter.outputs.typescript }} | |
| docs: ${{ steps.filter.outputs.docs }} | |
| docs_site: ${{ steps.filter.outputs.docs_site }} | |
| compose: ${{ steps.filter.outputs.compose }} | |
| deploy: ${{ steps.filter.outputs.deploy }} | |
| images: ${{ steps.filter.outputs.images }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: dorny/paths-filter@v4 | |
| id: filter | |
| with: | |
| filters: | | |
| go: | |
| - '**/*.go' | |
| - 'go.mod' | |
| - 'go.sum' | |
| - '.golangci.yml' | |
| python: | |
| - '**/*.py' | |
| - 'pyproject.toml' | |
| - 'packages/sdk-py/**' | |
| - 'apps/backend/**' | |
| typescript: | |
| - '**/*.ts' | |
| - '**/*.tsx' | |
| - 'package.json' | |
| - 'pnpm-workspace.yaml' | |
| - 'packages/sdk-ts/**' | |
| - 'apps/dashboard/**' | |
| - 'scripts/generate-brand.mjs' | |
| - 'brand.yaml' | |
| docs: | |
| - '**/*.md' | |
| - 'docs/**' | |
| docs_site: | |
| - 'docs-site/**' | |
| - 'docs/**' | |
| compose: | |
| - 'docker-compose*.yml' | |
| - 'scripts/postgres-init.sh' | |
| deploy: | |
| - '.github/workflows/ci.yml' | |
| - 'deploy/**' | |
| - 'docker-compose*.yml' | |
| - 'scripts/validate-deploy-targets.py' | |
| - 'scripts/test-helm-kind.sh' | |
| - 'scripts/test-fly-staging.sh' | |
| - 'scripts/test-prod-compose-smoke.sh' | |
| images: | |
| - 'apps/dashboard/Dockerfile' | |
| - 'apps/customer-app/Dockerfile' | |
| - 'scripts/generate-brand.mjs' | |
| - 'package.json' | |
| - 'pnpm-lock.yaml' | |
| - 'pnpm-workspace.yaml' | |
| lint-go: | |
| name: Lint (Go) | |
| needs: changes | |
| if: needs.changes.outputs.go == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| # only-new-issues needs the base ref to diff against. | |
| fetch-depth: 0 | |
| - uses: actions/setup-go@v5 | |
| with: | |
| # Match the toolchain go.mod requires (go 1.25.x). Pinning an older | |
| # version forced a GOTOOLCHAIN auto-download whose tool set was | |
| # missing `covdata`, breaking `go test -cover` on no-test packages. | |
| go-version-file: go.mod | |
| cache: true | |
| - name: go vet | |
| run: go vet ./... | |
| - name: go build | |
| run: go build ./... | |
| # golangci-lint v2 (S7): the config was migrated from the v1 schema so it | |
| # analyzes the go 1.25 toolchain cleanly again. It is restored as a | |
| # blocking gate in only-new-issues mode — the ~106-issue pre-existing | |
| # backlog is not gated, but any NEW issue a PR introduces fails the build, | |
| # so the debt burns down as files are touched. Run ENFORCE via | |
| # `golangci-lint run` locally to see the full backlog. | |
| - name: golangci-lint | |
| uses: golangci/golangci-lint-action@v7 | |
| with: | |
| # v2.5.0's release binary is built with go1.25.1; older v2 binaries | |
| # (e.g. v2.1.6, built with go1.24) refuse a config targeting go 1.25. | |
| version: v2.5.0 | |
| # Compute NEW issues from git, not the GitHub diff API. The action's | |
| # only-new-issues path fetches the PR patch from the API, which has a | |
| # hard 20k-line ceiling ("diff too large") — a large PR then trips it | |
| # and the action falls back to reporting the entire ~106-issue | |
| # backlog. `--new-from-merge-base` does the identical filtering via | |
| # git (fetch-depth: 0 above provides origin/main), with no ceiling: | |
| # only issues on lines this PR changed relative to the merge base | |
| # fail the build. Same gate, robust to PR size. | |
| only-new-issues: false | |
| args: --new-from-merge-base=origin/main | |
| test-go: | |
| name: Test (Go) | |
| needs: changes | |
| if: needs.changes.outputs.go == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-go@v5 | |
| with: | |
| # Match the toolchain go.mod requires (go 1.25.x). Pinning an older | |
| # version forced a GOTOOLCHAIN auto-download whose tool set was | |
| # missing `covdata`, breaking `go test -cover` on no-test packages. | |
| go-version-file: go.mod | |
| cache: true | |
| - run: go test ./... -race -cover | |
| lint-python: | |
| name: Lint (Python) | |
| needs: changes | |
| if: needs.changes.outputs.python == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: astral-sh/setup-uv@v7 | |
| - run: uv python install 3.11 | |
| # Pin ruff: its formatter output drifts across releases, so an unpinned | |
| # `ruff` would fail `format --check` whenever a new version ships. The | |
| # baseline is formatted with this exact version. | |
| - run: uv tool install ruff==0.15.20 | |
| - run: ruff check . | |
| - run: ruff format --check . | |
| test-python: | |
| name: Test (Python) | |
| needs: changes | |
| if: needs.changes.outputs.python == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: astral-sh/setup-uv@v7 | |
| - run: uv python install 3.11 | |
| - run: uv sync --all-packages | |
| continue-on-error: true # OK while packages are empty | |
| - run: | | |
| if find packages/sdk-py/tests apps/backend/tests -name 'test_*.py' 2>/dev/null | grep -q .; then | |
| uv run pytest -q | |
| else | |
| echo "No Python tests yet" | |
| fi | |
| lint-typescript: | |
| name: Lint (TypeScript) | |
| needs: changes | |
| if: needs.changes.outputs.typescript == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: pnpm/action-setup@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: "22" | |
| cache: "pnpm" | |
| - run: pnpm install --frozen-lockfile | |
| - name: Brand codegen (js-yaml ESM import) | |
| run: node scripts/generate-brand.mjs | |
| - run: pnpm -r lint || echo "no TS lint scripts yet" | |
| - run: pnpm exec prettier --check "**/*.{ts,tsx,js,jsx,json,md,yaml,yml}" || true | |
| test-typescript: | |
| name: Test (TypeScript) | |
| needs: changes | |
| if: needs.changes.outputs.typescript == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: pnpm/action-setup@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: "22" | |
| cache: "pnpm" | |
| - run: pnpm install --frozen-lockfile | |
| - run: pnpm -r test || echo "no TS tests yet" | |
| dco: | |
| name: DCO | |
| # PR-only: GitHub merge commits on main are not DCO-signed. The | |
| # required check is the PR range against origin/main. | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Verify Signed-off-by on PR commits | |
| env: | |
| BASE_REF: origin/${{ github.base_ref }} | |
| run: scripts/check-dco.sh "$BASE_REF" | |
| validate-compose: | |
| name: Validate docker-compose | |
| needs: changes | |
| if: needs.changes.outputs.compose == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Validate compose syntax | |
| run: | | |
| docker compose config --quiet | |
| - name: Validate prod compose | |
| run: | | |
| if [ -f docker-compose.prod.yml ]; then | |
| docker compose -f docker-compose.yml -f docker-compose.prod.yml config --quiet | |
| fi | |
| validate-deploy-targets: | |
| name: Validate deploy targets | |
| needs: changes | |
| if: needs.changes.outputs.deploy == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: azure/setup-helm@v4 | |
| - name: Validate Helm, Fly, Railway, Render, and prod compose | |
| run: scripts/validate-deploy-targets.py | |
| build-app-images: | |
| name: Build app images | |
| needs: changes | |
| # Catch release-image breakage (brand codegen, lockfile, Dockerfile) | |
| # before workflow_run/Release tries to push to GHCR. | |
| if: needs.changes.outputs.images == 'true' | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - name: dashboard | |
| dockerfile: apps/dashboard/Dockerfile | |
| - name: customer-app | |
| dockerfile: apps/customer-app/Dockerfile | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Build ${{ matrix.name }} (no push) | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: ${{ matrix.dockerfile }} | |
| push: false | |
| tags: af-stack-${{ matrix.name }}:ci | |
| cache-from: type=gha,scope=${{ matrix.name }} | |
| cache-to: type=gha,mode=max,scope=${{ matrix.name }} | |
| provenance: false | |
| helm-kind-smoke: | |
| name: Helm kind smoke | |
| needs: changes | |
| # Opt-in only: manual dispatch (with the input set) or a `deploy-smoke`-labeled PR. | |
| if: needs.changes.outputs.deploy == 'true' && ((github.event_name == 'workflow_dispatch' && github.event.inputs.run_helm_kind_smoke == 'true') || contains(github.event.pull_request.labels.*.name, 'deploy-smoke')) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: azure/setup-helm@v4 | |
| - uses: helm/kind-action@v1 | |
| with: | |
| install_only: true | |
| - name: Install kubectl | |
| uses: azure/setup-kubectl@v5 | |
| - name: Deploy chart to kind and probe health endpoints | |
| run: scripts/test-helm-kind.sh | |
| fly-staging-smoke: | |
| name: Fly staging smoke | |
| needs: changes | |
| # Opt-in only: deploys to REAL Fly infra (needs FLY_API_TOKEN), so never on | |
| # an ordinary PR. Manual dispatch or a `deploy-smoke`-labeled PR. | |
| if: needs.changes.outputs.deploy == 'true' && (github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, 'deploy-smoke')) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: superfly/flyctl-actions/setup-flyctl@master | |
| - name: Deploy Fly staging apps and probe health endpoints | |
| env: | |
| FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} | |
| AF_STACK_FLY_STAGING_RUNTIME_APP: ${{ vars.AF_STACK_FLY_STAGING_RUNTIME_APP }} | |
| AF_STACK_FLY_STAGING_DASHBOARD_APP: ${{ vars.AF_STACK_FLY_STAGING_DASHBOARD_APP }} | |
| AF_STACK_FLY_STAGING_REGION: ${{ vars.AF_STACK_FLY_STAGING_REGION }} | |
| AF_STACK_FLY_STAGING_RUNTIME_URL: ${{ vars.AF_STACK_FLY_STAGING_RUNTIME_URL }} | |
| AF_STACK_FLY_STAGING_DASHBOARD_URL: ${{ vars.AF_STACK_FLY_STAGING_DASHBOARD_URL }} | |
| run: scripts/test-fly-staging.sh | |
| prod-compose-smoke: | |
| name: Production compose smoke | |
| needs: changes | |
| # Opt-in only: needs the full prod secret set; absent secrets => guaranteed | |
| # red on ordinary PRs. Manual dispatch or a `deploy-smoke`-labeled PR. | |
| if: needs.changes.outputs.deploy == 'true' && (github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, 'deploy-smoke')) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Bring up production compose and probe services | |
| env: | |
| AF_STACK_PROD_COMPOSE_SMOKE: ${{ vars.AF_STACK_PROD_COMPOSE_SMOKE }} | |
| AF_STACK_DOMAIN: ${{ vars.AF_STACK_PROD_COMPOSE_DOMAIN }} | |
| ACME_EMAIL: ${{ vars.AF_STACK_PROD_COMPOSE_ACME_EMAIL }} | |
| AF_STACK_DATABASE_URL: ${{ secrets.AF_STACK_PROD_COMPOSE_DATABASE_URL }} | |
| AGENTFIELD_STORAGE_POSTGRES_URL: ${{ secrets.AGENTFIELD_STORAGE_POSTGRES_URL }} | |
| AF_STACK_KMS_KEY: ${{ secrets.AF_STACK_PROD_COMPOSE_KMS_KEY }} | |
| AF_STACK_AUTH_SECRET: ${{ secrets.AF_STACK_PROD_COMPOSE_AUTH_SECRET }} | |
| AF_STACK_S3_ENDPOINT: ${{ vars.AF_STACK_PROD_COMPOSE_S3_ENDPOINT }} | |
| AF_STACK_S3_BUCKET: ${{ vars.AF_STACK_PROD_COMPOSE_S3_BUCKET }} | |
| AF_STACK_S3_ACCESS_KEY: ${{ secrets.AF_STACK_PROD_COMPOSE_S3_ACCESS_KEY }} | |
| AF_STACK_S3_SECRET_KEY: ${{ secrets.AF_STACK_PROD_COMPOSE_S3_SECRET_KEY }} | |
| OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} | |
| E2B_API_KEY: ${{ secrets.E2B_API_KEY }} | |
| run: scripts/test-prod-compose-smoke.sh | |
| docs-lint: | |
| name: Lint docs | |
| needs: changes | |
| if: needs.changes.outputs.docs == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Check for broken internal links | |
| run: | | |
| # Simple check for obviously broken markdown links | |
| ! grep -rn '](\.\./\.\./\.\.' --include='*.md' . || (echo "Suspicious path traversal in docs" && exit 1) | |
| build-docs-site: | |
| name: Build docs-site (Astro Starlight) | |
| needs: changes | |
| if: needs.changes.outputs.docs_site == 'true' | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: docs-site | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| # Astro 5 requires Node >=22.12.0; the docs-site build fails on 20. | |
| node-version: "22" | |
| cache: "npm" | |
| cache-dependency-path: docs-site/package-lock.json | |
| - name: Install | |
| run: npm install | |
| - name: Build | |
| run: npm run build | |
| - name: Verify build output | |
| run: | | |
| # Build must produce a non-trivial static site. Threshold is loose | |
| # so adding/removing one or two pages doesn't fail CI, but a | |
| # broken collection that drops most pages does. | |
| pages=$(find dist -name '*.html' -type f | wc -l) | |
| echo "Built $pages HTML pages" | |
| if [ "$pages" -lt 15 ]; then | |
| echo "Expected at least 15 pages, got $pages" | |
| exit 1 | |
| fi | |
| ci-success: | |
| name: CI Success | |
| needs: | |
| [ | |
| lint-go, | |
| test-go, | |
| lint-python, | |
| test-python, | |
| lint-typescript, | |
| test-typescript, | |
| validate-compose, | |
| validate-deploy-targets, | |
| helm-kind-smoke, | |
| fly-staging-smoke, | |
| prod-compose-smoke, | |
| docs-lint, | |
| build-docs-site, | |
| dco, | |
| build-app-images, | |
| ] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check all jobs | |
| run: | | |
| # This job aggregates results so branch protection can require one check. | |
| # If any required job fails, this job fails. | |
| results='${{ toJSON(needs) }}' | |
| echo "Job results: $results" | |
| # Allow skipped (due to path filters) but not failed | |
| echo "$results" | jq -e 'all(.[]; .result == "success" or .result == "skipped")' |