Skip to content

CI Integration

This page covers practical patterns to publish private Allure reports from CI, assuming a one‑time infra provision has been done (e.g., allurehost-infra-setup --bucket <bucket> --region <region> --yes). Internal release mechanics (tagging, maintainer checklist) live in CONTRIBUTING to keep this page consumer‑focused.

Allure Report Publishing in CI

Two typical integration patterns:

1. Direct Pytest Invocation Pipeline

sequenceDiagram
  participant Dev
  participant CI as CI Job
  participant Pytest as pytest + plugin
  participant S3 as S3 private
  participant CF as CloudFront

  Dev->>CI: Push code / open PR
  CI->>Pytest: Run tests with plugin flags
  Pytest->>Pytest: Generate allure-results
  Pytest->>S3: Pull latest/history best-effort
  Pytest->>Pytest: Generate static Allure report
  Pytest->>S3: Upload run_id prefix
  Pytest->>S3: Two-phase promote to latest
  Pytest->>S3: Update manifest runs/index.json
  S3-->>CF: OAC fetch private
  Pytest->>CI: Print final run & latest URLs

2. Split Build/Test and Publish Steps

Useful when tests and publishing happen in different jobs (e.g. reusing artifacts).

flowchart TB
    subgraph T[Test job]
        A[Run tests]
    end
    subgraph P[Publish job]
        B[Start publish]
    end

    A -->|Upload allure-results artifact| B
    B --> C[Pull previous latest/history]
    C --> D[Generate report]
    D --> E[Upload run prefix]
    E --> F[Two-phase latest update]
    F --> G[Update manifest & HTML index]
    G --> H[Output URLs / summary]

  classDef phase fill:#e8f4ff,stroke:#1890ff,color:#222
  class A,B,C,D,E,F,G,H phase

Key Guarantees

  • Private delivery via CloudFront + OAC (no S3 website hosting)
  • Two‑phase latest/ update and history pull preserve trends and prevent race conditions

Caching Strategy (Implemented / Enforced by Uploader)

  • index.html: Cache-Control: no-cache
  • Assets (JS/CSS/immutable): public, max-age=31536000, immutable
  • Optional: widgets/ set to no-cache to keep summary live.

Uploader sets headers automatically; no CloudFront cache‑policy customization is needed for index.html.

Security Considerations

Concern Action (User)
Private access Keep bucket private; use CloudFront + OAC
Least privilege Scope ListBucket with s3:prefix; Get/Put on reports
Credentials Prefer OIDC / short‑lived role assumption in CI
OIDC to AWS Use GitHub OIDC + role assumption (no static keys)
Dependency hygiene (Optional) run pip-audit / bandit in your pipeline
Code scanning (Optional) enable CodeQL or similar
Cache correctness Ensure index/widgets served with no-cache

Optional Enhancements

Option Why
Provenance (add-provenance) Supply chain transparency
Auto-clean old runs Control S3 storage cost
TTL tagging (--ttl-days) Drive lifecycle expiration rules
SBOM (CycloneDX) Dependency inventory / compliance

GitHub Actions Examples

Single Job (pytest plugin, OIDC)

jobs:
  report:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-python@v6
        with:
          python-version: "3.12"
      - run: pip install pytest-allure-host allure-pytest
      - run: |
          pytest --alluredir=allure-results \
            --allure-bucket my-allure-reports \
            --allure-project payments \
            --allure-branch main \
            --allure-cloudfront https://reports.example.com

Split Test/Publish Jobs (Artifact Reuse, OIDC)

jobs:
  tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-python@v6
        with:
          python-version: "3.12"
      - run: pip install pytest allure-pytest
      - run: pytest --alluredir=allure-results
      - uses: actions/upload-artifact@v4
        with:
          name: allure-results
          path: allure-results

  publish:
    needs: tests
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-python@v6
        with:
          python-version: "3.12"
      - run: pip install pytest-allure-host allure-pytest
      - uses: actions/download-artifact@v4
        with:
          name: allure-results
          path: allure-results
      - run: |
          publish-allure --bucket my-allure-reports \
            --project payments --branch main \
            --cloudfront https://reports.example.com

Matrix Build Example (Multiple Python Versions)

jobs:
  report:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    strategy:
      matrix:
        python-version: [3.10, 3.11, 3.12]
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-python@v6
        with:
          python-version: ${{ matrix.python-version }}
      - run: pip install pytest-allure-host allure-pytest
      - run: |
          pytest --alluredir=allure-results \
            --allure-bucket my-allure-reports \
            --allure-project payments \
            --allure-branch main \
            --allure-cloudfront https://reports.example.com

LocalStack Example (for local testing)

jobs:
  localtest:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-python@v6
        with:
          python-version: "3.12"
      - run: pip install pytest-allure-host allure-pytest localstack-client
      - run: |
          export AWS_ACCESS_KEY_ID=test
          export AWS_SECRET_ACCESS_KEY=test
          export AWS_REGION=us-east-1
          export AWS_ENDPOINT_URL=http://localhost:4566
          pytest --alluredir=allure-results \
            --allure-bucket my-allure-reports \
            --allure-project payments \
            --allure-branch main \
            --allure-cloudfront http://localhost:4566/distribution

For release/versioning and maintainer workflows see CONTRIBUTING.md.

Jenkins Example (Declarative Pipeline)

pipeline {
  agent any
  environment {
    AWS_REGION = 'us-east-1'
    REPORT_BUCKET = 'my-allure-reports'
    PROJECT = 'payments'
    BRANCH = 'main'
    CF_DOMAIN = 'reports.example.com'
  }
  stages {
    stage('Setup') {
      steps {
        sh 'python3 -m venv .venv'
        sh '. .venv/bin/activate && pip install --upgrade pip pytest-allure-host allure-pytest'
      }
    }
    stage('Preflight') {
      steps {
        sh '. .venv/bin/activate && publish-allure --bucket ${REPORT_BUCKET} --project ${PROJECT} --branch ${BRANCH} --cloudfront https://${CF_DOMAIN} --check --dry-run || true'
      }
    }
    stage('Test') {
      steps {
        sh '. .venv/bin/activate && pytest --alluredir=allure-results'
        // Optional: archive results between stages
        archiveArtifacts artifacts: 'allure-results/**', fingerprint: true, onlyIfSuccessful: true
      }
    }
    stage('Publish') {
      steps {
        withAWS(region: "${AWS_REGION}", credentials: 'jenkins-oidc-role') {
          sh '''
            . .venv/bin/activate
            publish-allure \
              --bucket ${REPORT_BUCKET} \
              --project ${PROJECT} \
              --branch ${BRANCH} \
              --cloudfront https://${CF_DOMAIN} \
              --summary-json summary.json
          '''
        }
      }
    }
    stage('Summary') {
      steps {
        script {
          def summary = readJSON file: 'summary.json'
          echo "Run URL: ${summary.run_url}"
          echo "Latest URL: ${summary.latest_url}"
        }
      }
    }
  }
}