Publish Allure static reports to private S3 behind CloudFront with history preservation
Project description
pytest-allure-host
Publish Allure static reports to private S3 behind CloudFront with history preservation and SPA-friendly routing.
Optional infrastructure (AWS CDK stack to provision the private S3 bucket + CloudFront OAC distribution) lives externally: https://github.com/darrenrabbs/allurehosting-cdk
See docs/architecture.md and .github/copilot-instructions.md for architecture and design constraints.
Documentation
Full documentation (quickstart, AWS setup, IAM least-privilege, CLI usage, changelog) is published at:
https://darrenrabbs.github.io/allurehosting/
The README intentionally stays lean—refer to the site for detailed guidance.
Features
- Generate Allure static report from
allure-results - Preserve history by pulling
latest/historybefore generation - Upload to S3 under
<project>/<branch>/<run_id>/and update<project>/<branch>/latest/ - Two-phase latest update: upload to
latest_tmp/, swap intolatest/, writeLATEST_READYmarker - Optional retention: keep only N newest runs (
--max-keep-runs) - Correct caching headers:
index.htmlandwidgets/→no-cache; assets → immutable - Optional TTL tagging on objects (
--ttl-days) for S3 lifecycle policies - Optional summary JSON output for CI
- Best-effort manifest at
runs/index.jsonwith new run metadata - Lightweight pointer file
latest.json(branch root) - Human-friendly HTML index at
runs/index.htmlfor navigating past runs- Columns: Run ID, raw epoch, UTC Time (human readable), Size (pretty units), P/F/B (passed/failed/broken counts), links to the immutable run and the moving latest
- Newest run highlighted with a star (★) and soft background
Quick start
# Install the publisher
pip install pytest-allure-host
# Run your test suite and produce allure-results/
pytest --alluredir=allure-results
# Plan (no uploads) – shows what would be published
publish-allure \
--bucket my-allure-bucket \
--project myproj \
--branch main \
--dry-run --summary-json plan.json
# Real publish (requires AWS creds: env vars, profile, or OIDC)
publish-allure \
--bucket my-allure-bucket \
--project myproj \
--branch main
Notes:
--prefixdefaults toreports; omit unless you need a different root.--branchdefaults to$GIT_BRANCHormainif unset.- Add
--cloudfront https://reports.example.comto print CDN URLs. - Use
--checkto preflight (AWS / allure binary / inputs) before a real run. - Add
--context-url https://jira.example.com/browse/PROJ-123to link a change ticket in the runs index. - Use
--dry-run+--summary-jsonin CI for a planning stage artifact. - Provide
--ttl-daysand/or--max-keep-runsfor lifecycle & cost controls.
Requirements
- Python 3.9+
- AWS credentials with S3 access to the target bucket
- Allure commandline available on PATH (e.g., via Allure CLI or allure-pytest)
S3 layout and caching
- Keys:
s3://<bucket>/<prefix>/<project>/<branch>/<run_id>/...s3://<bucket>/<prefix>/<project>/<branch>/latest/...s3://<bucket>/<prefix>/<project>/<branch>/runs/index.json(manifest)s3://<bucket>/<prefix>/<project>/<branch>/runs/index.html(HTML index)s3://<bucket>/<prefix>/<project>/<branch>/latest.json(pointer)
- Two-phase swap writes a
LATEST_READYmarker file underlatest/when ready. - Cache-Control:
index.html:no-cache- files under
widgets/:no-cache - everything else:
public, max-age=31536000, immutable
CLI usage
Install locally for development:
pip install -e .[dev]
Run the publisher after tests generate allure-results:
publish-allure \
--bucket your-bucket \
--prefix reports \
--project demo \
--branch main \
--cloudfront https://reports.example.com \
--ttl-days 30 \
--max-keep-runs 10
Flags (CLI):
--bucket(required): S3 bucket name--prefix(default:reports): Root prefix--project(required): Project name--branch(default:$GIT_BRANCHormain)--run-id(default:$ALLURE_RUN_IDorYYYYMMDD-HHMMSS)--cloudfront(optional; default:$ALLURE_CLOUDFRONT)--results(default:allure-results): Input directory--report(default:allure-report): Output directory--ttl-days(optional): Addttl-days=<N>object tag--max-keep-runs(optional): Keep N newest run prefixes, delete older--summary-json <path>: Write machine-readable summary--check: Preflight validation (AWS access, allure, inputs)--dry-run: Print planned prefixes and sample headers, no upload--skip-preflight: Skip automatic preflight before publish (not recommended)--allow-duplicate-prefix-project: Allowprefix == project(guard is on by default)--s3-endpoint: Custom S3 endpoint (e.g.http://localhost:4566for LocalStack)
Environment fallbacks:
ALLURE_BUCKET→--bucketALLURE_PREFIX→--prefixALLURE_PROJECT→--projectGIT_BRANCH→--branchdefaultALLURE_RUN_ID→--run-iddefaultALLURE_CLOUDFRONT→--cloudfrontdefaultALLURE_AWS_REGION→ preferred region used in preflight comparisonALLURE_CLOUDFRONT_DISTRIBUTION_ID→ explicit distribution id for preflightALLURE_S3_ENDPOINT→--s3-endpointdefault (LocalStack / custom S3)
Pytest plugin usage
Run tests and publish during terminal summary:
pytest \
--allure-bucket your-bucket \
--allure-prefix reports \
--allure-project demo \
--allure-branch main \
--allure-cloudfront https://reports.example.com \
--allure-ttl-days 30 \
--allure-max-keep-runs 10
Flags (pytest):
--allure-bucket(required)--allure-prefix(default:reports)--allure-project(required)--allure-branch(default:$GIT_BRANCHormain)--allure-run-id(default:$ALLURE_RUN_IDorYYYYMMDD-HHMMSS)--allure-cloudfront(optional; default:$ALLURE_CLOUDFRONT)--allure-ttl-days(optional)--allure-max-keep-runs(optional)--allure-summary-json <path>(optional)--allure-check/--allure-dry-run(optional)
Preflight and dry-run
Preflight runs automatically before a real publish. You can also run it explicitly with --check. Use --skip-preflight only if you know what you’re doing.
What preflight validates (read‑only):
- Local inputs:
allure-resultsexists and is non-empty; Allure CLI is available on PATH. - S3 bucket:
HeadBucketand a smallListObjectsV2to confirm existence and access.- Public Access Block: all four flags must be
true. - Bucket policy status:
IsPublicmust befalse. - Region detection and comparison against
ALLURE_AWS_REGION/aws_regionif provided.
- CloudFront distribution:
- When
ALLURE_CLOUDFRONT_DISTRIBUTION_ID/cloudfront_distribution_idis set, it is used directly. - Otherwise, the distribution is discovered from
--cloudfrontdomain/aliases. - Status must be
Deployed, and an Origin Access Control (OAC) must be attached to at least one origin (legacy OAI is not sufficient).
- When
On success, a concise line is printed in addition to the machine-readable dict:
[preflight] OK — bucket=my-bucket, region=eu-west-1, distribution=E123ABC456
--dry-run/--allure-dry-run prints planned S3 keys and cache headers; no uploads occur.
Guardrails:
- The CLI fails fast if
prefix == projectto avoid redundant S3 paths. If this is intentional, pass--allow-duplicate-prefix-project.
Config keys (YAML or env) relevant to preflight:
# application.yml / allure-host.yml
bucket: my-bucket
prefix: reports
project: demo
branch: main
cloudfront: https://reports.example.com
aws_region: us-east-1 # used for region comparison
cloudfront_distribution_id: E123ABC456 # preferred for deterministic preflight
Environment equivalents:
ALLURE_BUCKET, ALLURE_PREFIX, ALLURE_PROJECT, ALLURE_BRANCH,
ALLURE_CLOUDFRONT, ALLURE_AWS_REGION, ALLURE_CLOUDFRONT_DISTRIBUTION_ID
Outputs
- S3 prefixes: run and latest
- Optional CDN URLs (if
--cloudfrontprovided) runs/index.jsonmanifest updated with new run entryruns/index.htmlHTML table of recent runs (newest first) with columns: Run ID, Epoch, UTC Time, Size, P/F/B, Run, Latest (newest row highlighted with ★)latest.jsonpointer to current run (simple machine-readable metadata)- Optional
--summary-jsonwith sizes, file counts, and destination URLs latest/LATEST_READYmarker indicates the swap is complete
Security
See SECURITY.md for how to report vulnerabilities. Never open a public issue containing sensitive details.
Badges / Status
- CI: multi-version test matrix + lint/security
- CodeQL: static code analysis
Contributing
See CONTRIBUTING.md and follow the pre-commit hooks (pre-commit install).
Release
Tagged versions (vX.Y.Z) are published to PyPI automatically via GitHub OIDC.
CI examples
GitHub Actions (CLI):
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: "3.11" }
- run: pip install .[dev]
- run: pytest -q
- name: Publish Allure
env:
AWS_REGION: us-east-1
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GIT_BRANCH: ${{ github.ref_name }}
run: >-
publish-allure --bucket $BUCKET --prefix reports --project demo
--branch "$GIT_BRANCH" --cloudfront https://reports.example.com
--ttl-days 30 --max-keep-runs 10
Pytest-driven (plugin):
- run: pytest -q \
--allure-bucket $BUCKET \
--allure-prefix reports \
--allure-project demo \
--allure-branch "$GIT_BRANCH" \
--allure-cloudfront https://reports.example.com \
--allure-ttl-days 30 \
--allure-max-keep-runs 10
Minimal publish-only workflow
Create .github/workflows/allure-publish.yml for a lightweight pipeline that runs tests, generates the report, and publishes it (using secrets for the bucket and AWS credentials):
name: allure-publish
on: [push, pull_request]
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install deps (minimal)
run: pip install pytest pytest-allure-host allure-pytest
- name: Run tests
run: pytest --alluredir=allure-results -q
- name: Publish Allure report (dry-run on PRs)
env:
AWS_REGION: us-east-1
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
ALLURE_BUCKET: ${{ secrets.ALLURE_BUCKET }}
run: |
EXTRA=""
if [ "${{ github.event_name }}" = "pull_request" ]; then EXTRA="--dry-run"; fi
publish-allure \
--bucket "$ALLURE_BUCKET" \
--project myproj \
--branch "${{ github.ref_name }}" \
--summary-json summary.json $EXTRA
- name: Upload publish summary (always)
if: always()
uses: actions/upload-artifact@v4
with:
name: allure-summary
path: summary.json
Notes:
- Add
--cloudfront https://reports.example.comif you have a CDN domain. - Add
--context-url ${{ github.server_url }}/${{ github.repository }}/pull/${{ github.event.pull_request.number }}inside PRs to link the run to its PR. - Use
--max-keep-runs/--ttl-daysto manage storage costs. - For LocalStack-based tests, set
--s3-endpointand exportALLURE_S3_ENDPOINTinenv:.
Troubleshooting
- Missing Allure binary: ensure the Allure CLI is installed and on PATH.
- Access denied: verify AWS credentials and bucket IAM for Put/Get/List/Delete.
- SPA routing 403/404: configure CloudFront error mapping to
/index.html.
Style tweaks (dashboard)
You can adjust dashboard typography and KPI sizing via CSS variables without editing the stylesheet. Add these anywhere global (e.g., a small <style> tag or a site-wide CSS file):
/* Example: make KPI label/value bigger and cards taller in v2 */
body {
--kpi-title-size: 13px; /* KPI label, e.g., "FAILURES" */
--kpi-value-size: 20px; /* big number */
--kpi-delta-size: 13px; /* ▲ ▼ delta text */
--kpi-card-height: 180px; /* v2 KPI card height */
}
Available variables (defaults shown):
--kpi-title-size: 12px--kpi-value-size: 18px--kpi-delta-size: 12px--kpi-card-height: 160px(v2)--kpi-card-min-height-v1: 64px(v1)
The stylesheet web/static/css/runs-polish.css is now organized with clear section headers for quick navigation:
- Section: KPI cards (v1)
- Section: V2 overrides (top-level, high specificity)
- Section: V2 failure summary sidebar
- Section: V2 general card chrome and text sizes
Development
- Install with Poetry:
poetry install - Run tests:
poetry run pytest -q - Lint (security quick):
poetry run bandit -r pytest_allure_host - Unified lint helper (mirrors CI):
scripts/lint.sh # check mode (ruff lint+format check, bandit, pip-audit) scripts/lint.sh --fix # apply ruff fixes + format scripts/lint.sh pre-commit # also run pre-commit hooks on all files
Quick local trial (macOS)
This section walks you through a minimal end-to-end run locally.
- Prereqs
- AWS credentials configured (via
AWS_PROFILEor access keys); setAWS_REGION. - Allure CLI installed on PATH:
brew install allure
- Python deps installed:
poetry install # or pip install -e .[dev]
- Generate Allure results
- Create a tiny test (optional example):
mkdir -p tests cat > tests/test_sample.py <<'PY' def test_ok(): assert True PY
- Run pytest to emit results:
poetry run pytest --alluredir=allure-results
- Preflight and dry-run
poetry run publish-allure \
--bucket <bucket> \
--prefix reports \
--project demo \
--branch $(git rev-parse --abbrev-ref HEAD) \
--cloudfront https://<cloudfront_domain> \
--check \
--dry-run
- Publish
poetry run publish-allure \
--bucket <bucket> \
--prefix reports \
--project demo \
--branch $(git rev-parse --abbrev-ref HEAD) \
--cloudfront https://<cloudfront_domain> \
--ttl-days 30 \
--max-keep-runs 5
- Verify
- S3:
reports/demo/<branch>/<run_id>/...andreports/demo/<branch>/latest/withLATEST_READY. - CDN: open printed
run_url/latest_url.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pytest_allure_host-2.1.0.tar.gz.
File metadata
- Download URL: pytest_allure_host-2.1.0.tar.gz
- Upload date:
- Size: 58.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
56d25dec81addb08f053ab91348c2869e6e28ba38461c5e427dd96c7976d267c
|
|
| MD5 |
0cc365b69ff455a383f906dbe755060a
|
|
| BLAKE2b-256 |
0b4e836229db7a2bc6a8bd083ed7f442ca3a88e3aba414d55b7e318086357203
|
Provenance
The following attestation bundles were made for pytest_allure_host-2.1.0.tar.gz:
Publisher:
release.yml on darrenrabbs/allurehosting
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytest_allure_host-2.1.0.tar.gz -
Subject digest:
56d25dec81addb08f053ab91348c2869e6e28ba38461c5e427dd96c7976d267c - Sigstore transparency entry: 628781086
- Sigstore integration time:
-
Permalink:
darrenrabbs/allurehosting@0c5acf53b1455d86b7c991308e7955698ce546a7 -
Branch / Tag:
refs/tags/v2.1.0 - Owner: https://github.com/darrenrabbs
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0c5acf53b1455d86b7c991308e7955698ce546a7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pytest_allure_host-2.1.0-py3-none-any.whl.
File metadata
- Download URL: pytest_allure_host-2.1.0-py3-none-any.whl
- Upload date:
- Size: 56.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ade6053d19e8a547b7f3066d89174b5122228dd44d3a62c58261f20f3cf9cba6
|
|
| MD5 |
bb87ca7bbbdde538635bd0affbc06bc6
|
|
| BLAKE2b-256 |
2683f61bc77a6912950bb9bf0171a1dcf9e0d50a42dd7ba44a7393087b1600c0
|
Provenance
The following attestation bundles were made for pytest_allure_host-2.1.0-py3-none-any.whl:
Publisher:
release.yml on darrenrabbs/allurehosting
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytest_allure_host-2.1.0-py3-none-any.whl -
Subject digest:
ade6053d19e8a547b7f3066d89174b5122228dd44d3a62c58261f20f3cf9cba6 - Sigstore transparency entry: 628781094
- Sigstore integration time:
-
Permalink:
darrenrabbs/allurehosting@0c5acf53b1455d86b7c991308e7955698ce546a7 -
Branch / Tag:
refs/tags/v2.1.0 - Owner: https://github.com/darrenrabbs
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0c5acf53b1455d86b7c991308e7955698ce546a7 -
Trigger Event:
push
-
Statement type: