chore(actions)(deps): bump codecov/codecov-action from 5 to 6 in the github-actions group across 1 directory #149
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # CI/CD Pipeline — bankstatementprocessor monorepo | |
| # | |
| # Jobs (all lint jobs run in parallel): | |
| # changes — detect which paths changed (skips heavy jobs on workflow-only PRs) | |
| # lint-core — black, isort, ruff, mypy on packages/parser-core | |
| # lint-free — black, isort, ruff on packages/parser-free | |
| # security — bandit + pip-audit + xenon complexity gate | |
| # test-core — pytest with 91% coverage gate (Python matrix), needs lint-core | |
| # test-free — pytest on packages/parser-free, needs lint-free | |
| name: CI/CD Pipeline | |
| on: | |
| push: | |
| branches: [ main, develop ] | |
| paths-ignore: | |
| - '**.md' | |
| - 'docs/**' | |
| - 'LICENSE' | |
| - '.gitignore' | |
| - '*.txt' | |
| - '**.png' | |
| - '**.jpg' | |
| - '**.jpeg' | |
| - '**.gif' | |
| - '**.svg' | |
| pull_request: | |
| branches: [ main, develop ] | |
| paths-ignore: | |
| - '**.md' | |
| - 'docs/**' | |
| - 'LICENSE' | |
| - '.gitignore' | |
| - '**.png' | |
| - '**.jpg' | |
| - '**.jpeg' | |
| - '**.gif' | |
| - '**.svg' | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| security-events: write | |
| actions: read | |
| # --------------------------------------------------------------------------- | |
| # Jobs | |
| # --------------------------------------------------------------------------- | |
| jobs: | |
| # Detect which paths changed so downstream jobs can skip when irrelevant | |
| changes: | |
| name: Detect changed paths | |
| runs-on: ubuntu-latest | |
| permissions: | |
| pull-requests: read | |
| contents: read | |
| outputs: | |
| core: ${{ steps.filter.outputs.core }} | |
| free: ${{ steps.filter.outputs.free }} | |
| any-src: ${{ steps.filter.outputs.any-src }} | |
| docker: ${{ steps.filter.outputs.docker }} | |
| workflows: ${{ steps.filter.outputs.workflows }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: dorny/paths-filter@v4 | |
| id: filter | |
| with: | |
| filters: | | |
| core: | |
| - 'packages/parser-core/**' | |
| free: | |
| - 'packages/parser-free/**' | |
| any-src: | |
| - 'packages/**' | |
| docker: | |
| - 'Dockerfile' | |
| - 'entrypoint.sh' | |
| workflows: | |
| - '.github/workflows/**' | |
| - '.github/labeler*.yml' | |
| lint-core: | |
| name: Lint — parser-core | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| needs: changes | |
| if: needs.changes.outputs.core == 'true' | |
| defaults: | |
| run: | |
| working-directory: packages/parser-core | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.12' | |
| cache: pip | |
| cache-dependency-path: packages/parser-core/pyproject.toml | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update -qq | |
| sudo apt-get install -y --no-install-recommends poppler-utils | |
| - name: Install dev + test dependencies | |
| run: pip install --upgrade pip && pip install -e ".[dev,test]" | |
| - name: Black | |
| run: black --check --diff src tests | |
| - name: isort | |
| run: isort --check-only --diff src tests | |
| - name: Ruff | |
| run: ruff check src tests | |
| - name: MyPy | |
| run: mypy src --ignore-missing-imports | |
| - name: Validate type stubs | |
| run: | | |
| mypy --check-untyped-defs stubs/pdfplumber/ | |
| stubtest pdfplumber --allowlist stubtest-allowlist.txt || echo "stubtest differences found — review allowlist" | |
| - name: Validate version consistency | |
| run: | | |
| CODE_VER=$(grep '__version__ = ' src/bankstatements_core/__version__.py | cut -d'"' -f2) | |
| TOML_VER=$(grep '^version = ' pyproject.toml | head -1 | cut -d'"' -f2) | |
| echo "Code: ${CODE_VER} TOML: ${TOML_VER}" | |
| [ "${CODE_VER}" = "${TOML_VER}" ] || { echo "Version mismatch"; exit 1; } | |
| lint-free: | |
| name: Lint — parser-free | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| needs: changes | |
| if: needs.changes.outputs.free == 'true' | |
| defaults: | |
| run: | |
| working-directory: packages/parser-free | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.12' | |
| cache: pip | |
| cache-dependency-path: packages/parser-free/pyproject.toml | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update -qq | |
| sudo apt-get install -y --no-install-recommends poppler-utils | |
| - name: Install parser-core (editable) + parser-free + lint tools | |
| run: | | |
| pip install --upgrade pip | |
| pip install -e ../parser-core | |
| pip install -e ".[test]" | |
| pip install black isort ruff | |
| - name: Black | |
| run: black --check --diff src tests | |
| - name: isort | |
| run: isort --check-only --diff src tests | |
| - name: Ruff | |
| run: ruff check src tests | |
| security: | |
| name: Security — bandit + pip-audit | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| needs: changes | |
| if: needs.changes.outputs.any-src == 'true' | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.12' | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update -qq | |
| sudo apt-get install -y --no-install-recommends poppler-utils | |
| - name: Install security tools + packages | |
| run: | | |
| pip install --upgrade pip bandit[toml] pip-audit | |
| pip install -e packages/parser-core | |
| pip install -e packages/parser-free | |
| - name: Bandit — parser-core | |
| run: bandit -r packages/parser-core/src -f json -o bandit-core.json || true | |
| - name: Bandit — parser-free | |
| run: bandit -r packages/parser-free/src -f json -o bandit-free.json || true | |
| - name: pip-audit — dependency vulnerability scan | |
| run: pip-audit -r requirements/base.txt --progress-spinner off -f json -o pip-audit-report.json | |
| - name: Complexity gate (Xenon) | |
| run: | | |
| # table_detector.py: _detect_text_based_table grade E (score 32) — PDF heuristic hotspot, | |
| # pdfplumber word-coordinate coupling blocks safe decomposition without characterisation tests. | |
| # Temporary exclusion — remove once refactored. Tracked: https://github.com/longieirl/bankstatementprocessor/issues/98 | |
| # template_detector.py: get_detection_explanation grade D (score 22) — diagnostic method; | |
| # exhaustive path coverage of all scoring outcomes is inherent to its purpose. Stable exemption. | |
| # commands/init.py: init_directories grade D (score 26) — CLI entrypoint orchestration; | |
| # complexity is setup sequencing (mkdir + sample file creation), not extractable decision logic. Stable exemption. | |
| xenon --max-average A --max-modules B --max-absolute C \ | |
| --exclude "packages/parser-core/src/bankstatements_core/analysis/table_detector.py,packages/parser-core/src/bankstatements_core/templates/template_detector.py,packages/parser-core/src/bankstatements_core/commands/init.py" \ | |
| packages/parser-core/src | |
| - name: Upload security reports | |
| uses: actions/upload-artifact@v7 | |
| if: always() | |
| with: | |
| name: security-reports | |
| path: | | |
| bandit-core.json | |
| bandit-free.json | |
| pip-audit-report.json | |
| # --------------------------------------------------------------------------- | |
| # Test jobs (serial after their respective lint job) | |
| # --------------------------------------------------------------------------- | |
| test-core: | |
| name: Test parser-core (Python ${{ matrix.python-version }}) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| needs: lint-core | |
| defaults: | |
| run: | |
| working-directory: packages/parser-core | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| # PRs: fast feedback on 3.12 only; pushes to main: full matrix | |
| python-version: ${{ github.event_name == 'pull_request' && fromJSON('["3.12"]') || fromJSON('["3.11", "3.12", "3.13"]') }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: pip | |
| cache-dependency-path: packages/parser-core/pyproject.toml | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update -qq | |
| sudo apt-get install -y --no-install-recommends poppler-utils | |
| - name: Install package + test deps | |
| run: pip install --upgrade pip && pip install -e ".[dev,test]" | |
| - name: Run tests with coverage | |
| run: | | |
| python -m pytest tests/ -v \ | |
| --cov=bankstatements_core \ | |
| --cov-report=term-missing \ | |
| --cov-report=xml \ | |
| --cov-report=html \ | |
| --cov-fail-under=91 \ | |
| -n auto \ | |
| --tb=short | |
| - name: Upload test results | |
| uses: actions/upload-artifact@v7 | |
| if: always() | |
| with: | |
| name: test-results-core-${{ matrix.python-version }} | |
| path: | | |
| packages/parser-core/coverage.xml | |
| packages/parser-core/htmlcov/ | |
| - name: Upload coverage to Codecov | |
| if: matrix.python-version == '3.12' | |
| uses: codecov/codecov-action@v6 | |
| with: | |
| files: packages/parser-core/coverage.xml | |
| flags: parser-core | |
| fail_ci_if_error: false | |
| test-free: | |
| name: Test parser-free | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| needs: lint-free | |
| defaults: | |
| run: | |
| working-directory: packages/parser-free | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.12' | |
| cache: pip | |
| cache-dependency-path: packages/parser-free/pyproject.toml | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update -qq | |
| sudo apt-get install -y --no-install-recommends poppler-utils | |
| - name: Install parser-core (editable) + parser-free | |
| run: | | |
| pip install --upgrade pip | |
| pip install -e ../parser-core | |
| pip install -e ".[test]" | |
| - name: Run tests | |
| run: python -m pytest tests/ -v --tb=short | |
| # --------------------------------------------------------------------------- | |
| # Docker build validation — ensures Dockerfile builds successfully on PRs. | |
| # Runs on pull_request only (no push to avoid duplicate builds with release.yml). | |
| # --------------------------------------------------------------------------- | |
| build-docker: | |
| name: Build Docker image (PR validation) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| needs: changes | |
| if: needs.changes.outputs.docker == 'true' && github.event_name == 'pull_request' | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: docker/setup-buildx-action@v4 | |
| - name: Build image (no push) | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| target: production | |
| push: false | |
| load: true | |
| tags: bankstatementsprocessor:pr-${{ github.event.pull_request.number }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| - name: Smoke-test image | |
| run: | | |
| docker run --rm \ | |
| bankstatementsprocessor:pr-${{ github.event.pull_request.number }} \ | |
| python -c "import bankstatements_free; import bankstatements_core; print('imports OK')" | |
| # --------------------------------------------------------------------------- | |
| # Workflow lint — validates GitHub Actions YAML syntax on PRs. | |
| # --------------------------------------------------------------------------- | |
| workflow-lint: | |
| name: Lint — workflow files | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| needs: changes | |
| if: needs.changes.outputs.workflows == 'true' | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Run actionlint | |
| uses: rhysd/actionlint@v1.7.11 | |
| # --------------------------------------------------------------------------- | |
| # Downstream dispatch — notify private repo when parser-core changes on main | |
| # Fires only on push to main (not PRs), only when parser-core files changed, | |
| # and only after test-core passes. | |
| # | |
| # To skip the downstream trigger (e.g. formatting fixes, doc updates): | |
| # add [skip downstream] anywhere in the commit message. | |
| # | |
| # Security: | |
| # - Gated on push to main + test-core success — PRs never trigger this | |
| # - DOWNSTREAM_DISPATCH_TOKEN must be a fine-grained PAT scoped to | |
| # longieirl/bankstatements only, with Contents: write permission | |
| # - The private repo validates the run_url payload origin before running | |
| # --------------------------------------------------------------------------- | |
| dispatch-downstream: | |
| name: Dispatch downstream CI (bankstatements) | |
| runs-on: ubuntu-latest | |
| needs: test-core | |
| if: | | |
| github.event_name == 'push' && | |
| github.ref == 'refs/heads/main' && | |
| !contains(github.event.head_commit.message, '[skip downstream]') | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Check if parser-core changed | |
| id: changes | |
| uses: dorny/paths-filter@v4 | |
| with: | |
| filters: | | |
| core: | |
| - 'packages/parser-core/**' | |
| - name: Trigger downstream CI in bankstatements | |
| if: steps.changes.outputs.core == 'true' | |
| uses: peter-evans/repository-dispatch@v4 | |
| with: | |
| token: ${{ secrets.DOWNSTREAM_DISPATCH_TOKEN }} | |
| repository: longieirl/bankstatements | |
| event-type: core-updated | |
| client-payload: | | |
| { | |
| "sha": "${{ github.sha }}", | |
| "ref": "${{ github.ref }}", | |
| "run_url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" | |
| } | |
| # --------------------------------------------------------------------------- | |
| # Summary gate — required status check for branch protection | |
| # Always runs so docs-only PRs (which skip the workflow) are handled by | |
| # ci-docs.yml instead. When code jobs run, this aggregates their result. | |
| # --------------------------------------------------------------------------- | |
| ci-pass: | |
| name: CI Pass | |
| runs-on: ubuntu-latest | |
| if: always() | |
| needs: [changes, lint-core, lint-free, security, test-core, test-free, build-docker, workflow-lint] | |
| steps: | |
| - name: Check all jobs passed | |
| run: | | |
| results="${{ join(needs.*.result, ' ') }}" | |
| for r in $results; do | |
| if [ "$r" != "success" ] && [ "$r" != "skipped" ]; then | |
| echo "Job failed with result: $r" | |
| exit 1 | |
| fi | |
| done | |
| echo "All jobs passed or skipped." |