feat(release)!: releases on Forgejo, signed by one key; tools.lock; Forgejo Actions only #13
No reviewers
Labels
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
needs measurements
question
testing wanted
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!13
Loading…
Reference in a new issue
No description provided.
Delete branch "HumanBean/Longshot:feat/forgejo-releases"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why
Longshot can't release anything yet: its release system, update checks and CI still point at GitHub, at a repository that isn't ours. This PR replaces all of that with plain Forgejo releases signed by one key, so the Longshot Launcher (MagicBeanDev/LongshotLauncher#1) can install and update the builder safely.
What changed
The design is in docs/releases.md (with how the launcher's setup exe installs itself or runs portable); the maintainer's step-by-step guide is the new RELEASING.md. Both, with
scripts/release.py,tests/test_release.pyandcliff.toml, are byte-identical in the launcher's repository.A release is a Forgejo release, with no generic packages, pointer files or serials. Each version ships:
LongshotBuilder-<v>-windows-x64.zip: the setup asLongshotBuilder.exe, the builder and Longshot's sources,tools.lock,VERSION, andrecomplauncher.jsonat the top;Longshot-<v>-x86_64.AppImage, withrecomplauncher.jsoninside;tools.lock;SHA256SUMSandSHA256SUMS.sig.How CI publishes (
.forgejo/workflows/release.yml): the first job checkstools.lock(tools.py check --require) and the tag, before any build. Then both builders are built, and the release is created as a draft, every file uploaded, and published last. Nightly runs then prune the nightlies past the newest 5.Channels come from the release list (
scripts/release.pyreleases()reads every release, page by page until an empty page, drafts and pre-releases told apart by their fields;newest()):vX.Y.Z;vX.Y.Z-rc.NorvX.Y.Z;nightly-<n>-<hash>, counted from the fork commite2a4d45.Pulling a version means deleting it or setting it back to a draft; its channel then falls back by itself.
No bot account or release token. CI makes the release with the job's own token (
forgejo.token, passed torelease.pyasRELEASE_TOKEN, as in the launcher'srelease.yml): on a push or tag in this repository Forgejo 17 gives it write access to this repository only, which covers every callrelease.pymakes.tools.pyrun by hand takes the maintainer's own token asRELEASE_TOKEN.One key. CI signs
SHA256SUMSwith theRELEASE_SIGNING_KEYsecret (ECDSA P-256, via openssl).scripts/release_keys.jsonlists the public key the launcher trusts (id5e18330371d7f5aa).keys_from_cs()reads the launcher'sCore/ReleaseTrust.cswith its comments left out, inside theBuiltIninitializer's braces.tools.lockreplaces the builder parts. It is a line per tool: name, version, system, sha256, size, dest, prefix, and URLs: the copy in Longshot's long-livedtoolsrelease first, then where it came from (upstream, or NotRexed/Hookshot'sbuilder-parts) as the fallback.builder/setup/setup.cand the launcher download each missing tool, verify it, share it across versions, and unpack its prefix as its dest.scripts/tools.pyhascheck,fetch,install,mirror(puts the copy first),add, andassemble python|cpm|third-party|profile.Tags, not release PRs; no release-please. A maintainer pushes
vX.Y.Z-rc.NorvX.Y.Z, or uses Create tag in the web UI.scripts/release.py check-tagrefuses: a tag that is not on main, not above every published release, or lower than the bump the commits since the last stable call for (feat → minor, fix/perf → patch,!orBREAKING CHANGE:→ major).cliff.toml, version pinned and checked by SHA-256). They go on the release page; nothing is committed back.scripts/release.py nextprints the version and the notes locally.Removed:
release_parts.py(itspackagestep is nowscripts/builder_package.py),make_setup_parts.py,release_parts.py previous, and the seed list;release-channelspointers, serials, and the nightly/release key split with offline signing;tests/test_release_cpm_add.pyandtests/test_release_prompt_packages.py(tests/test_tools_lock.pycovers that code now), andtests/test_updater_asset_digest.pywith its fixture (they tested the updater's downloads, whichUpdater.csno longer has);.github/(onlydependabot.ymlwas left).scripts/linux/make_parts.py. The Linux builder's local-list installer (LONGSHOT_LINUX_LIST) and its tests use it, but nothing in the release produces parts any more.The builders don't update themselves.
host/hos_update.cis deleted. The WPF, Linux and Python builders and the setup look up no release.RecompLauncher:
recomplauncher.jsonships in both builders.tools.lockbeside it, with no release lookup.CI is Forgejo Actions only.
source-regressions.ymlis nowci.yml, and the PR title check is a job inside it; it takes the WIP prefix off with #48'sstrip_wip.release.ymlcallsbuild-linux.ymlandbuild-windows.yml(formerlylinux-appimage.ymlandwindows-builder.yml). Every action is pinned by commit, Forgejo's upload/download-artifact too.vulkan-dlss.yml,third-party.yml, and #48'spr-description.ymlandpull_request_template.md.Kept from the previous rounds: the third_party check and the corrected notes;
hardware_report.pyported to the Forgejo API; noNotRexed/Longshotreferences.Docs updated:
docs/versioning.md(the bump rule, no release-please), CONTRIBUTING's Pull request titles, the README's install, update and network sections,docs/forgejo-runners.md(the workflow names and the Cloudflare upload limit).Rebased on main after #48 (
d725b363).Forgejo limits, checked on git.magicbean.dev (Forgejo 17.0.1):
[attachment] MAX_SIZEis 2048 MB; Cloudflare in front cuts uploads at 100 MB, so CI uploads toFORGEJO_UPLOAD_API(the instance without Cloudflare) when it is set;[repository.release] ALLOWED_TYPESmust stay empty;GET /repos/{owner}/{repo}/releasesgives at most 50 per page (max_response_items), and itsdraft=/pre-release=parameters are filters. All in RELEASING.md, One-time setup.What
tools.lockholds now (each line's first URL is thetoolsrelease, which is still to fill):llvm-mingw20261104python3.14 (with the prompt packages)builder-parts)emsdkcpmwgpu-native29.0.1.1Still to put in the
toolsrelease before the first release:tools.py mirrorthe five above, andassemble+addcpmwith dav1d,third-party(the prebuilt a64emit.dll, lld-link.exe, SDL2/SDL3 and zbic.dll only) andprofile(the play profile).Flags and settings
LONGSHOT_LAUNCHER_TRUSTRELEASE_SIGNING_KEY(secret),FORGEJO_UPLOAD_API(variable) andRELEASE_TOKEN(the job token, or a maintainer's own fortools.pyby hand) are CI's and the scripts', in RELEASING.md, One-time setup.Measurements
No game or GPU behaviour changes. The measurements are the test suites and the builds.
main:
d725b363(2026-11-13)this PR: feat/forgejo-releases @
5171786fHardware: Linux (CachyOS), Python 3.14, .NET 10 (
~/.dotnet)Scene: none (game-free)
Method:
LONGSHOT_VULKAN_HEADERS=/usr/include python -I -B tests/run_all.py, 2 interleaved runs each (main, PR, main, PR);tests/test_release.pyagainst the code before the fix; the WPF builder withdotnet build -p:EnableWindowsTargeting=trueHow to reproduce: the commands above, in a checkout of each commit
d725b3635171786ftests/run_all.py, run 1tests/run_all.py, run 2test_bake_portability(no zstandard),test_builder_log_read_only(no Tk),test_updater_asset_digest(built for net8.0, no .NET 8 runtime here)test_bake_portability,test_builder_log_read_only(the same two)tests/test_release.pyrelease.pywith the new tests, at22a280fa)tests/test_tools_lock.pydotnet buildLongshotBuilderTesting
Tested: on Linux (this PC), the game-free checks (
tests/run_all.py, interleaved against main, in Measurements);tests/test_release.pyagainst a fake Forgejo that filtersdraft,pre-release,limitandpageas Forgejo 17 does (publish, prune, check-tag, paging);dotnet buildof the Windows builder (0 warnings, 0 errors);builder/setup/setup.cwith llvm-mingw-Wall -Werror;release.py sums/sign/verifywith the real release key on real packaged files (in LongshotLauncher#1). After the rebase onto main @ef8b784f(#39, #72; no conflicts), on the merged tree: 489 passed, 3 failed, 36 skipped: the two known ones, plus "byte-compile every tracked .py", which needs a git checkout (the tree was extracted, "not a git repository"); every.pyin the tree parses. The new Forgejo runner (unraid-linux-1) runs LongshotLauncher#1's CI green, with the same job image as these workflows. Tested but not measured: nothing.Not tested: a workflow run on Longshot (Actions is off on this repo until this merges; the workflows were parsed, not run); a real release,
publishandpruneagainst git.magicbean.dev; thetoolsrelease (not filled yet:release.ymlrefuses to publish until it is); a game build.Needs other testers:
v3.0.0-rc.1(installs its tools fromtools.lock, then builds the game), expecting a working game folder and no release lookup (request to open once rc.1 is built)Screenshots
Nothing on screen. The release page is Forgejo's own.
Risk and rollback
Until the
toolsrelease is filled, every push to main stops inrelease.yml's first job (by design: no nightly without the tools). A bad release is pulled by deleting it or setting it back to a draft; the channel falls back by itself. The PR as a whole is reverted by reverting its commits; nothing it publishes is committed back to the repository.Reviewer notes
Start with docs/releases.md, then
scripts/release.py(releases,check_tag,publish,prune,keys_from_cs) and.forgejo/workflows/release.yml. The shared files must stay byte-identical with LongshotLauncher#1. Left for later on purpose: release.py'sTITLEtakes any scope where forge.py's takes[\w./-]+(no effect on the bump).Issues
See MagicBeanDev/LongshotLauncher#1 (the launcher's side).
Checklist
For everyone
type(scope): description).main.python -I -B tests/run_all.pyrun and passing (any failure that also fails onmainis named above).third_party/untouched (a new folder with aPROVENANCE.mdinstead). Only the rename's edits are undone, and the notes are corrected.If applicable
Assisted-by:/Co-Authored-By:trailers. None in the commits, at the maintainer's request.docs/tested-configurations.mdfor each measured run. No game run.MKWVK_API_VERSIONbumped. Not changed.mainand passing here.tests/that reads changed source updated in this PR, not weakened (test_setup_tar_diagnostics,test_ship_state_list,test_recomplauncher_json,test_tools_lock).Manual steps (none done)
5e18330371d7f5aa, inscripts/release_keys.jsonand the launcher'sCore/ReleaseTrust.cs).RELEASE_SIGNING_KEY(the private PEM). No token: the job's own is used. Leavenightly-*tags unprotected.FORGEJO_UPLOAD_API, the API address that bypasses Cloudflare.app.ini:[attachment] MAX_SIZEat least the AppImage's size, and[repository.release] ALLOWED_TYPESleft empty.docs/forgejo-runners.md. Longshot needs Linux and Windows.toolsrelease:tools.py mirrorthe five tools, thenassemble+addcpm(+dav1d),third-partyandprofile;tools.py check --requiremust pass.v3.0.0-rc.1, thenv3.0.0.87d2c59a6fto9d2989d6b59d2989d6b5toeda1e14081feat(release)!: signed releases on git.magicbean.dev, and the launcher updating from themto feat(release)!: signed builder releases on git.magicbean.dev, Forgejo Actions onlyeda1e14081toa5fd9c088cfeat(release)!: signed builder releases on git.magicbean.dev, Forgejo Actions onlyto feat(release)!: releases on Forgejo, signed by one key; tools.lock; Forgejo Actions onlyHumanBean referenced this pull request from MagicBeanDev/LongshotLauncher2026-11-13 00:13:26 +00:00
a5fd9c088ctof327d1018bPushed
f327d101: the PR title check in .forgejo/workflows/ci.yml now strips both of Forgejo's work-in-progress prefixes (WIP:and[WIP], any case) before checking the Conventional Commit, not onlyWIP:, so a[WIP] fix: ...title passes like aWIP: fix: ...one (#48 opens PRs as WIP).Pushed
a82a2230: the release key is inscripts/release_keys.json(ECDSA P-256, key id5e18330371d7f5aa). Its private half is the organization secretRELEASE_SIGNING_KEY(set 2026-11-13). The launcher has the same key built in (LongshotLauncher#1).tests/test_release.pypasses.Reviewed the code at
a82a2230. The design holds up and the signing contract with the launcher matches. Two bugs need fixing before the first release, though, and one test no longer compiles.Bugs
releases()sendsdraft=true, so it only ever sees drafts (scripts/release.py:396). In Forgejo 17,draftis a filter (is_draft = value), not "include drafts".?draft=trueon codeberg's forgejo/forgejo returns[], and without it the published releases come back. A writer token already sees drafts without the flag. As written:check-tag's "above every release" check is empty, becauseexisting(non-drafts of a drafts-only list) is always[];prunenever deletes a published nightly;publishnever fires;channelsandnextsee nothing.The fake Forgejo in test_release.py ignores the parameter, so the tests pass anyway. Fix: drop
draft=true. Also makepruneignore a 404 on the tag delete: a draft nightly left by a failed run has no tag yet.keys_from_csreads no key from the launcher's ReleaseTrust.cs as it now is (scripts/release.py:311). It cuts the text at the first;afterBuiltIn =. The new comment line,... keygen, 2026-11-13; RELEASING.md ..., has a;before theFromSpkiline. The result is[], so the launcher's release workflow stops atverify("no key to check with"). This file is shared, so the fix belongs here and in LongshotLauncher#1. Either cut at};, or drop the//comments before the search.tests/test_updater_asset_digest.py and tests/updater_asset_digest_test.cs test the old updater. The fixture calls
Updater.RunLatestSetup,UpdateFolder(w, folder, tag, fetched:)andDownloadOnce, which the 34-line Updater.cs no longer has. With a .NET SDK present (ci.yml installs one), the check can't compile. Delete both, or rewrite them for what is left.Worth changing
tools.py check --requireruns in the publish job, after both builds. The lock has nothird-partyorprofileyet, so every push to main builds Windows and Linux in full, then fails. Moving the check into theversionjob fails fast with the same message.builder-partsrelease. Their second URL, thetoolsrelease, doesn't exist yet. Untiltools.py mirrorhas run, one repository we don't control is the only source, for every Windows install. These are Hookshot's own archives, not upstream, so put the forge copy first once it is mirrored, and make mirroring part of the first-release checklist.Nits
download-artifact@v4andupload-artifact@v4(data.forgejo.org) are pinned by tag; every other action is pinned by SHA.strip_wipin check_pr_description.py (#48), and forge.py already imports it, so use that one. Otherwise ci.yml and #48 don't conflict: #13 merges cleanly ontod725b363, and the title job readsforge.TITLE/TITLE_TYPES, which main has.TITLEtakes any scope ([^)]*); forge.py's takes[\w./-]+. Not a bug, but the bump rule and the title check can drift apart..github/dependabot.ymlis GitHub-only.Checked and fine:
push(nopull_request_target);nightly-<n>-<hash>, so it never touches stables ortools;concurrencyon the ref, without cancelling;NotRexed/Longshotor release-please left in code.Works with the launcher (MagicBeanDev/LongshotLauncher#1)
Compared field by field:
release_keys.jsonandReleaseTrust.cshold the same SPKI, a P-256 key with id5e18330371d7f5aa. The launcher's 8-byte hex id equalskey_id's 16 hex.openssl dgst -sha256 -signwrites DER over the exact bytes of SHA256SUMS. The launcher checks it withVerifyData(..., SHA256, Rfc3279DerSequence)on those same bytes.<hex> <name>lines are written and read by the same regex.LongshotBuilder-<v>-windows-x64.zip,Longshot-<v>-x86_64.AppImage,tools.lock,SHA256SUMS,SHA256SUMS.sig) matchBuilderFile. The channel rules matchReleaseManifest.Newest: stable needs!prerelease, andtoolssits on no channel.<prefix>folder.LongshotBuilder/and the launcher runsLongshotBuilder/LongshotBuilder.exe. It then startstools/python/python.exeontools/nx2/make_game.py, which builder_package.py and the lock's dests provide. The AppImage'susr/share/longshotandusr/pythonare where Toolchain looks.The mismatches are bug 2 (the key parser breaks on the launcher's own file) and bug 1 (it also breaks check-tag and prune in the launcher's release workflow, since the file is shared). Both are fixed in release.py, in both repos at once.
@ -0,0 +102,4 @@run: |git fetch -q --tags origin# (every tool the Windows builder needs is in tools.lock: RELEASING.md, One-time setup)python3 -I scripts/tools.py check --requireThis fails every nightly until
third-partyandprofileare in the lock, but only after both full builds. Running it in theversionjob fails before any build.@ -0,0 +308,4 @@def keys_from_cs(text):"""[spki] of the launcher's ReleaseKeys.BuiltIn: the ReleaseKey.FromSpki("...") entries of its initializer"""start = text.index("BuiltIn =")return [base64.b64decode(k) for k in re.findall(r'ReleaseKey\.FromSpki\("([A-Za-z0-9+/=]+)"\)', text[start:text.index(";", start)])]Cuts at the first
;. The launcher's new comment above the key (2026-11-13; RELEASING.md) has one, so this returns[]there and the launcher'sverifystep fails. Cut at};, or skip//lines.@ -0,0 +393,4 @@def releases(repo):out, page = [], 1while True:items = api("GET", "/repos/%s/releases?draft=true&limit=50&page=%d" % (repo, page)) or []draft=truefilters to drafts only on Forgejo 17 (cond.And(builder.Eq{"is_draft": value})). Drop it: a writer token gets drafts and published releases together. As written, check-tag's 'above every release', prune and the already-published guard see nothing.@ -0,0 +13,4 @@# Taken as they are from Hookshot v2.1.0's Windows builder (NotRexed/Hookshot, release builder-parts), until# Longshot assembles its own: python, emsdk, cpm and wgpu-native.tool llvm-mingw 20261104 windows-x64 552191b5754c49a5a20723276a6fc56541ea1922c4c50b7b669022333fc1b40f 191037046 LongshotBuilder/tools/llvm-mingw llvm-mingw-20261104-ucrt-x86_64 https://github.com/mstorsjo/llvm-mingw/releases/download/20261104/llvm-mingw-20261104-ucrt-x86_64.zip https://git.magicbean.dev/MagicBeanDev/Longshot/releases/download/tools/552191b5754c49a5a20723276a6fc56541ea1922c4c50b7b669022333fc1b40f-llvm-mingw.ziptool python 3.14-hookshot-v2.1.0 windows-x64 c0eb2e3fbabae82cb7d3dc61ec4b0e9b4b854269d9c38c3bbcc5a08e4497bd12 55961280 LongshotBuilder/tools/python HookshotBuilder/tools/python https://github.com/NotRexed/Hookshot/releases/download/builder-parts/HookshotBuilder-python-c0eb2e3fbaba.zip https://git.magicbean.dev/MagicBeanDev/Longshot/releases/download/tools/c0eb2e3fbabae82cb7d3dc61ec4b0e9b4b854269d9c38c3bbcc5a08e4497bd12-python.zipThe first URL is a repository we don't control, and the second doesn't exist yet (no
toolsrelease). Mirror before the first release, and once mirrored, put the forge copy first for these four: they are Hookshot's archives, not upstream.a82a2230beto22a280fab5Review fixes (HumanBean's review at
a82a2230), rebased on main @d725b363(#48; itspr-description.ymlandpull_request_template.mdkept). Four new commits:22a280fafix(release): list every release, page by page; read the launcher's keys past its comments:scripts/release.pyandtests/test_release.pyonly.c9a3b0dcfix(release): the tools check before any build, the forge's copies first; review nits.2b8af4ecfix(release): releases with the job's own token, no RELEASE_TOKEN or bot account(the maintainer's request, after the review).5171786fdocs(release): the launcher's setup exe and portable zip; the job token as RELEASE_TOKEN: the shared docs follow LongshotLauncher#1 (LongshotLauncher-<v>-windows-x64-setup.exeand-portable.zip, and a section on how the launcher installs itself: Install / Run portable,--install,--uninstall, theportablemarker, Add to applications menu,<AppImage>.home), and the env var isRELEASE_TOKENagain, the name the launcher's workflow uses.Bugs
releases()anddraft=true. It now sends nodraft=/pre-release=filter (on Forgejo 17 they keep only the releases whose field equals the value) and reads every page (limit=50&page=Nuntil an empty page, a page repeated by the server stops it too);draftandprereleasecome from each release.prunedeletes the published nightlies past the newest 5, and drafts of nightlies older than the newest published one (left by failed runs), and takes a 404 on a draft's missing tag. Never a stable, an rc ortools. The fake Forgejo intest_release.pynow does what Forgejo'sListReleasesdoes: drafts only with a token,draft=/pre-release=as filters,limit(default 30, at mostmax_response_items) andpage, newest first, and a 404 on a tag that isn't there. New tests: check-tag against published releases (refused below a publishedv3.0.0, allowed once it is a draft again), prune over published and draft nightlies plustools/stable/rc/2.x (only nightlies go), every release over 3 pages at a 20-item server limit, and publish's "already published" guard for a tag on a later page. On the oldrelease.py6 of 18 tests fail; on the new one all 18 pass.keys_from_cs. It blanks//and/* */comments first (strings kept), findsBuiltIn ='s initializer{, reads to its matching}, and takes theReleaseKey.FromSpki("…")entries inside. Test: comments with;,{and}before, inside and after the initializer, a commented-out key, and aFromSpkiin a string outside it.tests/test_updater_asset_digest.pyandtests/updater_asset_digest_test.csdeleted (and theirrun_all_times.jsonentries). They tested the updater's downloads (RunLatestSetup,DownloadOnce, the asset digests), andUpdater.csno longer downloads anything: there is nothing of it left for them to test. The builder builds (dotnet build -p:EnableWindowsTargeting=true, 0 warnings).Worth changing
release.yml's first job (version, before the tag check and any build). It fails withthe tools release is missing: third-party, profile (…); RELEASING.md, One-time setupand an::error::line. The publish job no longer runs it. RELEASING.md's troubleshooting row is moved to The version.tools.lock: thetoolsrelease URL is first for all five tools, the upstream (llvm-mingw) or NotRexed/Hookshotbuilder-partsURL the fallback, sha256s and sizes unchanged.tools.py mirrornow puts the copy first.test_tools_lockchecks that every line's first URL is…/releases/download/tools/<sha256>-<name>.zip. RELEASING.md, One-time setup step 6: thetoolsrelease must be filled before the first release (tools.py mirrorthe five,assemble+addcpm, third-party, profile); docs/releases.md says the same.Nits
forgejo/upload-artifactpinned to16871d9eanddownload-artifacttod8d0a990: the commitsv4points to now (4.3.1 and 4.1.4 with the GHES check off), so nothing else changes.forge.desc.strip_wip(scripts/check_pr_description.py, which forge.py imports); the sparse checkout is the wholescripts/folder, so nothing to add. Checked with[WIP] feat(release): x,WIP: fix: yandnope.host/hos_update.ccomments removed fromRunner.cs,core.py(and one intests/test_linux_update.py)..github/dependabot.ymldeleted, so.github/is gone (the testing skill's.github/is now.forgejo/).TITLEscope vs forge.py's: left as it is (no effect on the bump), noted in the description.The job's token instead of a
RELEASE_TOKENsecret and a bot (2b8af4ec). From Forgejo 17.0.1's source: the task token authenticates as the Actions user (services/auth/method/action_task_token.go),reqTokenaccepts it, androuters/api/v1/permissions/repo_access.gogives itAccessModeWriteon every unit of the task's own repository (read for a fork's pull request, 404 for any other repository). Every endpointrelease.pycalls needs onlyreqRepoWriter(TypeReleases)(create, edit/publish, delete a release, upload and delete assets) orreqRepoWriter(TypeCode)(delete a tag), so the job token covers all of it, no fallback needed. The one limit: a protected tag can't be deleted by it (422), so RELEASING.md says to leavenightly-*unprotected.permissions:isn't set: Forgejo 17 ignores the key and shows a warning (calculateWarningsinservices/actions/notifier.go).release.ymlpassesforgejo.tokenas the env varRELEASE_TOKEN(no secret of that name any more);release.pyandtools.pyreadRELEASE_TOKEN(a maintainer's own token fortools.py mirror/addby hand). RELEASING.md's one-time setup now has one secret,RELEASE_SIGNING_KEY; docs/releases.md says what the token may do.Shared files.
scripts/release.py,tests/test_release.py(22a280fa),RELEASING.mdanddocs/releases.md(c9a3b0dc) changed (and again in2b8af4ecand5171786f); LongshotLauncher#1 takes all five shared files as they are at5171786f.cliff.tomlis unchanged.Tests (
LONGSHOT_VULKAN_HEADERS=/usr/include python -I -B tests/run_all.py, interleaved main, PR, main, PR):d725b3635171786ftests/run_all.py, run 1tests/run_all.py, run 2test_bake_portability(no zstandard),test_builder_log_read_only(no Tk),test_updater_asset_digest(built for net8.0, no .NET 8 runtime here)test_bake_portability,test_builder_log_read_only(the same two)tests/test_release.pyrelease.pywith the new tests, at22a280fa)tests/test_tools_lock.pydotnet buildLongshotBuilder5171786f02toa2f2accc84a2f2accc84toffc0dfa0b0Rebased onto main @
ef8b784f(#39 and #72; no conflicts), nowffc0dfa0, and every commit is authored HumanBean (two still said hckrman101). The description follows #72: a Testing section (Tested / Not tested / Needs other testers) replaces Not tested, and the For everyone box "Testing filled in" is ticked; main'scheck_pr_description.pypasses it, with one warning: the Windows test request opens oncev3.0.0-rc.1exists. On the merged treerun_all.py: 489 passed, 3 failed, 36 skipped: the two known failures, plus the byte-compile check, which needs a git checkout (every.pyparses).