From 9d2994a4aab34513fb727605d4dbd4f094830eea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 13:06:23 +0000 Subject: [PATCH] feat(ci): consolidate 6 workflow files into 3 optimized workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BREAKING CHANGE: Workflow files consolidated with proper dependencies - Create new unified CI pipeline (.github/workflows/ci.yml) - Multi-stage pipeline: test/lint/mypy → build → deploy - Add concurrency groups to cancel redundant PR runs - Fix bug: only push Docker images on push events (not PRs) - Fix bug: upgrade checkout action from v3 to v4 - Fix bug: enforce quality gates with job dependencies - Preserve Redis/RabbitMQ services for tests - Preserve Codecov integration and test artifacts - Push to both Docker Hub and GHCR with proper tags - Include SBOM and provenance attestations - Update release.yml: remove no-op "Trigger Docker Build" step - Update codeql.yml: remove template comments and boilerplate - Delete 4 redundant workflow files: - tests.yaml (merged into ci.yml) - docker-build.yaml (merged into ci.yml) - docker-ci.yml (merged into ci.yml) - deploy.yaml (merged into ci.yml) Docker registries: Docker Hub + GHCR (Quay.io auto-builds separately) Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> --- .github/workflows/ci.yml | 211 ++++++++++++++++++++++++++++ .github/workflows/codeql.yml | 48 ------- .github/workflows/deploy.yaml | 18 --- .github/workflows/docker-build.yaml | 80 ----------- .github/workflows/docker-ci.yml | 41 ------ .github/workflows/release.yml | 11 -- .github/workflows/tests.yaml | 117 --------------- 7 files changed, 211 insertions(+), 315 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/deploy.yaml delete mode 100644 .github/workflows/docker-build.yaml delete mode 100644 .github/workflows/docker-ci.yml delete mode 100644 .github/workflows/tests.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..8d8d347d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,211 @@ +name: CI Pipeline + +on: + push: + branches: + - main + - develop + tags: + - 'v*' + - '[0-9]+.*' + pull_request: + branches: + - main + +permissions: + contents: read + packages: write + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +env: + IMAGE_NAME: christianlouis/docuelevate + +jobs: + # ══════════════════════════════════════════════════════════════════════════ + # Stage 1: Quality Checks (run in parallel) + # ══════════════════════════════════════════════════════════════════════════ + + test: + name: Tests + runs-on: ubuntu-latest + services: + redis: + image: redis:7 + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + rabbitmq: + image: rabbitmq:3-management + ports: + - 5672:5672 + - 15672:15672 + options: >- + --health-cmd "rabbitmq-diagnostics -q ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install Dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements-dev.txt + + - name: Run Tests + run: pytest tests/ -v --cov=app --cov-report=xml --cov-report=term --junitxml=junit.xml -o junit_family=legacy -m "not e2e" + + - name: Upload coverage reports to Codecov + if: ${{ !cancelled() }} + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + file: ./coverage.xml + fail_ci_if_error: false + + - name: Upload test results to Codecov + if: ${{ !cancelled() }} + uses: codecov/test-results-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + + - name: Upload test artifacts + if: ${{ !cancelled() }} + uses: actions/upload-artifact@v4 + with: + name: test-results + path: | + junit.xml + coverage.xml + + lint: + name: Ruff Lint & Format + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install Ruff + run: pip install ruff + + - name: Run Ruff Check + run: ruff check app/ tests/ + + - name: Run Ruff Format + run: ruff format --check app/ tests/ + + mypy: + name: Mypy + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install Dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements-dev.txt + + - name: Run Mypy + run: mypy app/ + + # ══════════════════════════════════════════════════════════════════════════ + # Stage 2: Build & Push Docker Image (only after all Stage 1 jobs pass) + # ══════════════════════════════════════════════════════════════════════════ + + build: + name: Build & Push Docker Image + runs-on: ubuntu-latest + needs: [test, lint, mypy] + if: github.event_name == 'push' + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Generate Build Metadata + run: | + chmod +x scripts/generate_build_metadata.sh + ./scripts/generate_build_metadata.sh + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for tags + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ${{ env.IMAGE_NAME }} + ghcr.io/${{ github.repository_owner }}/docuelevate + tags: | + type=ref,event=branch + type=sha,prefix={{branch}}- + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and Push Docker Image + uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile + platforms: linux/amd64 + push: true + sbom: true + provenance: mode=max + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # ══════════════════════════════════════════════════════════════════════════ + # Stage 3: Deploy (only after build succeeds, only on main branch) + # ══════════════════════════════════════════════════════════════════════════ + + deploy: + name: Deploy to Production + runs-on: ubuntu-latest + needs: [build] + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + + steps: + - name: Call Deployment Webhook + run: | + curl -X POST https://docker2.kuechenserver.org/api/stacks/webhooks/960c7d8e-97ec-4175-a8dc-73f037b02349 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9e50027d..77a4caae 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,14 +1,3 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# name: "CodeQL Advanced" on: @@ -22,20 +11,10 @@ on: jobs: analyze: name: Analyze (${{ matrix.language }}) - # Runner size impacts CodeQL analysis time. To learn more, please see: - # - https://gh.io/recommended-hardware-resources-for-running-codeql - # - https://gh.io/supported-runners-and-hardware-resources - # - https://gh.io/using-larger-runners (GitHub.com only) - # Consider using larger runners or machines with greater resources for possible analysis time improvements. runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} permissions: - # required for all workflows security-events: write - - # required to fetch internal or private CodeQL packs packages: read - - # only required for workflows in private repositories actions: read contents: read @@ -49,43 +28,16 @@ jobs: build-mode: none - language: python build-mode: none - # CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift' - # Use `c-cpp` to analyze code written in C, C++ or both - # Use 'java-kotlin' to analyze code written in Java, Kotlin or both - # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both - # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, - # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. - # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how - # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages steps: - name: Checkout repository uses: actions/checkout@v4 - # Add any setup steps before running the `github/codeql-action/init` action. - # This includes steps like installing compilers or runtimes (`actions/setup-node` - # or others). This is typically only required for manual builds. - # - name: Setup runtime (example) - # uses: actions/setup-example@v1 - - # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - # If the analyze step fails for one of the languages you are analyzing with - # "We were unable to automatically build your code", modify the matrix above - # to set the build mode to "manual" for that language. Then modify this step - # to build your code. - # ℹ️ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - name: Run manual build steps if: matrix.build-mode == 'manual' shell: bash diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml deleted file mode 100644 index 933198dc..00000000 --- a/.github/workflows/deploy.yaml +++ /dev/null @@ -1,18 +0,0 @@ -name: Deploy to Production - -on: - workflow_run: - workflows: ["Build and Push Docker Image"] - types: - - completed - -permissions: - contents: read - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Call Deployment Webhook - run: | - curl -X POST https://docker2.kuechenserver.org/api/stacks/webhooks/960c7d8e-97ec-4175-a8dc-73f037b02349 diff --git a/.github/workflows/docker-build.yaml b/.github/workflows/docker-build.yaml deleted file mode 100644 index cd5dd6c9..00000000 --- a/.github/workflows/docker-build.yaml +++ /dev/null @@ -1,80 +0,0 @@ -name: Build and Push Docker Image - -permissions: - contents: read - packages: write - -on: - push: - branches: - - main - - develop - tags: - - 'v*' - - '[0-9]+.*' - pull_request: - branches: - - main - -env: - IMAGE_NAME: christianlouis/docuelevate - -jobs: - docker: - runs-on: ubuntu-latest - - steps: - - name: Checkout Code - uses: actions/checkout@v3 - - - name: Generate Build Metadata - run: | - chmod +x scripts/generate_build_metadata.sh - ./scripts/generate_build_metadata.sh - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract Git Tag (if applicable) - if: startsWith(github.ref, 'refs/tags/') - run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV - - - name: Extract metadata for tags - id: meta - uses: docker/metadata-action@v5 - with: - images: | - ${{ env.IMAGE_NAME }} - ghcr.io/${{ github.repository_owner }}/docuelevate - - - name: Build and Push Docker Image with Provenance and SBOM - uses: docker/build-push-action@v6 - with: - context: . - file: Dockerfile - platforms: linux/amd64 - push: true - sbom: true - provenance: mode=max - tags: | - ${{ env.IMAGE_NAME }}:latest - ${{ env.IMAGE_NAME }}:${{ github.sha }} - ghcr.io/${{ github.repository_owner }}/docuelevate:latest - ghcr.io/${{ github.repository_owner }}/docuelevate:${{ github.sha }} - ${{ startsWith(github.ref, 'refs/tags/') && format('{0}:{1}', env.IMAGE_NAME, env.VERSION) || '' }} - ${{ startsWith(github.ref, 'refs/tags/') && format('ghcr.io/{0}/docuelevate:{1}', github.repository_owner, env.VERSION) || '' }} - cache-from: type=gha - cache-to: type=gha,mode=max diff --git a/.github/workflows/docker-ci.yml b/.github/workflows/docker-ci.yml deleted file mode 100644 index 743f5a44..00000000 --- a/.github/workflows/docker-ci.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: ci - -on: - push: - branches: - - "main" - -jobs: - docker: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Generate Build Metadata - run: | - chmod +x scripts/generate_build_metadata.sh - ./scripts/generate_build_metadata.sh - - - name: Log in to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ vars.DOCKER_USER }} - password: ${{ secrets.DOCKER_PAT }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - with: - driver: cloud - endpoint: "christianlouis/cklbuilder" - install: true - - - name: Build and push - uses: docker/build-push-action@v6 - with: - context: . - tags: "${{ vars.DOCKER_USER }}/docuelevate:latest" - outputs: ${{ github.event_name == 'pull_request' && 'type=cacheonly' || 'type=registry' }} - provenance: mode=max - sbom: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1f7423c9..8b222b83 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -60,14 +60,3 @@ jobs: git commit -m "chore(release): update build metadata files [skip ci]" git push fi - - - name: Trigger Docker Build on Tag - if: startsWith(github.ref, 'refs/tags/') - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const ref = context.ref; - const tag = ref.replace('refs/tags/', ''); - console.log(`New tag created: ${tag}`); - console.log('Docker build will be triggered automatically by the docker-build workflow'); diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml deleted file mode 100644 index de7366b9..00000000 --- a/.github/workflows/tests.yaml +++ /dev/null @@ -1,117 +0,0 @@ -name: Run Tests & Linting - -on: [push, pull_request] - -permissions: - contents: read - -jobs: - # ── Tests ────────────────────────────────────────────────────────────── - test: - name: Tests - runs-on: ubuntu-latest - services: - redis: - image: redis:7 - ports: - - 6379:6379 - options: >- - --health-cmd "redis-cli ping" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - rabbitmq: - image: rabbitmq:3-management - ports: - - 5672:5672 - - 15672:15672 - options: >- - --health-cmd "rabbitmq-diagnostics -q ping" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install Dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements-dev.txt - - - name: Run Tests - # Exclude E2E tests (-m "not e2e") as they require Docker-in-Docker (testcontainers) - # which isn't well-supported in GitHub Actions without additional DinD configuration. - # E2E tests can be run locally with: pytest -m e2e - run: pytest tests/ -v --cov=app --cov-report=xml --cov-report=term --junitxml=junit.xml -o junit_family=legacy -m "not e2e" - - - name: Upload coverage reports to Codecov - if: ${{ !cancelled() }} - uses: codecov/codecov-action@v5 - with: - token: ${{ secrets.CODECOV_TOKEN }} - file: ./coverage.xml - fail_ci_if_error: false - - - name: Upload test results to Codecov - if: ${{ !cancelled() }} - uses: codecov/test-results-action@v1 - with: - token: ${{ secrets.CODECOV_TOKEN }} - - - name: Upload test artifacts - if: ${{ !cancelled() }} - uses: actions/upload-artifact@v4 - with: - name: test-results - path: | - junit.xml - coverage.xml - - # ── Lint (Ruff) ─────────────────────────────────────────────────────── - lint: - name: Ruff Lint & Format - runs-on: ubuntu-latest - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install Ruff - run: pip install ruff - - - name: Run Ruff Check - run: ruff check app/ tests/ - - - name: Run Ruff Format - run: ruff format --check app/ tests/ - - # ── Mypy ─────────────────────────────────────────────────────────────── - mypy: - name: Mypy - runs-on: ubuntu-latest - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install Dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements-dev.txt - - - name: Run Mypy - run: mypy app/