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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
*.md text eol=lf
*.toml text eol=lf
*.yml text eol=lf
*.ttf binary
25 changes: 25 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,28 @@ jobs:
working-directory: demo
- run: npm run lint
working-directory: demo

release-screenshot:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: '3.13'
- name: Install the hash-locked release-image renderer
run: python -m pip install --require-hashes -r tools/release-requirements.txt
- name: Run the screenshot tests and fail if any are skipped or missing
env:
PROOFCLI_REQUIRE_RENDER: '1'
run: python tests/test_release_screenshot.py
- name: Render the release screenshot
run: python tools/render_release_screenshot.py dist/proofcli-screenshot.png
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: release-screenshot
path: dist/proofcli-screenshot.png
if-no-files-found: error
retention-days: 7
79 changes: 0 additions & 79 deletions .github/workflows/demo-video.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,82 +59,3 @@ jobs:
demo/out/proofcli-motion-v1-verification.json
if-no-files-found: error
retention-days: 7

publish:
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
needs: render
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53
with:
name: motion-demos-v1
path: motion-demos/
- name: Add the new cut without replacing published package assets
env:
GH_TOKEN: ${{ github.token }}
run: |
tag="$(python - <<'PY'
import ast
import re
import sys
from pathlib import Path

root = Path('.')
section = None
pyproject = None
for line in (root / 'pyproject.toml').read_text(encoding='utf-8').splitlines():
stripped = line.strip()
if stripped.startswith('['):
section = stripped
elif section == '[project]':
match = re.fullmatch(r'version\s*=\s*"([^"]+)"', stripped)
if match:
pyproject = match.group(1)
if not pyproject:
sys.exit('pyproject.toml has no [project] version')
tree = ast.parse((root / 'proofcli' / '__init__.py').read_text(encoding='utf-8'))
module = None
for node in tree.body:
if isinstance(node, ast.Assign) and any(
getattr(target, 'id', None) == '__version__' for target in node.targets
):
module = ast.literal_eval(node.value)
if module != pyproject:
sys.exit(
f'refusing to select a release: proofcli.__version__ {module!r} '
f'!= pyproject {pyproject!r}'
)
if not re.fullmatch(r'[0-9]+\.[0-9]+\.[0-9]+', pyproject):
sys.exit(f'refusing to select a release: unsupported version {pyproject!r}')
print(f'v{pyproject}')
PY
)"
echo "Selected release tag ${tag}"
if ! gh release view "$tag" --repo "$GITHUB_REPOSITORY" --json tagName --jq .tagName; then
echo "Refusing to upload: release ${tag} does not exist" >&2
exit 1
fi
gh release upload "$tag" motion-demos/* --repo "$GITHUB_REPOSITORY"
gh release view "$tag" --repo "$GITHUB_REPOSITORY" --json body --jq .body > notes.md
TAG="$tag" python - <<'PY'
import os
from pathlib import Path
tag = os.environ['TAG']
p = Path('notes.md')
body = p.read_text(encoding='utf-8')
if 'proofcli-instagram-reel-v1.mp4' not in body:
body += (
'\n\n### 30-second motion demos\n\n'
f'[Vertical / Instagram](https://github.com/bk1202/proofcli/releases/download/{tag}/proofcli-instagram-reel-v1.mp4) · '
f'[Landscape](https://github.com/bk1202/proofcli/releases/download/{tag}/proofcli-landscape-motion-v1.mp4)\n\n'
'Both use results captured from the actual exporter and ProofCLI. Covers, checksums and media validation are attached separately.\n'
)
p.write_text(body, encoding='utf-8')
PY
gh release edit "$tag" --repo "$GITHUB_REPOSITORY" --notes-file notes.md
51 changes: 8 additions & 43 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
contents: read

publish:
needs: [validate, demo]
needs: [validate]
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
Expand All @@ -34,12 +34,12 @@ jobs:
- run: python -m pip install --require-hashes -r requirements-build.txt
- run: python -m build --no-isolation
- run: python tools/verify_dist.py
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53
with:
name: demo-video
path: dist/
- name: Add the demo checksum
run: python -c "import hashlib; from pathlib import Path; p=Path('dist/proofcli-demo.mp4'); f=Path('dist/SHA256SUMS'); f.write_text(f.read_text()+hashlib.sha256(p.read_bytes()).hexdigest()+' '+p.name+'\n')"
- name: Install the release-image font renderer
run: python -m pip install --require-hashes -r tools/release-requirements.txt
- name: Render a screenshot from a real failing check and a real passing run
run: python tools/render_release_screenshot.py dist/proofcli-screenshot.png
- name: Add the screenshot checksum
run: python -c "import hashlib; from pathlib import Path; p=Path('dist/proofcli-screenshot.png'); f=Path('dist/SHA256SUMS'); f.write_text(f.read_text()+hashlib.sha256(p.read_bytes()).hexdigest()+' '+p.name+'\n')"
- name: Prepare release notes from the versioned changelog
env:
RELEASE_TAG: ${{ github.ref_name }}
Expand All @@ -48,39 +48,4 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ github.ref_name }}
run: gh release create "$RELEASE_TAG" dist/*.whl dist/*.tar.gz dist/*.mp4 dist/SHA256SUMS --verify-tag --title "ProofCLI $RELEASE_TAG" --notes-file dist/RELEASE_NOTES.md

demo:
needs: validate
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: '22'
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97
with:
python-version: '3.13'
- name: Capture actual exporter and CLI results
run: python tools/capture_demo.py
- name: Install locked demo build tools
working-directory: demo
run: npm ci
- name: Check demo dependencies
working-directory: demo
run: npm audit --audit-level=high
- name: Type-check the composition
working-directory: demo
run: npm run lint
- name: Render the 30-second walkthrough
working-directory: demo
run: npm run render
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
with:
name: demo-video
path: demo/out/proofcli-demo.mp4
if-no-files-found: error
retention-days: 7
run: gh release create "$RELEASE_TAG" dist/*.whl dist/*.tar.gz dist/proofcli-screenshot.png dist/SHA256SUMS --verify-tag --title "ProofCLI $RELEASE_TAG" --notes-file dist/RELEASE_NOTES.md
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
### Changed

- `proofcli --help` lists `run`, `validate`, and `init`, each with its own one-line description. `proofcli run --help`, `proofcli validate --help`, and `proofcli init --help` each describe that command instead of sharing one sentence. `proofcli SPEC` still runs a spec, and exit codes are unchanged.
- GitHub releases now attach one PNG of real ProofCLI output: the failing export check and the passing run. The image is drawn with a vendored monospace font. Pillow is installed only while that release image is built, and ProofCLI itself still has no runtime dependencies. The release workflow no longer renders a demo video or installs the Remotion tools, so a video render cannot block the GitHub release or the PyPI publish that follows it. The motion-demo workflow can still render preview videos as workflow artifacts, and it no longer uploads those videos to a release.
- Every pull request and push runs a `release-screenshot` check on Python 3.13. That job installs only the hash-locked Pillow used for the release image, runs the screenshot tests, and fails if one of those tests is skipped or missing. It also uploads the PNG as a workflow artifact.

## 0.4.2 — 2026-10-16

Expand Down
2 changes: 1 addition & 1 deletion docs/demo.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ npm run poster

The output is `demo/out/proofcli-demo.mp4`; the poster is `demo/out/demo-poster.png`. Dependencies are locked in `demo/package-lock.json` and are used only to build the video. The Python package and GitHub Action do not install them.

`demo-video.yml` renders pull-request previews and publishes the named revision after merge to main. It refuses to replace an existing asset. Future edits must use a new revision filename and matching links instead of overwriting published bytes. This media workflow does not trigger a Python package release.
`demo-video.yml` can still render preview videos and store them as workflow artifacts. It does not upload those videos to a GitHub release, and it does not trigger a Python package release.

[The hosted demo workflow](../.github/workflows/demo.yml) intentionally has a successful ordinary job and a failed ProofCLI job. Its failed overall result is the expected demonstration. The separate validation workflow asserts that same failure and remains green only when ProofCLI rejects the broken export.
2 changes: 1 addition & 1 deletion docs/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
2. Install the hash-locked build tools, build wheel/sdist, run `tools/verify_dist.py`, then run the real-process suite against the installed wheel. `tools/audit_dependencies.py` checks build versions/hashes against PyPI's advisory feed; no runtime dependencies are declared.
3. Only after local validation, publish the candidate branch and obtain green Windows/macOS tests, both distribution-install jobs, and the advisory job. Inspect skips/failures. Merge the same reviewed candidate into main; retain CI links as evidence.
4. Create and push an annotated `v<package-version>` tag targeting the validated commit. A pre-1.0 release is not a production/security certification.
5. The tag triggers `release.yml`. The read-only validation matrix must pass, including Linux runtime and three-platform composite Action checks. A separate job captures real demo evidence and renders the 30-second video with locked npm tools. The publish job checks tag/version agreement, builds with pinned tools, verifies fresh wheel/sdist installs, and creates the GitHub release with both distributions, MP4, and `SHA256SUMS`.
5. The tag triggers `release.yml`. The read-only validation matrix must pass, including Linux runtime and three-platform composite Action checks. The publish job checks tag/version agreement, builds with pinned tools, verifies fresh wheel/sdist installs, installs hash-locked Pillow from `tools/release-requirements.txt` only to draw one PNG from a real failing run and a real passing run, and creates the GitHub release with both distributions, that screenshot, and `SHA256SUMS`. Pillow is not a ProofCLI runtime dependency. The job does not render a demo video.
6. Successful release completion triggers `pypi.yml` on the default branch. Its read-only job accepts only a successful tag-push release from this repository, checks that the tag points to the validated commit, and downloads/checksums the published distribution bytes. A separate `pypi` environment job uses PyPA Trusted Publishing with `id-token: write` to upload those exact bytes. It executes no repository code and stores no long-lived token.
7. Confirm both workflows, actual downloadable checksums, PyPI version metadata, and a fresh `pip install proofcli==<version>` before announcing index availability. Release creation and PyPI publication do not overwrite published versions; inspect partial failures rather than silently replacing bytes.

Expand Down
Loading
Loading