Search and browse the EXR Converter docs

Releasing

The full release and deployment process (protected main, Makefile, GitHub Actions, gh CLI, checklist, troubleshooting) lives in:

AGENTS.md: Releasing and deployment

User-facing history (required on every release):

CHANGELOG.md

Short path: merge a release PR that bumps pyproject.toml and rolls CHANGELOG.md (## [X.Y.Z] + compare link) → Auto-tag release pushes vX.Y.Z if missing → dispatches Release with gh workflow run Release --ref main -f tag=vX.Y.Z → lint/tests/gate → Nuitka → Cosign → GitHub Release.

You do not need a manual tag push after merge (that was what skipped 0.5.0 until fixed). After merge, do not also push a local vX.Y.Z unless Auto-tag failed. Dual triggers race and waste runner minutes.

Hard gates (automated): Auto-tag and Release refuse to ship if CHANGELOG.md lacks ## [X.Y.Z] / the bottom compare link, or if the tag does not match pyproject.toml version (except explicit emergency source_ref= rebuilds).

Emergency rebuild of an existing tag (packaging / native-bridge hotfix on main, same version, do not move the published tag):

gh workflow run Release --ref main -f tag=v0.10.0 -f source_ref=main

That checks out main for the build while still publishing GitHub Release v0.10.0. A plain re-run of the failed tag job will not pick up commits that landed after the tag.

Provenance: each asset gets a Cosign .sigstore.json bundle and a GitHub Attestations record (gh attestation verify). Release notes include the CHANGELOG section for that version plus verify commands. Optional SignPath Authenticode for Windows is off until repository vars/secrets are set (see AGENTS.md).

macOS notarization: when the Developer ID secrets are set, the build job signs each DMG with the team's Developer ID Application certificate (team 83546T4BT8, G2 cert, expires 2031-09-17), notarizes it, and staples it before Cosign. The p12 secret is an OpenSSL 3 pkcs12 -export -legacy export. Without those secrets the DMG stays ad-hoc and notarization is skipped. The notary upload returns a submission id immediately. The job then checks that same Apple submission for up to five hours instead of uploading the DMG again. Non-code files Nuitka drops under Contents/MacOS are moved to Contents/Resources (with symlinks left behind) before that signature, because a Developer ID seal rejects them as unsigned nested code. Secret names and the entitlements are in AGENTS.md.

To test signing without publishing a GitHub Release:

gh workflow run Release --ref <branch> -f macos_only=true -f publish=false

The run uploads exr_converter-macos-arm64-signed and exr_converter-macos-x86_64-signed. On a downloaded DMG:

spctl -a -vvv -t install exr_converter-macos-arm64.dmg
codesign --verify --deep --strict --verbose=2 exr_converter-macos-arm64.dmg

Microsoft Store: the Windows build can also publish exr_converter-windows-x86_64.msixupload (unsigned Partner Center upload) and exr_converter-windows-x86_64.msix (self-distribution). The loose .msix is signed with signtool when Azure Artifact Signing secrets are set, and left unsigned otherwise. An unsigned MSIX does not install. The upload's contents are never signed. A later release can also submit the .msixupload with msstore when the Store secrets and MSSTORE_APP_ID are set. The first submission is manual, and a publish failure does not block the GitHub Release. Steps: msix.md.

Docs site: Markdown under docs/ is built with Astro (site/) and published by Vercel on push to main (config in site/vercel.json). That is independent of the versioned app Release workflow. Preview locally with make docs-serve. Public URL: exr-converter.derekvfx.ca.

User-facing ProRes / VideoToolbox guide: prores.md. Related design notes (not release machinery): plan-12bit-prores-oxideav.md.

Source: EXR Converter on GitHub