Skip to content

Integration Tests (PR) #44

Integration Tests (PR)

Integration Tests (PR) #44

# Integration tests for PRs (including forks).
# Uses workflow_run to safely access secrets without checking out untrusted PR code.
# Security model:
# - ci.yml builds the PR's code in an unprivileged context and uploads binary artifacts
# - This workflow downloads those artifacts and runs tests from the DEFAULT BRANCH (main)
# - PR code is never checked out in a privileged context
# - Fork PRs require manual environment approval before tests run (security gate)
# - Internal PRs (same repo) skip the approval gate — contributors already have write access
name: Integration Tests (PR)
env:
PYTHON_VERSION: '3.12'
on:
workflow_run:
workflows: ["CI"]
types: [completed]
permissions:
contents: read
actions: read
statuses: write
jobs:
# Fork PRs: require manual approval via environment protection rules.
# A maintainer must review the fork's code before secrets are exposed to its artifacts.
approve-fork:
if: >
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.head_repository.full_name != github.repository
runs-on: ubuntu-latest
environment: integration-tests
steps:
- run: echo "Fork PR approved for ${{ github.event.workflow_run.head_branch }} from ${{ github.event.workflow_run.head_repository.full_name }}"
# Internal PRs: skip approval gate — contributors already have write access to the repo.
approve-internal:
if: >
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.head_repository.full_name == github.repository
runs-on: ubuntu-latest
steps:
- run: echo "Internal PR auto-approved for ${{ github.event.workflow_run.head_branch }}"
# Linux-only for fast PR feedback. Full platform smoke tests run post-merge.
smoke-test:
needs: [approve-fork, approve-internal]
# Run if either approval job succeeded (the other will be skipped)
if: always() && (needs.approve-fork.result == 'success' || needs.approve-internal.result == 'success')
runs-on: ubuntu-24.04
permissions:
contents: read
actions: read
steps:
# Checkout default branch (main) — never PR code
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '24'
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Cache uv environments
uses: actions/cache@v3
with:
path: |
~/.cache/uv
~/.local/share/uv
key: ${{ runner.os }}-uv-${{ hashFiles('**/pyproject.toml') }}
restore-keys: |
${{ runner.os }}-uv-
- name: Install dependencies
run: uv sync --extra dev
- name: Run smoke tests
env:
GITHUB_TOKEN: ${{ secrets.GH_MODELS_PAT }}
GITHUB_APM_PAT: ${{ secrets.GH_CLI_PAT }}
run: uv run pytest tests/integration/test_runtime_smoke.py -v
# Linux-only — downloads the single Linux binary artifact from ci.yml.
integration-tests:
name: Integration Tests
needs: [smoke-test]
if: always() && needs.smoke-test.result == 'success'
runs-on: ubuntu-24.04
permissions:
contents: read
actions: read
models: read
steps:
# Checkout default branch (main) for test scripts — never PR code
- name: Checkout code
uses: actions/checkout@v4
# Download binary artifact built from PR code by ci.yml
- name: Download APM binary from CI workflow
uses: actions/download-artifact@v4
with:
name: apm-linux-x86_64
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '24'
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Install test dependencies
run: uv sync --extra dev
- name: Run integration tests
env:
APM_E2E_TESTS: "1"
GITHUB_TOKEN: ${{ secrets.GH_MODELS_PAT }}
GITHUB_APM_PAT: ${{ secrets.GH_CLI_PAT }}
ADO_APM_PAT: ${{ secrets.ADO_APM_PAT }}
run: |
chmod +x scripts/test-integration.sh
uv run ./scripts/test-integration.sh
timeout-minutes: 20
# Linux-only — validates the Linux binary in isolation. Full platform validation runs post-merge.
release-validation:
name: Release Validation
needs: [integration-tests]
if: always() && needs.integration-tests.result == 'success'
runs-on: ubuntu-24.04
permissions:
contents: read
actions: read
models: read
steps:
# Checkout default branch for test scripts — never PR code
- name: Checkout test scripts
uses: actions/checkout@v4
with:
sparse-checkout: scripts
path: repo
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '24'
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
# Download binary artifact to isolated test directory
- name: Download APM binary from CI workflow
uses: actions/download-artifact@v4
with:
name: apm-linux-x86_64
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
path: /tmp/apm-isolated-test/
- name: Make binary executable and verify isolation
run: |
cd /tmp/apm-isolated-test
echo "Downloaded structure:"
find . -name "apm" -type f
ls -la ./dist/
chmod +x ./dist/apm-linux-x86_64/apm
- name: Create APM symlink for testing
run: |
cd /tmp/apm-isolated-test
ln -s "$(pwd)/dist/apm-linux-x86_64/apm" "$(pwd)/apm"
echo "/tmp/apm-isolated-test" >> $GITHUB_PATH
- name: Prepare test scripts from default branch
run: |
# Copy test scripts from main branch checkout into isolated test dir
cp -r repo/scripts /tmp/apm-isolated-test/scripts
- name: Run release validation tests
env:
APM_E2E_TESTS: "1"
GITHUB_TOKEN: ${{ secrets.GH_MODELS_PAT }}
GITHUB_APM_PAT: ${{ secrets.GH_CLI_PAT }}
ADO_APM_PAT: ${{ secrets.ADO_APM_PAT }}
run: |
cd /tmp/apm-isolated-test
chmod +x scripts/test-release-validation.sh
./scripts/test-release-validation.sh
timeout-minutes: 20
# Report integration test results back to the PR as a commit status
report-status:
needs: [smoke-test, integration-tests, release-validation]
if: always()
runs-on: ubuntu-latest
permissions:
statuses: write
steps:
- name: Report status to PR
uses: actions/github-script@v7
with:
script: |
const success = '${{ needs.release-validation.result }}' === 'success'
&& '${{ needs.integration-tests.result }}' === 'success'
&& '${{ needs.smoke-test.result }}' === 'success';
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: context.payload.workflow_run.head_sha,
state: success ? 'success' : 'failure',
target_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
context: 'Integration Tests (PR)',
description: success ? 'All integration tests passed' : 'Integration tests failed'
});