Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions .github/instructions/cicd.instructions.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
---
applyTo: ".github/workflows/build-release.yml"
applyTo: ".github/workflows/**"
description: "CI/CD Pipeline configuration for PyInstaller binary packaging and release workflow"
---

# CI/CD Pipeline Instructions

## Workflow Architecture (Fork-safe)
Three workflows split by trigger and secret requirements:

1. **`ci.yml`** — `pull_request` trigger (all PRs, including forks)
- Unit tests + build. No secrets needed. Gives fast feedback.
- Uploads binary artifacts for downstream integration testing.
2. **`ci-integration.yml`** — `workflow_run` trigger (after CI completes, environment-gated)
- Smoke tests, integration tests, release validation. Requires `integration-tests` environment approval.
- Security: uses `workflow_run` (not `pull_request_target`) — PR code is NEVER checked out.
- Downloads binary artifacts from ci.yml, runs test scripts from default branch (main).
- Reports results back to PR via commit status API.
3. **`build-release.yml`** — `push` to main, tags, schedule, `workflow_dispatch`
- Full pipeline for post-merge / release. Secrets always available.

## PyInstaller Binary Packaging
- **CRITICAL**: Uses `--onedir` mode (NOT `--onefile`) for faster CLI startup performance
- **Binary Structure**: Creates `dist/{binary_name}/apm` (nested directory containing executable + dependencies)
Expand All @@ -22,9 +36,18 @@ description: "CI/CD Pipeline configuration for PyInstaller binary packaging and
3. **Path Resolution**: Use symlinks and PATH manipulation for isolated binary testing

## Release Flow Dependencies
- **Sequential Jobs**: test → build → integration-tests → release-validation → create-release → publish-pypi → update-homebrew
- **PR workflow**: ci.yml (test → build) then ci-integration.yml via workflow_run (approve → smoke-test → integration-tests → release-validation → report-status)
- **Push/Release workflow**: test → build → integration-tests → release-validation → create-release → publish-pypi → update-homebrew
- **Tag Triggers**: Only `v*.*.*` tags trigger full release pipeline
- **Artifact Retention**: 30 days for debugging failed releases
- **Cross-workflow artifacts**: ci-integration.yml downloads artifacts from ci.yml using `run-id` and `github-token`

## Fork PR Security Model
- Fork PRs get unit tests + build via `ci.yml` (no secrets, runs PR code safely)
- `ci-integration.yml` triggers via `workflow_run` after CI completes — NEVER checks out PR code
- Binary artifacts from ci.yml are tested using test scripts from the default branch (main)
- Environment approval gate (`integration-tests`) ensures maintainer reviews PR before integration tests run
- Commit status is reported back to the PR SHA so results appear on the PR

## Key Environment Variables
- `PYTHON_VERSION: '3.12'` - Standardized across all jobs
Expand Down
27 changes: 17 additions & 10 deletions .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,6 @@ on:
- 'docs/**'
- '.gitignore'
- 'LICENSE'
pull_request:
branches: [ main ]
paths-ignore:
- 'docs/**'
- '.gitignore'
- 'LICENSE'
schedule:
# Run daily at 04:00 UTC (6 AM CEST)
- cron: '0 4 * * *'
Expand All @@ -28,10 +22,16 @@ on:
default: true
type: boolean

# Restrict default GITHUB_TOKEN permissions for all jobs (principle of least privilege)
permissions:
contents: read

jobs:
# Always run tests first with matrix strategy
test:
runs-on: ${{ matrix.os }}
permissions:
contents: read
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -93,8 +93,8 @@ jobs:

- name: Run smoke tests
env:
GITHUB_TOKEN: ${{ secrets.GH_MODELS_PAT }} # Models access
GITHUB_APM_PAT: ${{ secrets.GH_CLI_PAT }} # APM module access
GITHUB_TOKEN: ${{ secrets.GH_MODELS_PAT }}
GITHUB_APM_PAT: ${{ secrets.GH_CLI_PAT }}
run: uv run pytest tests/integration/test_runtime_smoke.py -v

# Build binaries
Expand Down Expand Up @@ -123,7 +123,7 @@ jobs:

runs-on: ${{ matrix.os }}
permissions:
contents: write # Required for release uploads
contents: read # Checkout code; upload-artifact uses separate Actions API

steps:
- name: Checkout code
Expand Down Expand Up @@ -177,8 +177,11 @@ jobs:
if-no-files-found: error

# Integration tests with full source code access
# Skip on push-to-main: already validated by ci-integration.yml during the PR.
# Run on tags (release gate), schedule (regression), and dispatch (manual).
integration-tests:
name: Integration Tests
if: github.ref_type == 'tag' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
needs: [test, build]
strategy:
matrix:
Expand Down Expand Up @@ -255,6 +258,7 @@ jobs:
# Release validation tests - Final pre-release validation of shipped binary
release-validation:
name: Release Validation
if: github.ref_type == 'tag' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
needs: [test, build, integration-tests]
strategy:
matrix:
Expand Down Expand Up @@ -437,7 +441,8 @@ jobs:
name: pypi
url: https://pypi.org/p/apm-cli
permissions:
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
contents: read # Required for actions/checkout
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing

steps:
- name: Checkout code
Expand Down Expand Up @@ -470,6 +475,8 @@ jobs:
runs-on: ubuntu-latest
needs: [test, build, integration-tests, release-validation, create-release, publish-pypi]
if: github.ref_type == 'tag' && needs.create-release.outputs.is_private_repo != 'true' && needs.create-release.outputs.is_prerelease != 'true'
permissions:
contents: read

steps:
- name: Extract SHA256 checksums from GitHub release
Expand Down
Loading
Loading