Merge branch 'main' into copilot/fix-github-actions-job-failure #1580
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
| # ============================================================================= | ||
| # QAI CI Pipeline - Main continuous integration workflow | ||
| # ============================================================================= | ||
| # Purpose: Validates code quality, runs tests, and performs daily training | ||
| # Triggers: Daily scheduled runs and manual dispatch | ||
| # Jobs: | ||
| # - validate: Code validation, unit & integration tests | ||
| # - train: Daily scheduled training workflow (requires validate pass) | ||
| # - deploy: Model deployment after successful training | ||
| # ============================================================================= | ||
| name: QAI CI Pipeline | ||
| on: | ||
| schedule: | ||
| - cron: '0 2 * * *' # Daily at 2 AM UTC | ||
| workflow_dispatch: {} | ||
| permissions: | ||
| contents: read | ||
| actions: read | ||
| checks: write | ||
| concurrency: | ||
| group: ci-pipeline-${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: ${{ github.event_name != 'workflow_dispatch' }} | ||
| env: | ||
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" | ||
| HARDEN_RUNNER_SHA: 0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.2 | ||
| CHECKOUT_SHA: 11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||
| ACTIONS_CACHE_SHA: 1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 | ||
| UPLOAD_ARTIFACT_SHA: 65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 | ||
| PYTHON_UNBUFFERED: "1" | ||
| PYTHONDONTWRITEBYTECODE: "1" | ||
| PIP_DISABLE_PIP_VERSION_CHECK: "1" | ||
| defaults: | ||
| run: | ||
| shell: bash | ||
| jobs: | ||
| validate: | ||
| name: Validate (lint, tests, integration smoke) | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 30 | ||
| strategy: | ||
| matrix: | ||
| python-version: ['3.11'] | ||
| steps: | ||
| - name: Harden runner | ||
| uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 | ||
| with: | ||
| egress-policy: audit | ||
| disable-sudo: true | ||
| # cspell:words pyproject PYTHONDONTWRITEBYTECODE | ||
| concurrency: | ||
| cancel-in-progress: ${{ github.event_name != 'workflow_dispatch' }} | ||
| group: ci-pipeline-${{ github.workflow }}-${{ github.ref }} | ||
| defaults: | ||
| run: | ||
| shell: bash | ||
| env: | ||
| ACTIONS_CACHE_SHA: 1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 | ||
| CHECKOUT_SHA: 11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" | ||
| HARDEN_RUNNER_SHA: 0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.2 | ||
| PIP_DISABLE_PIP_VERSION_CHECK: "1" | ||
| PYTHONDONTWRITEBYTECODE: "1" | ||
| PYTHON_UNBUFFERED: "1" | ||
| UPLOAD_ARTIFACT_SHA: 65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 | ||
| jobs: | ||
| deploy: | ||
| if: github.event_name == 'schedule' | ||
| name: Deploy (scheduled) | ||
| needs: train | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Harden runner | ||
| uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 | ||
| with: | ||
| disable-sudo: true | ||
| egress-policy: audit | ||
| - name: Checkout | ||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||
| with: | ||
| fetch-depth: 0 | ||
| persist-credentials: false | ||
| - name: Cache pip | ||
| uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 | ||
| with: | ||
| key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt', 'pyproject.toml') }} | ||
| path: | | ||
| ~/.cache/pip | ||
| ~/.cache/pip-wheel | ||
| - name: Setup Python | ||
| uses: ./.github/actions/setup-python-env | ||
| with: | ||
| python-version: '3.11' | ||
| - name: Deploy best model | ||
| run: | | ||
| set -euo pipefail | ||
| python scripts/model_deployer.py --deploy best --strategy canary | ||
| - name: Upload deployment manifest | ||
| uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 | ||
| with: | ||
| name: deployment-manifest | ||
| path: deployed_models/model_registry.json | ||
| retention-days: 90 | ||
| timeout-minutes: 30 | ||
| train: | ||
| if: github.event_name == 'schedule' | ||
| name: Train (scheduled) | ||
| needs: validate | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Harden runner | ||
| uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 | ||
| with: | ||
| disable-sudo: false | ||
| egress-policy: audit | ||
| - name: Checkout | ||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||
| with: | ||
| fetch-depth: 0 | ||
| persist-credentials: false | ||
| - name: Cache pip | ||
| uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 | ||
| with: | ||
| key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt', 'pyproject.toml') }} | ||
| path: | | ||
| ~/.cache/pip | ||
| ~/.cache/pip-wheel | ||
| - name: Setup Python | ||
| uses: ./.github/actions/setup-python-env | ||
| with: | ||
| python-version: '3.11' | ||
| - name: Run training workflow | ||
| run: | | ||
| set -euo pipefail | ||
| python scripts/master_orchestrator.py --workflow daily_full_pipeline | ||
| - if: always() | ||
| name: Generate training summary | ||
| run: | | ||
| set -euo pipefail | ||
| { | ||
| echo "## Training Results" | ||
| } >> "$GITHUB_STEP_SUMMARY" | ||
| if [ -f data_out/master_orchestrator/status.json ]; then | ||
| { | ||
| echo '```json' | ||
| cat data_out/master_orchestrator/status.json | ||
| echo '' | ||
| echo '```' | ||
| } >> "$GITHUB_STEP_SUMMARY" | ||
| else | ||
| echo "⚠️ No master_orchestrator status file found" >> "$GITHUB_STEP_SUMMARY" | ||
| fi | ||
| if [ "${{ job.status }}" != "success" ]; then | ||
| { | ||
| echo "" | ||
| echo "### ❌ Training job failed — see logs above." | ||
| } >> "$GITHUB_STEP_SUMMARY" | ||
| fi | ||
| - if: always() | ||
| name: Upload training results | ||
| uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 | ||
| with: | ||
| name: training-results | ||
| path: data_out/ | ||
| retention-days: 30 | ||
| timeout-minutes: 90 | ||
| validate: | ||
| name: Validate (lint, tests, integration smoke) | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Harden runner | ||
| uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 | ||
| with: | ||
| disable-sudo: true | ||
| egress-policy: audit | ||
| - name: Checkout | ||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||
| with: | ||
| fetch-depth: 0 | ||
| persist-credentials: false | ||
| - name: Cache pip | ||
| uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 | ||
| with: | ||
| key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt', 'pyproject.toml') }} | ||
| path: | | ||
| ~/.cache/pip | ||
| ~/.cache/pip-wheel | ||
| - name: Setup Python | ||
| uses: ./.github/actions/setup-python-env | ||
| with: | ||
| extra-packages: 'pytest pytest-timeout pytest-cov' | ||
| python-version: ${{ matrix.python-version }} | ||
| - name: Run CI validation | ||
| run: | | ||
| set -euo pipefail | ||
| python scripts/ci_orchestrator.py --validate-all | ||
| - name: Run integration smoke checks | ||
| run: | | ||
| set -euo pipefail | ||
| python scripts/integration_smoke.py | ||
| - name: Run integration contract unit tests | ||
| run: | | ||
| set -euo pipefail | ||
| python scripts/ci_orchestrator.py --integration-contract-tests | ||
| - name: Run unit tests | ||
| run: | | ||
| set -euo pipefail | ||
| python scripts/test_runner.py --unit --coverage | ||
| - name: Run ai-project tests | ||
| run: | | ||
| set -euo pipefail | ||
| python scripts/test_runner.py --ai-projects | ||
| - name: Run integration tests | ||
| run: | | ||
| set -euo pipefail | ||
| python scripts/test_runner.py --integration | ||
| - if: always() | ||
| name: Generate summary | ||
| run: | | ||
| set -euo pipefail | ||
| { | ||
| echo "## CI Validation Results" | ||
| echo "" | ||
| } >> "$GITHUB_STEP_SUMMARY" | ||
| if [ -f data_out/ci_orchestrator/status.json ]; then | ||
| { | ||
| echo '```json' | ||
| cat data_out/ci_orchestrator/status.json | ||
| echo '' | ||
| echo '```' | ||
| } >> "$GITHUB_STEP_SUMMARY" | ||
| else | ||
| echo "⚠️ No ci_orchestrator status file found" >> "$GITHUB_STEP_SUMMARY" | ||
| fi | ||
| if [ "${{ job.status }}" != "success" ]; then | ||
| { | ||
| echo "" | ||
| echo "### ❌ Validation failed — see logs above for details." | ||
| } >> "$GITHUB_STEP_SUMMARY" | ||
| fi | ||
| - if: always() | ||
| name: Upload validation results | ||
| uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 | ||
| with: | ||
| name: validation-results | ||
| path: | | ||
| data_out/ci_orchestrator/ | ||
| data_out/integration_smoke/ | ||
| data_out/test_runner/ | ||
| retention-days: 14 | ||
| strategy: | ||
| matrix: | ||
| python-version: ['3.11'] | ||
| timeout-minutes: 30 | ||
| - name: Checkout | ||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||
| with: | ||
| fetch-depth: 0 | ||
| persist-credentials: false | ||
| - name: Cache pip | ||
| with: | ||
| path: | | ||
| contents: read | ||