Skip to content

feat: CI/CD improvements with coverage, bandit, and safety#18

Merged
sadykovIsmail merged 1 commit intomainfrom
feat/cicd-improvements-issue-9
Mar 30, 2026
Merged

feat: CI/CD improvements with coverage, bandit, and safety#18
sadykovIsmail merged 1 commit intomainfrom
feat/cicd-improvements-issue-9

Conversation

@sadykovIsmail
Copy link
Copy Markdown
Owner

Summary

Splits the single CI job into three focused parallel jobs and adds security scanning + coverage reporting.

CI Jobs

1. Test & Coverage

  • Runs the full Django test suite with coverage
  • Fails if coverage drops below 80%
  • Uploads coverage.xml to Codecov

2. Lint (flake8)

  • max-line-length = 100
  • Excludes migrations, __pycache__, manage.py, settings.py
  • Ignores W503 (conflicts with Black style)

3. Security Scan

  • bandit: static analysis for common Python security issues (medium+ severity)
  • safety: audits all installed packages against known CVE database

Files Changed

File Change
.github/workflows/checks.yml Split into 3 parallel jobs
requirements.dev.txt Added coverage, bandit, safety
app/.coveragerc Coverage config with 80% threshold
app/.flake8 Updated max-line-length, ignore list

Test plan

  • Push to any branch → all 3 CI jobs run in parallel
  • Test with <80% coverage → build fails
  • Check Codecov dashboard for coverage report

Closes #9

…ety audit

- Split CI into three parallel jobs: test, lint, security
- Test job: runs coverage >= 80% threshold, uploads XML report to Codecov
- Lint job: flake8 with max-line-length=100, W503 ignored
- Security job: bandit SAST (medium+ severity) + safety dependency audit
- Add .coveragerc: omit migrations, manage.py, wsgi/asgi, test files
- Update .flake8: max-line-length=100, extend-ignore W503
- Update requirements.dev.txt: add coverage, bandit, safety
- Trigger on push to any branch and on PRs targeting main

Closes #9
Copilot AI review requested due to automatic review settings March 30, 2026 22:45
@sadykovIsmail sadykovIsmail merged commit 539115c into main Mar 30, 2026
4 of 8 checks passed
@sadykovIsmail sadykovIsmail deleted the feat/cicd-improvements-issue-9 branch March 30, 2026 22:45
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves the project’s GitHub Actions CI by splitting checks into parallel jobs and adding coverage + security tooling to better enforce quality gates for the Django codebase.

Changes:

  • Split the previous combined job into separate Test & Coverage, Lint (flake8), and Security Scan (bandit + safety) jobs.
  • Add dev/CI dependencies for coverage and security scanning (coverage, bandit, safety).
  • Introduce/adjust tool configuration via .coveragerc and .flake8.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
.github/workflows/checks.yml New CI workflow structure with parallel jobs, coverage generation/upload, and security scans.
requirements.dev.txt Adds CI/dev tools for coverage and security scanning.
app/.coveragerc Defines coverage source/omit rules and an 80% minimum threshold.
app/.flake8 Updates flake8 settings (line length, excludes, ignore list).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 16 to 20
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Docker Hub login step will fail on pull_request events from forks because secrets (DOCKERHUB_USER/DOCKERHUB_TOKEN) are not provided to forked PRs. Consider guarding this step with an if: that checks secrets are present / PR is from same repo, or remove the login entirely to avoid breaking CI for external contributors.

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +38
- name: Run tests with coverage
run: |
docker compose run --rm app sh -c "
coverage run --source=. --omit='*/migrations/*,manage.py,app/settings.py' \
manage.py test &&
coverage report --fail-under=80 &&
coverage xml -o /app/coverage.xml
"
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

coverage run writes its data file (and your coverage xml -o /app/coverage.xml output) into /app, which is a bind mount from the GitHub workspace. Since the container runs as django-user (non-root), it often won’t have write permission to the mounted workspace directory in CI, causing coverage generation to fail. Consider running the coverage commands with a writable COVERAGE_FILE/output dir (e.g., /tmp) and/or running the container with a UID that matches the runner (or as root for this step).

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +36
coverage run --source=. --omit='*/migrations/*,manage.py,app/settings.py' \
manage.py test &&
coverage report --fail-under=80 &&
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workflow passes --source/--omit/--fail-under on the command line even though a .coveragerc was added. Coverage CLI options override config, so the omit list in .coveragerc (e.g., excluding tests) won’t apply, and the workflow and config can easily drift. Prefer relying on .coveragerc (and keep a single source of truth) or update the config to match the CLI and remove the flags here.

Suggested change
coverage run --source=. --omit='*/migrations/*,manage.py,app/settings.py' \
manage.py test &&
coverage report --fail-under=80 &&
coverage run manage.py test &&
coverage report &&

Copilot uses AI. Check for mistakes.
run: |
docker compose run --rm app sh -c "
bandit -r . \
--exclude ./migrations,./tests \
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Bandit exclude paths don’t match this repo’s layout: there is no top-level ./migrations directory (migrations live under app modules like core/migrations). As written, Bandit will still scan migrations (and likely tests), which can add noise/false positives. Adjust --exclude to cover */migrations/* and */tests/* (or the specific app paths) based on the actual tree under /app.

Suggested change
--exclude ./migrations,./tests \
--exclude '*/migrations/*,*/tests/*' \

Copilot uses AI. Check for mistakes.
Comment on lines +40 to +44
- name: Copy coverage report from container
if: always()
run: |
docker compose run --rm -v ${{ github.workspace }}:/workspace app sh -c \
"cp /app/coverage.xml /workspace/coverage.xml" || true
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This step spawns an extra docker compose run just to copy a file, which can also re-trigger dependency containers (e.g., Postgres) and is sensitive to the same UID/permissions issue as the coverage generation. Since /app is already a bind mount, the coverage XML should already exist on the host after the previous step (likely under app/coverage.xml); consider copying it directly on the runner (or writing it to the workspace path in the first place) instead of starting a new container.

Suggested change
- name: Copy coverage report from container
if: always()
run: |
docker compose run --rm -v ${{ github.workspace }}:/workspace app sh -c \
"cp /app/coverage.xml /workspace/coverage.xml" || true

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: rate limiting and security hardening

2 participants