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
12 changes: 12 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.git
.github
.venv
.superpowers
.pytest_cache
**/__pycache__
*.egg-info
build
dist
docs
tests
.tools
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,11 @@ updates:
groups:
github-actions:
patterns: ["*"]
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "monthly"
open-pull-requests-limit: 5
groups:
docker:
patterns: ["*"]
22 changes: 22 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,25 @@ jobs:
test-protocol EPC-24-00137/* --pdf-engine $engine --pdf $engine.pdf --lang de
head -c 4 $engine.pdf | grep -q '%PDF'
done

docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- run: docker build -t test-protocol .
- name: Runs unprivileged by default
run: test "$(docker run --rm --entrypoint id test-protocol -u)" = 10001
- name: HTML and PDF from a mounted folder, exit status 0 for PASS and 1 for FAIL
working-directory: examples
run: |
run() { docker run --rm --user "$(id -u):$(id -g)" -v "$PWD:/data" test-protocol "$@"; }
run EPC-24-00137/* --limits limits.toml -o pass.html --pdf pass.pdf --lang de
grep -q 'EPC-24-00137' pass.html
head -c 4 pass.pdf | grep -q '%PDF'
test "$(stat -c %u pass.pdf)" = "$(id -u)"
set +e
run EPC-24-00142/* --limits limits.toml --pdf fail.pdf
code=$?
set -e
test $code -eq 1
head -c 4 fail.pdf | grep -q '%PDF'
30 changes: 29 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Release

# Publishes to PyPI when a version tag (v1.2.3) is pushed: runs the tests,
# Publishes to PyPI and ghcr.io when a version tag (v1.2.3) is pushed: runs the tests,
# checks that the tag matches the package version, builds, then publishes.
# Needs a Trusted Publisher on PyPI for this repository and workflow
# (environment "pypi"); no API token is stored in the repository.
Expand Down Expand Up @@ -52,6 +52,34 @@ jobs:
name: dist
path: dist/

docker:
needs: [test, build, publish] # only an image for a version that is on PyPI
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v7
- id: version
name: Version from the tag; "latest" only for final releases (no rc/a/b/dev)
run: |
version="${GITHUB_REF_NAME#v}"
echo "version=${version}" >> "$GITHUB_OUTPUT"
if [[ "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then prerelease=false; else prerelease=true; fi
echo "prerelease=${prerelease}" >> "$GITHUB_OUTPUT"
- uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v7
with:
context: .
push: true
tags: |
ghcr.io/steitec/test-protocol:${{ steps.version.outputs.version }}
${{ steps.version.outputs.prerelease == 'false' && 'ghcr.io/steitec/test-protocol:latest' || '' }}

publish:
needs: build
runs-on: ubuntu-latest
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 0.1.1 — 2026-11-08

- Docker image `ghcr.io/steitec/test-protocol` with WeasyPrint and its system libraries,
built with every release, so PDF output needs no local installation

## 0.1.0 — 2026-10-29

First release.
Expand Down
40 changes: 40 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# test-protocol in a container, with WeasyPrint and its system libraries, so PDF
# output needs no local installation. Runs in /data; mount your results there and
# run as your own user so the files you get back belong to you.
#
# docker run --rm --user "$(id -u):$(id -g)" -v "$PWD:/data" ghcr.io/steitec/test-protocol \
# result.json --limits limits.toml -o protocol.html --pdf protocol.pdf
FROM python:3.13-slim
LABEL org.opencontainers.image.source=https://github.com/steitec/test-protocol \
org.opencontainers.image.description="Turn raw test results into a printable test protocol (HTML, PDF, JSON)" \
org.opencontainers.image.licenses=MIT

RUN apt-get update \
&& apt-get install -y --no-install-recommends \
libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz-subset0 fonts-liberation \
&& rm -rf /var/lib/apt/lists/*

# The protocol asks for system-ui, which fontconfig would otherwise resolve to the
# first font it finds (a monospaced one here).
COPY <<'FONTS' /etc/fonts/local.conf
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<alias binding="same"><family>system-ui</family><prefer><family>Liberation Sans</family></prefer></alias>
<alias binding="same"><family>sans-serif</family><prefer><family>Liberation Sans</family></prefer></alias>
</fontconfig>
FONTS

WORKDIR /src
COPY pyproject.toml README.md LICENSE ./
COPY src ./src
RUN pip install --no-cache-dir ".[pdf]" && rm -rf /src

RUN useradd --create-home --uid 10001 protocol && install -d -o protocol /data
# With --user there is no home directory; fontconfig still needs a place for its cache.
ENV XDG_CACHE_HOME=/tmp/.cache
USER protocol
WORKDIR /data

ENTRYPOINT ["test-protocol"]
CMD ["--help"]
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,24 @@ Python 3.10 or newer. For PDF output you need either WeasyPrint (the `pdf`
extra; on Windows it needs the GTK/Pango libraries, see the WeasyPrint docs)
or Chrome, Chromium or Edge, which the tool finds on its own.

## Docker

The image contains WeasyPrint and its system libraries, so PDF output needs no
local installation. Run it in the folder with your results:

```console
docker run --rm --user "$(id -u):$(id -g)" -v "$PWD:/data" ghcr.io/steitec/test-protocol \
result.json --limits limits.toml -o protocol.html --pdf protocol.pdf
```

`--user` makes the written files yours; without it the container runs as an
unprivileged user that cannot write to your folder. On Docker Desktop use
`--user 0:0` instead, because it shows the folder as owned by root inside the
container and still writes the files as you on the host. The exit status is the
same as without Docker (0 PASS, 1 FAIL, 2 bad input). The image is built with
every release and tagged with the version and `latest`; pin the version in CI.
PDF fonts come from the image (Liberation Sans).

## Inputs

The format is detected from the file; `--format` forces it.
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "test-protocol"
version = "0.1.0"
version = "0.1.1"
description = "Turn raw test results (CSV, JSON, JUnit XML) into a printable test protocol with limits, PASS/FAIL verdict, traceability and signature fields"
readme = "README.md"
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion src/test_protocol/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Turn raw test results into a printable test protocol."""

__version__ = "0.1.0"
__version__ = "0.1.1"
2 changes: 1 addition & 1 deletion tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,4 @@ def fail(*args, **kwargs):
def test_module_entry():
result = subprocess.run([sys.executable, "-m", "test_protocol", "--version"],
capture_output=True, text=True)
assert result.stdout.strip() == "test-protocol 0.1.0"
assert result.stdout.strip() == "test-protocol 0.1.1"
Loading