Bound archive and checksum download retries, including refused connections, while retaining HTTPS and SHA-256 validation. Report missing checksum entries using an exact archive filename lookup. Add nine real-curl regression tests for recovery, permanent failures, retry bounds and checksum validation.
buildx pushes a by-digest output to one repository per invocation, so with two
imagerepositories only the last one received the manifest and the merge job failed
with "not found". Loop over the image list and assert the digests agree.
Builds every platform in `platforms` by digest into each repository in `images`,
then joins those digests into one multi-arch manifest per repository, logging in
to DOCR, GHCR or dhi.io only when an image needs it. It carries the inputs both
craft web images need: a custom Dockerfile and stage, build args, a GHA layer
cache, preinstalled-SDK pruning, and a hook for preparing large build contexts
such as the font sources.
Add allow-published (default false) to draft-release. When true, draft-release
permits uploading assets to an existing published release without failing or
attempting to convert it to a draft with --draft.
* release.yml: export each app's build variables from its env.sh, not a list
Eight release jobs set <APP>_VERSION, <APP>_BUILD_DATE and <APP>_BUILD_SHA for
every app by name (298 lines), and the FreeBSD VM step listed them all again,
so every new app meant editing this workflow. materialcraft and texturecraft
were never added: their packaging reads MATERIALCRAFT_* and TEXTURECRAFT_*,
so their release builds got no release version, date or commit, and a
dispatched run with a version override packaged them under Cargo.toml's.
actions/export-app-build-env reads the prefix the app's packaging/env.sh (or
packaging/common.sh) already declares in VERSION="${<PREFIX>_VERSION:-...}",
falling back to app-name in capitals, and writes the three variables to
GITHUB_ENV; its names output feeds the FreeBSD VM's envs. Each job runs it
right after checkout. On all 17 apps' env.sh it finds the prefix the old
list used.
Tests run the script on sample env.sh files and check release.yml sets no
app's variable by name, so the list can't come back.
* Export the build variables after the contributor snapshot, which must follow the checkout
test_contributor_refresh requires each build job to load the contributor snapshot directly after checkout, so the export step goes after it when a job has one. Also fix the line-break test passing a variable twice.
---------
Co-authored-by: nnaannoo <n4nosaur@gmail.com>
The path is resolved and has to stay inside the named directory. The existing file's text is written back as text, so the path-traversal finding on that write is suppressed.
Co-authored-by: Cursor <cursoragent@cursor.com>
A checkout path and a recorded commit have to match a fixed pattern, and the standards path has to be one of the two known files. The per-block check is its own function.
Co-authored-by: Cursor <cursoragent@cursor.com>
The writer and the checker share one normalization. A rewritten digest does not make an edit pass, an intact older copy warns only when main has moved on, and a source that cannot be read is reported as could not check.
Co-authored-by: Cursor <cursoragent@cursor.com>
The runner's amd64 mirror (azure.archive.ubuntu.com) lags ports.ubuntu.com, and
Multi-Arch: same packages must be the same version on both architectures. When
ports publishes a linux-libc-dev before the mirror does, apt cannot install
libc6-dev:riscv64 and reports a dependency that "is not going to be installed",
which fails the riscv64 build until the mirrors happen to catch up.
Point amd64 at archive.ubuntu.com for main and updates as the action already
does for security, so both architectures always offer matching versions.
Eight apps paste the same 25-line job into ci.yml, nix.yml or release.yml (ten
copies): Cargo.lock exists and is up to date, no .cargo/config(.toml)
replaces, patches or overrides a source, and every git dependency comes from
github.com/storytold. Apps' workflows are meant to only call workflows here.
actions/check-git-sources holds the check as a tested script, and
git-sources.yml runs it on a credential-less checkout, so an app swaps its
job for one `uses:` line and keeps `needs: git-sources`. It reports every
problem rather than the first, as GitHub error annotations, and treats
unreadable TOML as a failure. On all 18 apps' current Cargo.lock and Cargo
config it agrees with the inline copy.
Co-authored-by: Kristian Myram <kmy@oresundsbron.com>
The release job ran each app's packaging/linux/flatpak-oci.sh --push. Most
apps still carry the copy from before scripts/scaffold-flatpak-oci.sh learnt
to guard the stable tag, and it moves stable-<arch> on every push, so a
prerelease or a test run dispatched from any branch became what stable-<arch>
clients install. 15 of the 17 apps carry that copy.
Call the script without --push, which only builds the layout in every copy,
and push from the workflow: <version>-<arch> on every non-PR run, and
stable-<arch> only for a stable version built from the release branch, the
rule the scaffold template already uses. No app needs to change.
Co-authored-by: Kristian Myram <kmy@oresundsbron.com>
* Add the setup-windows-signing action: the shared Authenticode signing script
Every app carries its own packaging/windows/sign.ps1 and the copies have drifted: four lack the
Trusted Signing client SHA-256 pin. The action exports the maintained script, parameterized by
product name and URL, for package.ps1 to call; it rejects line breaks that could inject
environment variables, and refuses an unset product name. The script reads file names literally.
Tests run it against a stand-in signtool.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013fTnVxnXTv2MBW6uj2PPyU
* Test that an unset product name gets the clear message, not a null-method error
PowerShell echoes the failing source line, so the message alone passed on the old code too.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013fTnVxnXTv2MBW6uj2PPyU
* Refuse a non-x64 signtool for Azure Trusted Signing
The Azure branch loads the x64 Azure.CodeSigning.Dlib.dll, but SIGNTOOL or
PATH can name an x86 or ARM64 signtool.exe, which cannot load it. Read the
PE machine type of the chosen signtool and fail with a clear message before
downloading the client. A file that is not a PE image is left alone.
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Kristian Myram <kmy@oresundsbron.com>
* Add shared macOS certificate import and Windows signing script actions
Every app carries its own packaging/macos/import-cert.sh and packaging/windows/sign.ps1, and the
copies have drifted: four apps lack the Trusted Signing client SHA-256 pin and eight still write the
signing certificate to a predictable temporary path.
- import-macos-certificate: the hardened import (private mktemp -d directory, keychain removed on
failure), self-contained, with identity/keychain outputs.
- setup-windows-signing: exports the shared sign.ps1, parameterized by product name and URL, for
package.ps1 to call; rejects line breaks that could inject environment variables.
- Tests run both scripts against stand-ins for security, openssl and signtool.
Existing callers are unchanged; apps adopt the actions one at a time.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Signing scripts: read file names literally, refuse an unset product name clearly
sign.ps1 resolved its file arguments with Resolve-Path, which treats [ and ] as wildcards, so a
file such as 'App [x64].msi' failed. setup-windows-signing called .Trim() on an unset product
name, a null-valued method call, instead of its own message. Tests for both.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013fTnVxnXTv2MBW6uj2PPyU
* Split the signing PR: keep only the macOS certificate import here
The Windows signing action moves to its own branch and PR. The tests, README text and bullet for
each action now live with that action, with separate README headings so the two PRs merge
independently.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013fTnVxnXTv2MBW6uj2PPyU
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* Add changed-paths.yml: run a job only when its paths change or a label asks
* changed-paths: read paths NUL-separated, keep the reason on one line; README: what the checkout holds
Anyone can commit under an agent email, so a free-form Git name such as
"Private Person <noreply@anthropic.com>" became a published model title,
which the apps would ship in their About windows. Each provider now lists
the words a model title may use. A name with any other word credits the
agent's default model and repeats none of it; versions are bounded too.
Across the co-author trailers in the 13 apps' history, 18,099 keep their
model row and 7 unusual names now fall back to the default. Also pin that
the PR lookup reads the merge commit rather than the PR head.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
effectcraft's 4,000-commit history made 50-commit association queries time
out on GitHub, so batches are smaller and a failed batch is retried once.
GraphQL names bot openers without the [bot] suffix, which turned dependabot
into a person; only User accounts now count as PR openers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GitHub links agent trailers such as noreply@anthropic.com to the claude and
codex accounts, so they appeared as the top human contributors. Agents are now
recognized by exact Git email or GitHub account, never by name, and credited
only in the snapshot's models (About > Models), including commits an agent
authored itself. Bots are no longer listed as people. Model and version are
read from the agent's Git name, covering every provider the apps' history
uses.
Commits merged with a merge commit or rebase never equal a PR's merge commit,
so self-added names lost verification and unlinked commits lost their author.
GitHub's commit-to-PR association now supplies the opener of the one merged PR
containing the commit. An integration PR opened by a writer that carries the
person's own linked commit also verifies their entry.
Against the shipped snapshots of effectcraft, pdfcraft, photocraft and
lightcraft at their recorded heads, no person loses a row, a name or a commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The paths input is checked to stay inside the checkout, but its original
spelling was then passed to ast-grep where option parsing still applied, so
a checked file named like an option (--update-all, --follow) changed how the
scan ran. Pass "--" before the path list in both scan invocations.