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: backendRunning 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: frontendStep 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: nightlyAfter 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_WEBHOOKpost-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



[](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.mdexists 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.