PDF.js's signature dialog saved every signature in localStorage under pdfjs.signature, "save" ticked on every open, for this origin rather than for a bau-pm user. Measured on v0.1.0: a typed and an uploaded signature were offered to a fresh page on the same browser, the way the next person on a shared tablet would get it, a Redmine logout included. Another browser profile saw nothing; the server never had anything. The glue's fifth duty, in every window before PDF.js's modules: delete the store (a device that saved signatures loses them on its first visit), refuse writes to that key, hold the save box unticked through a property on the element and hide it, and hide the dialog's Bild tab and its file picker, so a scanned signature never enters the dialog. test/signatures.mjs is the second gate, in CI and the release workflow: a browser seeded with a real saved entry (captured from v0.1.0's own store) is offered none, the dialog shows neither the box nor the tab, a typed signature is placed and not stored, a fresh page is offered nothing. Fed v0.1.0: 9 findings, exit 1. Against this image: 0, and the zero-host gate drove 117 tool pages with nothing leaving the origin. A signed PDF still exports with the signature on the page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
91 lines
4.3 KiB
YAML
91 lines
4.3 KiB
YAML
# Release - runs on semver tags (vMAJOR.MINOR.BUGFIX, cut on main after a
|
|
# fast-forward from dev): the gates, the image built and checked like in CI,
|
|
# pushed to the registry, and a Gitea release with the CHANGELOG section.
|
|
# Required: secrets REGISTRY_USERNAME, REGISTRY_PASSWORD (instance-wide).
|
|
name: Release
|
|
|
|
on:
|
|
push:
|
|
tags: ['v[0-9]+.[0-9]+.[0-9]+']
|
|
|
|
env:
|
|
REGISTRY: ${{ vars.REGISTRY || 'hub.bundespruefstelle.ch' }}
|
|
IMAGE: bau-pm/pdf-tools
|
|
|
|
jobs:
|
|
release:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 150
|
|
steps:
|
|
# Full history, or the ancestry gate cannot answer and would pass.
|
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: derive version from tag
|
|
run: echo "VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_ENV"
|
|
|
|
- name: gate - CHANGELOG has a section for this version
|
|
run: |
|
|
if ! grep -q "^## \[$VERSION\]" CHANGELOG.md; then
|
|
echo "no '## [$VERSION]' section in CHANGELOG.md - cut it from [Unreleased] first" >&2
|
|
exit 1
|
|
fi
|
|
|
|
- name: gate - the tag is on main
|
|
run: |
|
|
git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main
|
|
if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/main; then
|
|
echo "$GITHUB_SHA is not an ancestor of main - fast-forward main before tagging" >&2
|
|
exit 1
|
|
fi
|
|
|
|
- name: build the image
|
|
timeout-minutes: 60
|
|
# --pull (bau-pm BAU-191, review finding 41). Both base images are
|
|
# floating tags - node:20-alpine and nginx-unprivileged:alpine-slim -
|
|
# so the nginx and OS packages that ship are whatever the tag pointed
|
|
# at when the runner last pulled it. v0.1.0 went out with nginx 1.31.6
|
|
# while the tag had moved to 1.31.8. This is the build people run.
|
|
run: |
|
|
docker build --pull --build-arg VERSION="v$VERSION" \
|
|
-t "$REGISTRY/$IMAGE:$VERSION" -t "$REGISTRY/$IMAGE:latest" .
|
|
|
|
- name: gate - nothing leaves the origin, no signature is kept
|
|
timeout-minutes: 50
|
|
run: |
|
|
docker build -f test/Dockerfile -t bau-pm-pdf-tools-gate:release .
|
|
net="bau-pm-pdf-tools-release-${GITHUB_RUN_ID}"
|
|
docker network create "$net" >/dev/null
|
|
trap 'docker rm -f -v "$net-app" >/dev/null 2>&1; docker network rm "$net" >/dev/null 2>&1' EXIT
|
|
docker run -d --name "$net-app" --network "$net" --network-alias pdf-tools "$REGISTRY/$IMAGE:$VERSION" >/dev/null
|
|
for i in $(seq 1 30); do
|
|
docker run --rm --network "$net" bau-pm-pdf-tools-gate:release wget -q --spider http://pdf-tools:8080/ && break
|
|
[ "$i" -eq 30 ] && { echo "the image never answered" >&2; exit 1; }
|
|
sleep 2
|
|
done
|
|
docker run --rm --network "$net" bau-pm-pdf-tools-gate:release node test/zero-hosts.mjs http://pdf-tools:8080
|
|
# bau-pm §2.77: the signing tool keeps no signature on the device.
|
|
docker run --rm --network "$net" bau-pm-pdf-tools-gate:release node test/signatures.mjs http://pdf-tools:8080
|
|
|
|
- name: push
|
|
run: |
|
|
echo "${{ secrets.REGISTRY_PASSWORD }}" \
|
|
| docker login "$REGISTRY" -u "${{ secrets.REGISTRY_USERNAME }}" --password-stdin
|
|
docker push "$REGISTRY/$IMAGE:$VERSION"
|
|
docker push "$REGISTRY/$IMAGE:latest"
|
|
echo "digest: $(docker inspect --format '{{index .RepoDigests 0}}' "$REGISTRY/$IMAGE:$VERSION")"
|
|
|
|
# Below 1.0.0 a release is a prerelease, derived from the tag.
|
|
- name: create Gitea release with changelog notes
|
|
run: |
|
|
notes="$(awk -v ver="$VERSION" '$0 ~ "^## \\[" ver "\\]" {f=1; next} /^## \[/ {f=0} f' CHANGELOG.md)"
|
|
payload="$(printf '%s' "$notes" | python3 -c 'import json,os,sys; v=os.environ["VERSION"]; print(json.dumps({"tag_name": "v" + v, "name": "bau-pm-pdf-tools v" + v, "body": sys.stdin.read().strip(), "prerelease": v.split(".")[0] == "0"}))')"
|
|
curl -sf -X POST \
|
|
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
|
-H "Content-Type: application/json" \
|
|
-d "$payload" \
|
|
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/releases" >/dev/null \
|
|
&& echo "Gitea release v$VERSION created" \
|
|
|| echo "release creation skipped (may already exist)"
|