Solutions — GitHub Actions Pipeline
Lesson 04 · Cloud Infrastructure & DevOps
Solution to Task 1 — Code-coverage gate
Replace the two separate pytest steps in the test job with a single step that collects coverage across both services:
- name: Run tests with coverage
run: |
pip install pytest-cov
pytest cloud-devops/sample-app/api/tests cloud-devops/sample-app/worker/tests \
--cov=cloud-devops/sample-app/api \
--cov=cloud-devops/sample-app/worker \
--cov-report=term-missing \
--cov-fail-under=80
Why this works: pytest-cov instruments the code paths covered by the test run. The --cov-fail-under=80 flag causes pytest to exit with code 2 if combined coverage is below the threshold. A non-zero exit code makes the GitHub Actions step (and therefore the job) fail. The --cov-report=term-missing flag prints which lines are not covered, making it easy to see what to test next.
Common mistake
Running --cov-fail-under per matrix variant means each Python version checks coverage independently. This is fine as a gate (the lowest version must pass), but if you want an aggregate combined report you need to collect .coverage files across jobs with coverage combine — a more advanced pattern not needed here.
Solution to Task 2 — Path-filter for doc-only edits
Update the on: block in ci.yml:
on:
push:
branches: [main]
paths-ignore:
- '**.md'
- 'docs/**'
pull_request:
branches: [main]
paths-ignore:
- '**.md'
- 'docs/**'
Why this works: paths-ignore is a deny-list. GitHub skips creating a workflow run if every changed file in the push matches an entry in the paths-ignore list. A commit that changes both a .py file and a .md file still triggers CI, because not all changed files are ignored.
Common mistake
Using paths: instead of paths-ignore:. These are opposites. paths: is an allow-list: CI only runs if at least one changed file matches a listed pattern. Using paths: ['**.py'] would silently skip CI for commits that change only YAML, Docker, or HTML files. Always prefer paths-ignore for “skip on doc changes” rules.
Solution to Task 3 — Step summary
Add a final step to the build job (after the docker/build-push-action step):
- name: Write build summary
run: |
echo "### Built images" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- \`${{ matrix.service }}:ci-${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
Why this works: $GITHUB_STEP_SUMMARY is a file path injected by GitHub Actions. Anything you append to it renders as Markdown in the “Summary” section of the workflow run page. Each matrix variant writes its own line, so the final summary lists both api:ci-<sha> and worker:ci-<sha>.
Common mistake
Writing to $GITHUB_OUTPUT instead of $GITHUB_STEP_SUMMARY. $GITHUB_OUTPUT is for passing values between steps in the same job (step outputs); it does not appear in the run summary. $GITHUB_STEP_SUMMARY is for the human-visible run page. They look similar but serve different purposes.
Lesson 05 turns the green check into a required check via branch protection, adds Trivy security scans, and signs images with cosign.