Repository navigation
feat: draft release with a signable zip and a NEVR-PLUGIN-BUILD stamp - #2
Merged
Merged
Conversation
…le zip The release signer (docs/plugin-release.md) takes a mod release as exactly three files and compares the DLL's build identity with the tag and the attested commit. - plugin.cpp carries one \0NEVR-PLUGIN-BUILD <version> <commit>\0 literal, never NEVR-BUILD (nEVR's installer takes that for the runtime). Init logs it, so --gc-sections keeps it. CMake sets MOD_COMMIT from GITHUB_SHA, else git, else 40 zeros. - package.py packs a mod as <id>-v<version>-windows.zip (DLL, LICENSE, NOTICE if any, settings sheet, inner SHA256SUMS; flat, fixed dates) beside echo-mod.json (no sha256 or size, with package) and a SHA256SUMS of those two. - check_mod.py refuses a DLL without exactly one matching literal, and a release that is not that layout (extra files, directories, symlinks, dates, sums that disagree). - Tests written first; they failed (26 of 30) before the change. Co-Authored-By: Andrew Bates <a@sprock.io>
…ownloaded code release.yml's build job holds contents: read, id-token and attestations: write and nothing else, and runs this repo's own check_mod.py instead of curl-ing the template's main. A second job, with contents: write and no build code, creates the release as a draft: the signer writes only into drafts and publishes after signing. Co-Authored-By: Andrew Bates <a@sprock.io>
…edits Co-Authored-By: Andrew Bates <a@sprock.io>
Co-Authored-By: Andrew Bates <a@sprock.io>
chore/standards now contains origin/main (b53afe8, plugin API 7 and the asset example). The merge was textually clean; checked beyond that: - just verify exits 0 (31 tests) - an api 7 build with -DECHO_MOD_ASSET_EXAMPLE=ON -DECHO_MOD_WERROR=ON passes check_mod.py --dll and carries the NEVR-PLUGIN-BUILD literal Co-Authored-By: Andrew Bates <a@sprock.io>
AGENTS.md rule 7 says only the Nvr* exports leave the DLL, and the signer's RS3 check refuses anything else, but check_mod only warned. A release that passed the template's check could then be refused at signing. It is an error now. The new test renames one export in a copy of the built DLL (same length, so the PE layout stays) and expects an error naming it. It failed before the change (AssertionError: False is not true : []) and passes after. The built DLL still passes, and so does Aaliyah's tablet DLL. Co-Authored-By: Andrew Bates <a@sprock.io>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release step for the nEVR release signer: a
v*tag builds, checks and attests the mod, then opens a draft release with the three files the signer takes. Built to Teth's spec,docs/plugin-release.mdin thesprockee/release-signer PR #25 (head642888d), section numbers below are that file's.Stacked on #1 (base
chore/standards):build.ymlhas to stopcurl-ing the oldcheck_mod.pybefore it can check the new layout, andAGENTS.mdshares rows with #1. Merge #1 first; this retargets tomainby itself, or I rebase it.What changes
src/plugin.cppcarries one\0NEVR-PLUGIN-BUILD <version> <commit40>\0(neverNEVR-BUILD), logged inInitso--gc-sectionskeeps it.CMakeLists.txtsetsMOD_COMMITfromGITHUB_SHA, elsegit rev-parse HEAD, else 40 zeros.check_mod.pyrefuses a DLL with none, two, aNEVR-BUILDanywhere, the wrong version, or (in a release) a commit that isn'tsource.commit.package.pymakes<id>-v<ver>-windows.zip(DLL,LICENSE,NOTICEif present,<Stem>.plugin.jsonif the mod has settings, innerSHA256SUMS; flat, fixed dates),echo-mod.jsonwithoutsha256/sizeand withpackage, and aSHA256SUMSof those two. No loose DLL. A mod with noLICENSEis not packed.check_mod.py --publishedenforces exactly that: three files, name<id>-v<ver>-windows.zip, exact zip entries, no directories/symlinks/special entries/colliding names, fixed dates, entries ≤ 16 MiB, both sums files agree.release.yml:buildjobcontents: read+id-token+attestations: writebuilds, runs this repo's owncheck_mod.py, attestsdist/*, uploads it;draftjob (contents: write, no build code) downloads it and runsgh release create --draft --verify-tag(--prereleasefrom the manifest). Top-levelpermissions: {}. Every action pinned by SHA. Nocurl.AGENTS.mdrule 14 (the identity), the layout table, the Releasing section; aNOTICEtemplate that goes in the zip.Failing-first tests
Written before the code. First run on the new tests, old tools (
Ran 30 tests ... FAILED (failures=6, errors=20); the 4 that passed include "never the runtime's literal"):After:
python3 tests/test_check_mod.py->Ran 30 tests in 1.969s OK. They cover the identity (wrong version, wrong commit,NEVR-BUILD, none, two, unset commit) and the layout (extra asset, loose DLL,sha256in the manifest, wrong package name, extra sums line, directory / nested / unknown / colliding / backslash entries, symlink, dates, bad inner sums, wrong DLL name), plus "same files -> same zip" and "no LICENSE -> not packed".Commands run, on
89f3433just verify: exit 0.Ran 5 tests ... OK(PR-title),Ran 30 tests ... OK; the release check printedok.actionlint .github/workflows/*.yml: exit 0 (it found SC2086 on the first version; fixed in the last commit).GITHUB_SHA=abab...ab:[b'\x00NEVR-PLUGIN-BUILD 0.1.0 abababababababababababababababababababab\x00'], andd.count(b"NEVR-BUILD ") == 0.352517f1...bb47b3 example-mod-v0.1.0-windows.zip,fc162f99...15ee echo-mod.json,597b69b3...0242c SHA256SUMS(both runs).ExampleMod.dll, LICENSE, NOTICE, ExampleMod.plugin.json, SHA256SUMS, all dated 1980-01-01.Not run
It has now: one run onrelease.ymlhas not run on GitHub.v0.0.0-test.1, see the update below. Not run: the signer itself, and a signed DLL loading.Things the template can't meet, or that change under people
For Spritz / Teth, not worked around:
inspect_submission.pyexpect a loose DLL plussha256/sizeinecho-mod.json; they won't read this release until design items E2/E3 (Mia) land. Merge this when that is ready, or expect the "result" comment on submissions to fail.Nvr*":check_mod.pyrefuses any export outside theNvr*ABI (an error since52676c5, as Spritz asked; the test renames one export in a copy of the built DLL and failed before the change). The template now matches the signer on this one.SYSTEM_DLLSincheck_mod.pyplus anyapi-ms-win-*; a vendored copy in the signer will need that same list. The built DLL importsKERNEL32,SHELL32andapi-ms-win-crt-*.release.ymlbyte-equal to the template's": met by construction (nothing in the file is per-mod), but it ties every mod to this file's text; a later change to it needs a new pinned commit in the signer.package.pypacks whateverNOTICEcontains.mingw-w64andwinepackages are the runner's current Ubuntu 24.04 ones, unpinned. Rebuilds are byte-identical on one machine; a reviewer on another toolchain may differ.actions/attest-build-provenance@v2.4.0attestations satisfy the signer'sgh attestation verify --signer-workflowfor a two-job workflow (the attesting job isbuild, inrelease.yml, as §3 says).Quest: not applicable (PC-only template). No issue opened for this unit (the order named none).
Co-Authored-By: Andrew Bates a@sprock.io
Update 2026-10-28: main moved, brought up to date by merge
chore/standards(#1) now containsorigin/main(b53afe8, plugin API 7). Merged it into this branch (merge commit1802a59, a plain fast-forward push, no force-push). The text merge was clean; I checked beyond that:An
"api": 7build with-DECHO_MOD_ASSET_EXAMPLE=ON -DECHO_MOD_WERROR=ONandGITHUB_SHA=abab...ab(scratch build dir, manifest restored afterwards):So the identity literal survives an API 7 build with the example, and
check_mod.pyaccepts it.CI on
1802a59: build (push and PR) pass, PR-title check pass.Co-Authored-By: Andrew Bates a@sprock.io
Update 2026-10-28 (2): exports are an error, and release.yml ran once on GitHub
Exports.
check_mod.pynow errors on any export outside theNvr*ABI (52676c5). Failing-first:tests/test_check_mod.pyclassExportsrenamesNvrPluginShutdowntoXvrPluginShutdownin a copy of the built DLL (same length) and expects an error naming it.The built DLL and the Aaliyah-tablet DLL both still pass.
Release dry run (approved by Spritz). A throwaway signed commit
b88ac0aon top of52676c5(onlyecho-mod.jsonversionset to0.0.0-test.1; no branch, only the tag was pushed), tagv0.0.0-test.1. Run 40709349413:buildsuccess,draftsuccess. It made one draft prerelease with exactly three assets:ExampleMod.dll,LICENSE,NOTICE,ExampleMod.plugin.json,SHA256SUMS; flat, all dated 1980-01-01; the inner sums list the other four.echo-mod.json:package= the zip name,source= repository, repository_id, tag, commitb88ac0ac..., workflow.../release.yml@refs/tags/v0.0.0-test.1,prerelease: true, nosha256orsize.NEVR-PLUGIN-BUILD 0.0.0-test.1 b88ac0ac98cdceae57acf121e7b1622fe6f99ac3; noNEVR-BUILD.gh attestation verify <asset> --repo EchoTools/nevr-runtime-plugin-template --signer-workflow EchoTools/nevr-runtime-plugin-template/.github/workflows/release.yml --source-digest b88ac0ac...exit 0 for all three assets: signer.../release.yml@refs/tags/v0.0.0-test.1, source digestb88ac0ac..., refrefs/tags/v0.0.0-test.1.So item 7 below (two-job attestation vs
gh attestation verify) is now verified with that command. The signer's own invocation may differ; I haven't run the signer.Cleaned up in the same turn:
gh release delete v0.0.0-test.1 --cleanup-tag --yes; after itgh release listis[]andgit ls-remote --tags originis empty; branches untouched. Left behind (not deletable by me from here): the Actions run record and the three attestations, which stay in the attestation log.CI on
52676c5: build (push and PR) pass, PR-title check pass.Co-Authored-By: Andrew Bates a@sprock.io