Guided Walkthrough Part 2 — Quality Gates and Reporting

10 min read

With the test execution pipeline running, this lesson adds the quality layer: coverage gates, Allure history, the staging deploy workflow with post-deploy verification, Slack routing, and the README badges that make the pipeline's health instantly visible to anyone who opens the repository.

Step 7: JaCoCo coverage gate

Add JaCoCo to the backend pom.xml with both reporting and enforcement:

<!-- backend/pom.xml -->
<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.12</version>
    <executions>
        <execution>
            <id>prepare-agent</id>
            <goals><goal>prepare-agent</goal></goals>
        </execution>
        <execution>
            <id>report</id>
            <phase>test</phase>
            <goals><goal>report</goal></goals>
        </execution>
        <execution>
            <id>check</id>
            <goals><goal>check</goal></goals>
            <configuration>
                <rules>
                    <rule>
                        <element>BUNDLE</element>
                        <limits>
                            <limit>
                                <counter>LINE</counter>
                                <value>COVEREDRATIO</value>
                                <minimum>0.80</minimum>
                            </limit>
                        </limits>
                    </rule>
                </rules>
                <excludes>
                    <exclude>**/generated/**</exclude>
                    <exclude>**/dto/**</exclude>
                </excludes>
            </configuration>
        </execution>
    </executions>
</plugin>

Add a dedicated coverage job to pr-checks.yml:

  backend-coverage:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    defaults:
      run:
        working-directory: backend
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with: { java-version: '21', distribution: 'temurin', cache: 'maven' }
 
      - name: Run tests with coverage
        run: mvn test jacoco:check -B
        env:
          BASE_URL: ${{ secrets.STAGING_URL }}
 
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: jacoco-report
          path: backend/target/site/jacoco/
          retention-days: 14
 
      - uses: codecov/codecov-action@v4
        if: always()
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: backend/target/site/jacoco/jacoco.xml
          flags: backend

Running coverage as a separate job (not inside backend-smoke) keeps the smoke job fast. Coverage instrumentation adds 20–30% to test execution time — acceptable for a dedicated coverage job, not acceptable for the 5-minute smoke target.

Add PR Checks / backend-coverage to the required status checks in branch protection. Now a PR that drops coverage below 80% blocks merging.

For frontend coverage, add to playwright.config.ts:

reporter: [
  ['html', { open: 'never' }],
  ['junit', { outputFile: 'results.xml' }]
],

And add vitest --coverage as a separate check:

  frontend-coverage:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    defaults:
      run:
        working-directory: frontend
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20', cache: 'npm', cache-dependency-path: frontend/package-lock.json }
      - run: npm ci
      - run: npx vitest run --coverage
      - uses: codecov/codecov-action@v4
        if: always()
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: frontend/coverage/lcov.info
          flags: frontend

Step 8: Allure history on GitHub Pages

Add Allure output to the nightly regression workflow's backend jobs and publish to GitHub Pages:

  publish-allure:
    runs-on: ubuntu-latest
    needs: backend-regression
    if: always()
    steps:
      - uses: actions/checkout@v4
 
      - uses: actions/download-artifact@v4
        with:
          pattern: backend-shard-*
          path: collected-results
          merge-multiple: true
 
      - name: Generate Allure report with history
        uses: simple-elf/allure-report-action@master
        with:
          allure_results: collected-results
          allure_history: allure-history
          keep_reports: 30
 
      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v4
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: allure-history
          destination_dir: nightly

After the first successful nightly run, the Allure report is available at https://your-org.github.io/shopfast/nightly/. Each subsequent run updates the trend graph. With keep_reports: 30, the last 30 nightly runs' history is preserved — enough to identify tests that have been flaky across weeks, not just the latest run.

Step 9: Staging deploy workflow

# .github/workflows/deploy-staging.yml
name: Deploy to Staging
 
on:
  push:
    branches: [main]
 
jobs:
  deploy-backend:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    defaults:
      run:
        working-directory: backend
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with: { java-version: '21', distribution: 'temurin', cache: 'maven' }
 
      - name: Build artifact
        run: mvn package -DskipTests -B
 
      - name: Deploy to staging
        run: ./scripts/deploy.sh staging
        env:
          DEPLOY_TOKEN: ${{ secrets.STAGING_DEPLOY_TOKEN }}
          STAGING_HOST: ${{ secrets.STAGING_HOST }}
 
  deploy-frontend:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    defaults:
      run:
        working-directory: frontend
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20', cache: 'npm', cache-dependency-path: frontend/package-lock.json }
      - run: npm ci && npm run build
      - name: Deploy frontend
        run: ./scripts/deploy-frontend.sh staging
        env:
          DEPLOY_TOKEN: ${{ secrets.STAGING_DEPLOY_TOKEN }}
 
  post-deploy-smoke:
    runs-on: ubuntu-latest
    needs: [deploy-backend, deploy-frontend]
    timeout-minutes: 10
    defaults:
      run:
        working-directory: frontend
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20', cache: 'npm', cache-dependency-path: frontend/package-lock.json }
      - run: npm ci
      - uses: actions/cache@v4
        with:
          path: ~/.cache/ms-playwright
          key: playwright-${{ runner.os }}-${{ hashFiles('frontend/package-lock.json') }}
      - run: npx playwright install --with-deps chromium
      - name: Post-deploy smoke (5 tests)
        run: npx playwright test --grep @critical --workers=1
        env:
          BASE_URL: ${{ secrets.STAGING_URL }}
 
  notify-deploy:
    runs-on: ubuntu-latest
    needs: post-deploy-smoke
    if: always()
    steps:
      - uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {
              "text": "${{ needs.post-deploy-smoke.result == 'success' && '✅ Staging deploy succeeded' || '❌ Staging deploy FAILED — post-deploy smoke broken' }}: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}",
              "channel": "#releases"
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
          SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK

post-deploy-smoke runs after both deploy jobs complete successfully. The @critical tag marks the 5 tests that verify the most fundamental behaviours: can you load the homepage, can you log in, can you add an item to the cart? If any of these fail on staging immediately after a deploy, something is seriously wrong.

--workers=1 in the post-deploy smoke is deliberate: these tests run against a live staging environment, and parallel execution with shared state (the same test user account) would cause race conditions. Serial execution for 5 tests adds only seconds.

Step 10: README badges

Add to README.md at the repository root:

## Pipeline Health
 
![PR Checks](https://github.com/shopfast/app/actions/workflows/pr-checks.yml/badge.svg)
![Nightly Regression](https://github.com/shopfast/app/actions/workflows/nightly-regression.yml/badge.svg)
![Staging Deploy](https://github.com/shopfast/app/actions/workflows/deploy-staging.yml/badge.svg)
[![Coverage](https://codecov.io/gh/shopfast/app/branch/main/graph/badge.svg)](https://codecov.io/gh/shopfast/app)

Four badges: PR checks (green on every working PR), nightly (green every morning if regression is healthy), staging (green after every successful deploy), coverage (shows current percentage). Anyone who opens the repository sees the pipeline's current state in three seconds.

Step 11: Pipeline documentation

Create docs/pipeline.md with five sections:

What runs when — a table mapping triggers to workflows to jobs. New developers shouldn't have to read YAML to understand when their PR will be tested.

How to add a new test — for Selenium: add groups = {"smoke"} to the @Test annotation, confirm mvn test -Dgroups=smoke includes it locally. For Playwright: add test.use({ tag: '@smoke' }), confirm npx playwright test --grep @smoke runs it.

What to do when a gate fails — test failure: read the PR annotation, link to the artifact, reproduce locally. Coverage failure: check the JaCoCo report artifact, identify uncovered lines, add assertions. Nightly failure: check #qa-builds link, read the Allure report, determine if it's a regression or a flaky test.

Who owns what — test suite owner, Slack channels, deployment scripts, SonarQube access. Reduce "who do I ask about X" questions.

Secrets and configuration — which secrets are required, where they're stored (GitHub Settings → Secrets), who has access to rotate them. Not the secret values — just the names and their purpose.

Checklist before calling the capstone complete

  • PR workflow runs in under 6 minutes with warm cache
  • A deliberate test failure blocks the merge button
  • A deliberate coverage drop below threshold blocks the merge button
  • Nightly runs with sharding and the Allure report appears on GitHub Pages
  • A staging deploy + post-deploy smoke runs on every merge to main
  • Slack notifications arrive for nightly failures and staging deploys
  • README shows four green badges
  • docs/pipeline.md exists and is accurate

If any item is unchecked, go back and address it. A partial pipeline is worse than no pipeline — it gives false confidence while providing incomplete coverage.

// tip to track lessons you complete and pick up where you left off across devices.