Revision history for App::Ariza
0.0.2 2026-08-12T03:02:19+01:00
- Every Windows CI failure across the suite's first two runs on a
real Windows machine was a genuine portability bug rather than a
test being fussy, and four of them were in lib/. All are fixed
below; the fixes are fixture-tested from every platform.
- `sha256-file` now falls through instead of stopping at the first
tool that disappoints it. sha256sum, `shasum -a 256` and
`certutil -hashfile` are each tried in turn, and absence, an
unspawnable binary, a non-zero exit and output that holds no digest
are all "try the next one"; only an exhausted list dies, naming
what every attempt did. The Windows failure this fixes: a GitHub
runner has a `shasum` on PATH that is a Perl script with no
interpreter association, so `where` finds it and CreateProcess
refuses it -- which took down every digest on the machine, and with
it t/08, t/09 and t/11 mid-file, while certutil sat untried behind
it. certutil stays last because Linux distributions ship an
unrelated NSS tool of that name; being last means its refusal is
simply one more failed attempt. There is still no "skip
verification" path. `sha256-file` takes the usual `:&run` seam now,
so the output parsers -- including certutil's banner, its
upper-case hex and the spaced-out byte pairs older builds print --
are covered by unit tests on machines that have never seen one.
- `resource-list` returns forward-slash paths on every platform. In a
checkout it walks the filesystem, and `.absolute` speaks the native
separator, so Windows was handing back `templates\ci\...` for what
is a META6 `resources` key -- the string that gets fed back through
`resource` and compared against the manifest, and forward-slash by
the packaging spec. Normalised at the one seam where the filesystem
is consulted, so a checkout and an installed run agree.
- New .gitattributes declaring `* -text`. The golden files in t/ are
compared byte for byte, read with `slurp(:bin)` precisely so a
line-ending difference cannot be shrugged off; Windows CI images
set `core.autocrlf=true`, which rewrote every LF golden on checkout
and failed t/12, t/15 and t/16 against output that was rendered
correctly. `-text` turns conversion off in both directions, which
also preserves the goldens that are deliberately CRLF -- the
Windows launcher twins and the PowerShell installers, which exist
because cmd.exe mis-parses an LF-only batch file. The templates
those goldens are rendered from are covered by the same rule: a
template rewritten on checkout would silently change what ariza
ships. The three golden comparisons now also diag when the only
difference is CRLF, so a stale working tree says so instead of
printing two identical-looking scripts.
- Test fixtures that spoke POSIX now speak whatever the platform
does. t/01 and t/09 and t/12 normalise the separator before
comparing a relative path against the bundle-internal spelling it
is asserting about; t/14 derives its expected environment values
from the same IO::Path it hands the code under test, since a bare
`/b` acquires a drive letter on Windows and the assertion is that
the environment names the bundle root rather than that it spells it
the way macOS would; t/08 writes its digest fixtures as bytes,
because a digest is a statement about exact bytes and a text write
is entitled to opinions about line endings.
- `sha256-file` no longer asks any tool to hash an empty file; the
SHA-256 of empty input is a constant, and both certutil and
sha256sum answer it badly. certutil cannot map a zero-byte file at
all and fails outright (`ERROR_FILE_INVALID`, a documented quirk of
the tool rather than a transient), and sha256sum, given a Windows
path (backslash separators), switches to GNU coreutils' "escaped
output" format, fusing a leading backslash onto the digest -- which
a parser anchoring the hex at the start of the line then misses
entirely, reading "exit 0, but no digest in its output". The digest
parser is fixed too, independent of the short-circuit: it now takes
the first 64-character run of lowercase hex anywhere on the line,
so a leading backslash simply isn't part of the match.
- The Linux ELF audit's containment check normalises both sides of
the comparison to forward slashes before deciding whether a
resolved dependency lands inside the bundle. `ldd`-style resolution
hands back a target spelled in the host's native separators, while
the `:@inside` bundle-root prefixes may not share that spelling --
and on Windows CI a bundled `libcrypto.so.3` resolving to a path
genuinely inside `native/sqlcipher` was flagged as a stray purely
because one side had backslashes and the other did not.
Containment is a path-identity question, not a byte question.
0.0.1 2026-08-12T02:13:21+01:00
- The Windows release lane caches SQLCipher instead of building it
from vcpkg on every run. `actions/cache/restore@v4` restores
`C:\vcpkg\installed` under the key `vcpkg-sqlcipher-x64-windows-v1`
before the build, and the build step itself is gated on the miss.
The save is a separate `actions/cache/save@v4` step immediately
after the build, not the combined `actions/cache` action, which
only saves in a post step gated on the job succeeding -- a failed
smoke later in the same run would otherwise discard a fifteen-
minute build every single time. The dll-locating step that writes
SQLCIPHER_LIB_DIR stays unconditional, since it has to run whether
the library was just built or restored from cache.
- The legacy installer machinery is retired. App::Ariza::Legacy, the
eight Jinja2 templates and two shell partials under
resources/legacy, the per-app configs, the SQLCipher mirror
reference material, `ariza render-legacy` and the parity test are
all gone. The ruling behind it: apps get retired rather than
carried, and an app that is ready to ship ships the way Moneymoor
does -- a bundle, four generated installer scripts and two
workflows. Keeping a second distribution mechanism alive for one
unreleased app meant two ways to install everything, two things to
keep true of every pin bump, and a parity gate that could only ever
say "the old thing still renders the old bytes".
- The absorption was not wasted. The parts of the legacy partials
that a bundle installer still needs -- the marker-block PATH
persistence, the shell-rc detection -- were adapted into
resources/templates/installer-common-{posix.sh,windows.ps1} when
`ariza installers` was written, and those stay. What has been
deleted is only what drove package managers, refcounted a shared
registry and installed a terminal emulator: everything a bundle
exists to not do.
- versions.toml loses `wezterm`, `wezterm-macos-sha256` and
`sqlcipher-release-base`. Each was read by exactly one consumer,
the legacy render context, and by nothing else in lib/ or in any
live template. `sqlcipher` stays (the advisory pin a bundle is
expected to carry), `rakudo-star` stays, and `[rakudo]` -- the
runtime a bundle actually embeds -- is untouched. The matching
accessors and their tests go with the keys.
- `rakudo-star` follows in the same cleanup: the legacy renderer was
its only consumer, and with that gone the pin named a Rakudo
release nothing in ariza installs on anyone's behalf. `sqlcipher`
and `[rakudo]` are unaffected; App::Ariza::Versions drops the
accessor and the parse arm, and their tests go with it.
- Documentation stops naming the directory it was developed in. The
distribution description, the NAME line and the example output
paths no longer carry a private path segment, or imply that the
apps live together in one tree: ariza is a bundler for Raku
terminal applications, not for one person's directory of them. The `releases/latest` URL in the
installer section now uses the C<< ... >> Pod form, since the
E/E escapes rendered literally through C<>.
- Phase four: `ariza scaffold-ci`. App::Ariza::CI writes the two
GitHub Actions workflows an app needs into its own
.github/workflows -- a test.yml in the house shape, and a
release.yml that builds one bundle per platform the app declares,
smokes each one, and publishes them on a tag. `ariza bundle`
builds a bundle on the machine it is standing on; a release needs
one per platform, on machines that are actually those platforms,
because SQLCipher comes off the build host and there is no
cross-build to fall back on. That is a CI problem, so this is CI.
- One build lane per declared slug, each installing what ariza needs
before running the same three steps (bundle, smoke, upload):
macos-latest with `brew install sqlcipher`; ubuntu-latest inside
quay.io/pypa/manylinux_2_28_x86_64, so the archive's glibc floor
is 2.28 (RHEL 8+, Ubuntu 18.10+, Debian 10+) rather than whatever
the runner image ships this month; windows-latest with
`vcpkg install sqlcipher:x64-windows` and SQLCIPHER_LIB_DIR
pointed at the result, which is the sourcing contract
App::Ariza::Native documents for Windows.
- The manylinux lane installs Rakudo by hand, out of the same JSON
release index App::Ariza::Rakudo reads and at the same [rakudo]
pin: Raku/setup-raku installs into the runner's tool cache, which
is not in the container's filesystem, and there is no URL pattern
to construct because upstream filenames carry a toolchain suffix.
It builds SQLCipher from source at the pinned version too --
EPEL's package is a 3.34.1-era build with a renamed soname, which
ariza can now find but which is years behind the pin.
- Every lane's Raku is there to RUN ariza and is not what ends up in
the bundle; ariza downloads the pinned runtime for itself. Which
is why setup-raku asks for `latest` and nothing in the workflow
pretends otherwise.
- publish is tag-gated, collects every lane's artefact, recomputes a
combined checksums.txt and then checks it against the .sha256
sidecars ariza wrote -- a release whose digest refuses its own
archive is worse than one with no digest -- before cutting the
release with a body that says what a bundle is, which machines
each archive runs on, and how to verify a download.
- smoke-installer is the job that matters: on a plain ubuntu-latest
runner with nothing installed on it, it downloads the archive that
was JUST published, installs it with the repository's own
committed install.sh, and runs the installed launcher under
`env -i`. It is the only check that exercises the artefact a user
will actually receive by the path they will actually take, and it
reads install.sh out of the checkout rather than from a raw
GitHub URL so that it works for the first release.
- workflow_dispatch triggers the build lanes and stops there, with
an optional `ref` input. That is the iteration loop and the reason
the trigger exists: a recipe that has gone stale costs a run and a
push to a branch rather than a burnt tag and a deleted release.
- release.yml is derived from bundle.platforms and is rewritten in
place on every run. test.yml is written only when it is ABSENT --
a test workflow acquires system dependencies, extra jobs and skip
conditions no generator can infer from a manifest -- unless
--force says otherwise, and each generated header says which of
the two it is. The scaffolded test.yml names the app's
bundle.native libraries and says it is deliberately not installing
them: what a bundle carries and what a test run needs on a runner
are different questions.
- A declared platform with no lane (musl, aarch64 Linux, the Intel
Mac, Windows on ARM) is a hard error rather than a skipped job,
the same reasoning as an unknown slug in bundle.platforms:
silently dropping one produces a release quietly missing a
platform the author asked for. The three that do have lanes are
the ones with both a GitHub-hosted runner and an official Rakudo
build behind them, which is deliberately the same shape as
App::Ariza::Rakudo's partial platform map.
- ariza.toml gains an optional [ci] table with one key,
`ariza-source`: "fez" (the default) or anything zef can install
from, for an app whose release workflow has to exist before ariza
is published. Whichever is not in use is rendered beside it as a
comment, along with the ariza version that scaffolded the file, so
switching is an uncomment rather than a remembering exercise. Not
a closed set, so only an empty value is an error -- it would
render a step that installs nothing and succeeds.
- Golden-file tests cover both rendered workflows, with the pins and
the ariza version supplied as fixtures so a golden fails when a
template changes rather than when a version is bumped, and
structural tests assert the things a golden diff would not
explain: the job list matches the declared platforms, publish
names every lane in `needs:`, both publishing jobs are tag-gated,
the container line is there, and no unrendered Jinja2 delimiter
survives (GitHub's own `${{ }}` does, which is exactly the
collision the templates escape). Verified with actionlint,
shellcheck and pyflakes: zero findings on both files.
- Sourcing SQLCipher on Linux no longer requires the canonical
libsqlcipher.so.0 soname. EPEL's `sqlcipher` package renames it
per version -- libsqlcipher-3.34.1.so.0, and nothing literally
called libsqlcipher.so.0 at all -- which failed sourcing on every
RHEL-family host. The exact canonical name still wins outright
when it is present; where it is not, any libsqlcipher*.so* from
the same two sources (ldconfig -p, then the standard library
directories) is considered, and the newest of several candidates
is taken by a numeric-aware comparison rather than a plain string
sort, with the manifest's origin naming what was actually found.
What lands in the bundle is unaffected either way: stage-sqlcipher
always copies to the canonical name, which is safe because
`is native('sqlcipher')` dlopens by leaf name through
LD_LIBRARY_PATH -- filename, never the file's own DT_SONAME.
- The Fedora/RHEL install remedy in the "no SQLCipher on this
machine" message now names the real package, `dnf install
sqlcipher` -- `sqlcipher-libs` does not exist and was never
installable advice.
- A Linux bundle now carries its own OpenSSL. It did not, and the
audit could not tell: a distro libsqlcipher.so.0 names OpenSSL as a
bare `NEEDED libcrypto.so.3` -- no path, no RPATH, nothing in the
file to object to -- which at run time means "whatever libcrypto
this machine has", and on a user's machine may mean nothing at all.
Staging an ELF now does what staging a Mach-O has always done:
walks the library's dependencies with ldd (the loader itself, so
the answer is the one the machine will really give), copies every
non-system one in beside it under its soname, recurses, and runs
`patchelf --set-rpath '$ORIGIN'` over the library and every copy.
The skiplist -- what stays dynamic -- is Notcurses-Native's
bundle-elf.sh list (the loader, libc, libm, libpthread, libdl,
librt, libgcc_s, libstdc++ and friends), with one deliberate
difference: libcrypto and libssl are NOT on it. That script leaves
them dynamic because ffmpeg's use of them is optional. SQLCipher
without OpenSSL is SQLite.
- The ELF audit grew the two checks that can actually see this, and
they only run on a Linux host because both tools answer for the
machine they are on: `patchelf --print-rpath` must be
$ORIGIN-relative (and present, on any file with a non-system
NEEDED), and `ldd` in a REPLACED environment -- bare PATH, i.e.
`env -i`, which is the user's view rather than the build's -- must
resolve every non-system dependency to a path inside the bundle,
with "not found" a finding. From a Mac, cross-inspecting an ELF is
the static half and says so rather than pretending to more.
- xxt/linux-selfcontain-proof.sh proves the above in a manylinux
container rather than asserting it: real SQLCipher, real Rakudo,
App::Ariza's whole test suite run on Linux, a real bundle staged
and audited through App::Ariza::Native, the result re-checked
independently in shell, and three negative controls (a planted
absolute NEEDED, a deleted dependency, a stripped rpath) that must
each make the audit fail. A proof that only ever passes proves
nothing. It is xxt/, so nothing in t/ needs docker.
- Binary formats are now read from the file's own magic number
instead of shelling out to `file`. A format probe that answers "not
a binary" when its helper is missing turns the audit into a loop
that checks nothing and passes silently, and `file` is exactly the
sort of tool a minimal build image leaves out.
- SQLCipher now comes from the build machine's package manager, and
there is no mirror to download it from -- the one named by
`sqlcipher-release-base` never existed, and standing one up would
have been a second piece of release infrastructure with its own
signing story, staleness and outages for a library whose ABI moves
about once a year. macOS takes the installed Homebrew keg (or, if
it is not installed, the bottle `brew fetch` puts in the cache);
Linux takes whatever `ldconfig -p` resolves libsqlcipher.so.0 to,
falling back to the Debian multiarch, Red Hat and Alpine library
directories; Windows takes a vcpkg tree under VCPKG_ROOT. CI
installs the distro package before it calls `ariza bundle`.
- What makes a package-manager library safe to ship is unchanged and
untouched: the self-containment pass still copies every dependency
that resolves outside the bundle in beside it, rewrites it to
@loader_path, re-signs, and the audit still refuses to package a
bundle where anything points off the build machine.
- --sqlcipher-archive is unchanged and still beats everything;
SQLCIPHER_LIB_DIR joins it as a second explicit override, naming a
directory rather than an archive. Both bypass the new cross-build
guard, which refuses to hand a `linux-x86_64-glibc` build the
macOS keg it is standing on -- a library installed here is built
for here, and an artefact made that way fails at dlopen on every
machine it was named for. Every "not installed" path dies naming
the package to install AND the override to pass.
- The `sqlcipher` pin is now advisory. The package manager decides
what is installed, so ariza reads the version out of the staged
library's own bytes (SQLCipher keeps CIPHER_VERSION as a
NUL-terminated X.Y.Z constant; filenames are no use -- a Homebrew
keg's real file is libsqlcipher.3.51.3.dylib, which is SQLite's
version, and Debian's is libsqlcipher.so.0.8.6, which is libtool's
current.revision.age) and warns on a mismatch rather than failing.
ariza-manifest.json records the version STAGED, the pin beside it
as `pinned`, the digest of the library as it came off the machine,
and which keg, bottle or archive it came from. It used to record
the pin, which was a number nobody had verified.
- Every slug ariza can name can now stage SQLCipher, musl and
windows-arm64 included: with the library coming off the machine
there is no per-slug publication question left to answer, only
"is it installed".
- Phase three: `ariza installers`. App::Ariza::Installer renders the
four scripts an end user actually runs -- install.sh, install.ps1,
uninstall.sh, uninstall.ps1 -- from the app's ariza.toml, into the
app's own repository by default, because `curl … | sh` has to be
able to fetch them from somewhere.
- One POSIX script covers macOS and Linux, rather than the legacy
pair. A bundle installer drives no package manager, so the only
per-platform decision left is which asset to download, and that is
a uname call away at run time: one curl line in a README, and
nobody has to work out which of two files applies to them. The
Linux branch probes for a ld-musl-*.so.1 loader before choosing
between -glibc and -musl, exactly as App::Ariza::Platform does.
- Detection produces one of the slugs the app *declares* in
bundle.platforms or nothing at all -- never a nearest match, since
a glibc bundle does not run on Alpine. An unnamed platform gets
"no prebuilt bundle for yet" and a link
to the releases page. An app that declares no Windows platform gets
no install.ps1 at all.
- Versions resolve from the `location:` header of the
releases/latest redirect: one HEAD request, no API token and no jq
on a machine that may have neither. --version names a tag instead.
- Every download is verified against the .sha256 asset published
beside it, and a missing or wrong digest is fatal. --url (or
_BUNDLE_URL, e.g. MONEYMOOR_BUNDLE_URL) installs from a
source the user names and bypasses GitHub entirely -- it takes a
plain file path as readily as a URL, which is what makes an
air-gapped install and ariza's own network-free end-to-end test
possible. --insecure-no-verify applies to that path only; a
published release always has a digest, so its absence there stays
fatal.
- The layout is $XDG_DATA_HOME//versions// with a
`current` symlink and a ~/.local/bin/ link. The new version
is unpacked into a staging directory on the same filesystem and
renamed into place before anything is flipped, so a failed or
interrupted download cannot damage a working install. One previous
version is kept to roll back to and anything older is pruned, by
name. Version directories are stamped at install time, not build
time: tar restores the original mtime, which is identical for every
bundle cut from one source tree.
- Re-running for a version already installed downloads nothing. It
re-points both links, re-checks the PATH block and exits 0 saying
"already installed", which makes "run the installer again" the
correct advice for the most common breakage.
- PATH persistence is the legacy dp_persist_path machinery, adapted
into an installer partial rather than sourced whole: a marker block
named after the executable (so uninstalling one app never removes
the line another needs), added only when ~/.local/bin is not
already on PATH, and only once per rc file. The uninstaller removes
exactly that block, rewriting through the existing file so its mode
and ownership survive, and leaves the state directory and the app's
own data alone -- naming them rather than deciding for you.
- The generated scripts are curl-pipeable: entirely definitions with
a single `main "$@"` on the last line, so a truncated download
cannot half-run one, and nothing reads standard input, because when
the script arrives *on* standard input there is nothing left to
read from.
- Windows mirrors all of it in PowerShell: %LOCALAPPDATA%\\
versions\, a `current` junction (a symlink needs
administrator rights or Developer Mode, which a per-user install
has no business demanding), and ...\current\bin added once to the
user PATH in HKCU\Environment -- which means an upgrade needs no
PATH change at all. Archives are unpacked with tar.exe, which has
shipped since Windows 10 1803 and reads the .tar.gz ariza actually
publishes for every platform; Expand-Archive handles a .zip if one
is ever pointed at with -Url.
- ariza.toml gains an [installer] table with one key, `repo`, the
GitHub owner/name releases come from. Optional in the schema and
required by `ariza installers`, which says so rather than
rendering a script that 404s. Its shape is closed like a platform
slug's: a full URL or a bare name is a hard error.
- Golden-file tests cover all four rendered scripts, `sh -n` checks
the POSIX pair, and a quoting gate walks both with the shell's own
quoting rules -- command substitutions and here-documents included
-- to prove every path expansion is inside double quotes. A new
xt/02-installer-e2e.rakutest drives a real bundle end to end
against a scratch HOME: install, launcher runs and reports its own
version, re-install repairs, upgrade flips current, a third install
prunes to one previous, a missing or wrong digest is refused
without disturbing the working install, and uninstall leaves both
rc files byte-identical to what they were.
- Phase two: `ariza bundle` and `ariza smoke`. A bundle is one
directory and one archive containing the app, a Rakudo runtime,
every Raku dependency with warm bytecode, and every native library
it loads. Nothing is installed, nothing is written outside it (bar
a one-line first-run marker under XDG_STATE_HOME), and deleting the
directory is the uninstall.
- App::Ariza::Rakudo resolves the [rakudo] pin against rakudo.org's
JSON release index, downloads the archive once into
$XDG_CACHE_HOME/ariza/rakudo and unpacks it into the bundle. The
cache records a sha256 on first download and verifies it on every
reuse; a mismatch discards the copy and re-fetches rather than
baking a bad byte into every bundle built on that machine.
rakudo.org publishes binaries for four of ariza's eight slugs, and
asking for one of the other four dies naming the four that work.
- App::Ariza::Site installs the app and its whole closure into the
bundle's own repository using the *bundled* zef, so the bytecode
matches the runtime that ships. It then warms the precompilation
store, which zef does not do: measured on Moneymoor, a cold bundle
spends ~55s compiling on first launch (and again on every launch,
if the bundle is somewhere unwritable) against ~0.5s warm. The
store is position-independent, so warming it at build time survives
being unpacked anywhere, including a path with spaces.
- Site checks what it built: the app distribution is present, every
installed distribution's dependencies are satisfied from inside the
bundle, every module the app provides loads, the precomp store is
not empty, and notcurses staged a non-empty library directory under
the BINARY_TAG it actually used (read off disk, never hardcoded).
- App::Ariza::Native stages SQLCipher — into rakudo/lib on macOS,
which is on the bundled interpreter's own LC_RPATH and so resolves
`is native('sqlcipher')` with no launcher involvement at all; into
native/sqlcipher elsewhere, where the launcher names it explicitly.
On macOS the staged library is made self-contained: every absolute
dependency (a Homebrew OpenSSL, say) is copied in beside it,
rewritten to @loader_path, and everything touched is re-signed,
because install_name_tool invalidates the signature and arm64 macOS
will not load a library whose signature is wrong.
- Native then audits every binary ariza put into the bundle and fails
the build if any of them would load a library from outside it:
otool -L on Mach-O, readelf -d on ELF, a presence check on PE. The
verdict functions are pure and take tool output as text, and the
tool call itself is an injectable seam, so every branch — including
the Linux and Windows verdicts — is testable from any machine.
- App::Ariza::Launcher renders bin/ from a Jinja2 template
(plus the .ps1 and .cmd twins for Windows, CRLF). It resolves its
own path through a readlink loop rather than `readlink -f`, which
is a GNU extension a bundle cannot rely on; exports RAKULIB and
NOTCURSES_NATIVE_DATA_DIR (never NOTCURSES_NATIVE_LIB_DIR, which
suppresses the terminfo setup) and unsets PERL6LIB; adds
LD_LIBRARY_PATH and DBIISH_SQLCIPHER_LIB on Linux only; prints a
one-line first-run notice, once, guarded by a sentinel outside the
bundle; warns without refusing when TERM is dumb or absent; and
quotes every expansion, so a bundle unpacked into a path with
spaces works. Golden-file tests cover all three platform slugs.
- App::Ariza::Bundle orchestrates the four, then writes VERSION (one
screen: app version and component pins), ariza-manifest.json (the
same plus every source URL, every sha256, every installed
distribution and the smoke commands) and LICENSES/ — the app's
licence, Rakudo's, and a COMPONENTS.md whose native inventory is
read off the staged libraries rather than written down, including
an explicit note that this notcurses pack's FFmpeg is a GPL build.
Building a platform the app does not declare in bundle.platforms is
an error.
- App::Ariza::Smoke unpacks a finished archive into a scratch
directory with a space in its name and runs it with a *replaced*
environment — PATH, HOME, TERM and nothing else — so a bundle
cannot pass by quietly using the developer's system Rakudo or their
DBIISH_SQLCIPHER_LIB. It re-runs the native audit over the unpacked
tree and reports pass or fail per check, keeping the tree on
failure. Smoke commands come from the bundle's own manifest, so an
archive can be checked with no access to the app's repository.
- bundle.smoke now accepts a list, and each entry may be argv rather
than a command line, so a smoke test can carry an entire program
with no shell and no quoting layer. Placeholders: {exec}, {raku},
{site}, {native}, {bundle}, {tmp}. App-Moneymoor uses this to
smoke-test the thing --version never touches: creating, writing and
reopening an encrypted database from inside the bundle.
- App::Ariza::Tools is the new shell-out layer — run, download,
digest, extract — so no other module contains a bare `run` and
every failure reads the same way. No new dependencies: ariza
fetches three artefacts from static hosts with curl, exactly as
every Build.rakumod in this tree already does.
- Fixed: current-slug() and detect-slug() died with a type-check
failure when ARIZA_PLATFORM was unset, because %*ENV returns Any
for a missing key and the parameter is typed Str. That is every
machine that has never heard of the variable.
- Initial release. Phase one of ariza: the distribution scaffold and
the shared machinery a bundler needs, with no bundling yet and no
placeholder pretending otherwise — three verbs, all of which do
their whole job.
- App::Ariza::Platform names the platform an artefact is built for.
The eight slugs (macos-arm64, macos-x86_64, linux-{x86_64,aarch64}-
{glibc,musl}, windows-{x86_64,arm64}) are Notcurses-Native's,
character for character, because a bundle carries that
distribution's prebuilt libraries and the two have to agree about
what platform they are on. Detection probes $*KERNEL plus, on
Linux, the libc: a ld-musl-*.so.1 loader first, then ldd --version
for glibc. ARIZA_PLATFORM forces a slug for cross-builds and CI
matrices, and is validated against the known set, because an
override is the one input that is definitely a human mistake if it
is wrong.
- App::Ariza::Versions parses resources/versions.toml, the single pin
file every artefact is built against. A new [rakudo] section
(version 2026.07, revision 01) names the runtime a bundle embeds,
which is deliberately not the same pin as rakudo-star: that one is
the release an installer tells a package manager to fetch onto the
user's machine.
- App::Ariza::Config parses the per-app ariza.toml manifest, which
lives in the app's own repository so ariza never grows a list of
special cases about specific apps. [app] name/exec/display are
required; [bundle] declares platforms, native libraries and a smoke
command whose {exec} expands to the launcher name.
- Unknown keys warn and never die, in both config files, so one
manifest can serve several ariza versions; wrong types die naming
the dotted path; //-prefixed keys are ignored. The one exception is
an unknown platform slug in bundle.platforms, which dies: the
supported set is closed, so a typo cannot be a future feature, and
ignoring it would ship a release quietly missing a platform.
- App::Ariza::Legacy absorbs installers/generate.raku intact — the
Jinja2 templates, the per-app configs, the sqlcipher mirror notes
and the pin file are all ariza resources now. It renders the eight
install/uninstall scripts per app, defaulting to each app's own
repository exactly as the old script did, with an --out-dir
override so a render can be inspected before it lands.
- The absorption is gated by a byte-for-byte parity test: all eight
App-Mindmoor scripts are rendered to a scratch directory and
compared against the copies committed in that repository, with a
unified diff excerpt on any mismatch. Drift in a template, a
context key or the Jinja2 dependency fails the suite instead of
reaching a user.
- App::Ariza::Resources finds ariza's own data through %?RESOURCES
when installed and the checkout tree when not, including directory
listing, which %?RESOURCES cannot do — installed runs enumerate the
distribution manifest, checkouts read the working tree.
- bin/ariza: render-legacy [--app] [--out-dir], version, help.
- App-Moneymoor gains the first ariza.toml.