diff --git a/.github/workflows/ci.test.mjs b/.github/workflows/ci.test.mjs index 0ba68aa3..ad14f38b 100644 --- a/.github/workflows/ci.test.mjs +++ b/.github/workflows/ci.test.mjs @@ -55,37 +55,46 @@ test('frontend CI job runs the frontend test command', async () => { ) }) -test('backend CI job runs go test and go vet via docker compose prebuilt image', async () => { +test('backend CI job runs go test and go vet natively from services/api', async () => { const workflow = await readFile(workflowPath, 'utf8') const backendJob = workflowJobBlock(workflow, 'backend') assert.match( backendJob, - /- name: Run tests\n\s+run: docker compose run --pull never --no-deps --rm app go test \.\/\.\.\./, + /uses: actions\/setup-go@v6\n\s+with:\n\s+go-version-file: services\/api\/go\.mod/, ) assert.match( backendJob, - /- name: Run vet\n\s+run: docker compose run --pull never --no-deps --rm app go vet \.\/\.\.\./, + /- name: Run tests\n\s+working-directory: services\/api\n\s+run: go test \.\/\.\.\./, ) + + assert.match( + backendJob, + /- name: Run vet\n\s+working-directory: services\/api\n\s+run: go vet \.\/\.\.\./, + ) + + assert.doesNotMatch(backendJob, /actions\/download-artifact|docker load|backend-image/) }) test('backend CI vet assertion does not match vet steps from later jobs', () => { const workflow = ` backend: steps: - name: Run tests - run: docker compose run --pull never --no-deps --rm app go test ./... + working-directory: services/api + run: go test ./... frontend: steps: - name: Run vet - run: docker compose run --pull never --no-deps --rm app go vet ./... + working-directory: services/api + run: go vet ./... ` const backendJob = workflowJobBlock(workflow, 'backend') assert.doesNotMatch( backendJob, - /- name: Run vet\n\s+run: docker compose run --pull never --no-deps --rm app go vet \.\/\.\.\./, + /- name: Run vet\n\s+working-directory: services\/api\n\s+run: go vet \.\/\.\.\./, ) }) @@ -216,6 +225,16 @@ test('docs/template-only PRs skip heavy product CI in scope gate', async () => { workflow, /Skipping heavy product CI because this PR only changes docs\/templates\/metadata\./, ) + assert.doesNotMatch( + workflow, + /name\.startsWith\('\.github\/workflows\/'\)/, + 'workflow changes must not be classified as metadata-only because they need CI validation', + ) + assert.match( + workflow, + /\^\\.github\\\/workflows\\\/ci\\.yml\$/, + 'ci.yml changes must request backend integration validation', + ) }) test('global auto-merge workflow excludes Dependabot PRs', async () => { diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c0426116..794b74e2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -237,46 +237,32 @@ jobs: with: context: ./services/api target: dev - load: true + load: false tags: tachigo-app:latest cache-from: type=gha cache-to: type=gha,mode=max - - name: Export image artifact - run: | - set -euo pipefail - docker save tachigo-app:latest | gzip -c > tachigo-app.tar.gz - test -s tachigo-app.tar.gz - - - name: Upload image artifact - uses: actions/upload-artifact@v4 - with: - name: backend-image - path: tachigo-app.tar.gz - retention-days: 1 - backend: timeout-minutes: 10 name: Backend tests - needs: [scope-gate, backend-build] + needs: [scope-gate] if: github.event_name == 'push' || needs.scope-gate.outputs.run_ci == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Download image artifact - uses: actions/download-artifact@v4 + - name: Set up Go + uses: actions/setup-go@v6 with: - name: backend-image - - - name: Load backend image - run: docker load < tachigo-app.tar.gz + go-version-file: services/api/go.mod - name: Run tests - run: docker compose run --pull never --no-deps --rm app go test ./... + working-directory: services/api + run: go test ./... - name: Run vet - run: docker compose run --pull never --no-deps --rm app go vet ./... + working-directory: services/api + run: go vet ./... backend-integration: timeout-minutes: 20 diff --git a/scripts/check-backend-ci-cache.sh b/scripts/check-backend-ci-cache.sh index eb7309fb..de82d45b 100644 --- a/scripts/check-backend-ci-cache.sh +++ b/scripts/check-backend-ci-cache.sh @@ -33,7 +33,15 @@ require_in "$workflow" "context: ./services/api" "backend CI should build from s require_in "$workflow" "target: dev" "backend CI should build the same dev target used by docker compose tests" require_in "$workflow" "cache-from: type=gha" "backend CI should restore Docker layers from GitHub Actions cache" require_in "$workflow" "cache-to: type=gha,mode=max" "backend CI should save Docker layers to GitHub Actions cache" -require_in "$workflow" "load: true" "backend CI should load the cached image into the local Docker daemon" +require_in "$workflow" "load: false" "backend build should validate Docker cache without loading the image" require_in "$workflow" "tags: tachigo-app:latest" "backend CI should tag the image used by docker compose" -require_in "$workflow" "docker compose run --pull never --no-deps --rm app go test ./..." "unit tests should reuse the prebuilt backend image" +require_in "$workflow" "actions/setup-go@v6" "backend unit tests should use native Go" +require_in "$workflow" "go-version-file: services/api/go.mod" "backend unit tests should read services/api/go.mod" +require_in "$workflow" "working-directory: services/api" "backend unit tests should run from services/api" +require_in "$workflow" "run: go test ./..." "backend unit tests should run natively" +require_in "$workflow" "run: go vet ./..." "backend vet should run natively" +reject_in "$workflow" "actions/download-artifact" "backend unit tests should not download a Docker image artifact" +reject_in "$workflow" "docker load" "backend unit tests should not load a Docker image artifact" +reject_in "$workflow" "backend-image" "backend image artifact roundtrip should be removed" reject_in "$workflow" "docker compose build app" "backend CI should not rebuild the app image without GHA layer cache" +reject_in "$workflow" "docker compose run --pull never --no-deps --rm app go test ./..." "unit tests should not use the prebuilt backend image"