The Homebrew formula is the cli module's product (amy), and the CLI already
owns its packaging artifacts under cli/packaging/ (cli/packaging/macos/
amy.entitlements). The root packaging/ dir was new in this branch and held
nothing else, so co-locate the formula with the module that owns it and drop
the stray root dir. Updates the two BUILDING.md references.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015sso31DfSF9B6EFCVkEqWD
macOS validation (Developer ID D77MCV9NZ7) confirmed the hardened-runtime
entitlements are correct and load-bearing: amy init derives a secp256k1 key
cleanly, and dropping disable-library-validation reproduces the runtime
dlopen Team-ID failure. The one unverified gap is whether Apple's notary
service accepts the unsigned Mach-O dylibs embedded inside lib/*.jar
(secp256k1/jna/sqlite/skiko), which it inspects recursively.
- create-release.yml: the notarize step now submits with --output-format json,
and on any non-Accepted status dumps `notarytool log` (per-file issues) and
fails — so the first real run names the offending files instead of failing
opaquely. No speculative in-jar signing yet; gather the log first.
- BUILDING.md: record the validation result, the embedded-jar-native risk, the
one-run way to decide it (workflow_dispatch dry_run with MAC_* secrets), and
the staged fixes (sign-in-jar and/or strip the skiko/Compose leak). Note the
desktop app shares the same jars and needs its own dry-run.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015sso31DfSF9B6EFCVkEqWD
Sign the macOS jlink image (amy-<version>-macos-arm64.tar.gz) so users who
download it directly clear Gatekeeper. Reuses the same Developer ID cert and
the six MAC_* secrets as the desktop DMG; no-op when they're absent.
- .github/actions/import-macos-cert: factor the throwaway-keychain cert import
into a composite action; the desktop leg now uses it too (was inline).
- create-release.yml (build-cli macOS leg): import the cert, then codesign
every Mach-O binary in the bundled JRE (executables get hardened-runtime
entitlements, dylibs don't) and notarize via notarytool --wait. Runs before
the collect step so the tarred image is signed. Job timeout 30->45 min for
notarization headroom.
- cli/packaging/macos/amy.entitlements: hardened-runtime entitlements; the
disable-library-validation key lets the JVM load the secp256k1 native dylib
it extracts from a jar at runtime (would otherwise crash under notarization).
- BUILDING.md: document the tarball signing, the no-stapling/online-check
caveat, and that the Homebrew-core jvm bundle is intentionally left unsigned.
Untested end-to-end (no macOS runner / Apple creds here) — validate with a
workflow_dispatch dry-run once the secrets are provisioned.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015sso31DfSF9B6EFCVkEqWD
Enable distributing the `amy` CLI via Homebrew-core (mainline formulae).
Homebrew-core builds in a network-sandboxed env, so a from-source Gradle
build can't resolve Maven deps there; the accepted pattern for JVM tools is a
pre-built no-JRE jar bundle + `depends_on "openjdk"`. installDist already
produces exactly that (bin/amy + lib/*.jar, no bundled runtime).
- create-release.yml: publish `amy-<version>-jvm.tar.gz` (the installDist tree)
as a release asset on the linux leg. Pure JVM bytecode, so one
platform-independent artifact serves every OS.
- packaging/homebrew/amy.rb: reference formula (depends_on openjdk, livecheck
for BrewTestBot auto-bumps, `amy --help` smoke test). Not consumed by any
build here — it's the artifact to submit to Homebrew/homebrew-core.
- BUILDING.md: homebrew-core submission runbook; note that the desktop app is
already on mainline Homebrew (homebrew/cask); document name-collision and
pre-built-jar review caveats.
- asset-name.sh: document the jvm bundle naming exception.
Verified locally: :cli:installDist builds with only a JDK (no Android SDK),
and the extracted bundle runs via JAVA_HOME (`amy --help` exits 0).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015sso31DfSF9B6EFCVkEqWD
Add gated code-signing + notarization for the macOS desktop DMG so it can
clear Gatekeeper and stay in Homebrew's main cask (unsigned casks are
rejected after 2026-09-01).
- desktopApp/build.gradle.kts: macOS signing{}/notarization{} blocks, gated
on the AMETHYST_MAC_SIGN_IDENTITY env var. Absent => unsigned DMG, exactly
as before, so local dev and PR CI are unaffected.
- create-release.yml: import a Developer ID cert into a throwaway keychain on
the macOS leg and export the signing/notary env. Soft-gated on the
MAC_CERTIFICATE_P12 secret — no secret => unsigned build.
- BUILDING.md: document the six MAC_* secrets, how to generate them, and flip
the unsigned-cask fallback note to reflect the wiring is now in place
(pending Apple credentials).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015sso31DfSF9B6EFCVkEqWD
Make BUILDING.md everything a fork needs to build and release, and add a
maintainer-facing ship checklist.
- BUILDING.md: full CI secrets inventory (Android keystore vs GPG/Maven, with
generation commands), a distribution-channels table (GitHub, Maven, Play,
F-Droid pull, Zapstore, Homebrew/Winget), and a "generated & vendored
artifacts" section (Material Symbols subset, Arti native libs).
- RELEASE_OPS.md (new, repo root): Amethyst-specific shipping — pre-tag
checklist, per-channel rollout (manual Play AAB, Zapstore `zsp publish` with
our nsec, F-Droid build-from-source), the push-notification server
(amethyst-push-notif-server), secret ownership, and verification.
- README.md: slim the Deploying section to point at both docs, fix the stale
quartz dependency version, and add a Maven Central badge plus JitPack
snapshot docs.
- android-expert skill: drop the duplicated build-config block, delegate to
gradle-expert, keep only the Android-specific flavor note.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
linuxdeploy auto-walks every binary in the staged AppDir with ldd to
bundle their shared-library deps. That fights jpackage's self-contained
output on two axes and aborts the createReleaseAppImage task before any
AppImage is produced:
- The bundled JRE puts libjvm.so at usr/lib/runtime/lib/server/, while
its sibling libs (libmanagement.so, libawt_xawt.so, libfontmanager.so
and others in usr/lib/runtime/lib/) have RPATH=$ORIGIN. ldd cannot
resolve libjvm.so from those libs without help, so linuxdeploy errors:
ERROR: Could not find dependency: libjvm.so
ERROR: Failed to deploy dependencies for existing files
- The bundled libvlc plugins under usr/lib/app/resources/vlc/ are
UPX-compressed by the vlc-setup plugin. patchelf cannot read their
section headers, so linuxdeploy aborts:
ERROR: Call to patchelf failed:
patchelf: no section headers. The input file is probably a
statically linked, self-decompressing binary
- Even after working around libjvm.so via LD_LIBRARY_PATH, several VLC
libs have RUNPATH pointing at the wrong directory, so ldd fails on
libva.so.2 / libvlccore.so.9 next.
This is why v1.09.2 shipped .deb + .rpm from the linux leg but no
.AppImage and no linux .tar.gz from the linux-portable leg — the gradle
step failed on linuxdeploy, so the subsequent "Build portable archives"
step never ran.
Swap to appimagetool, which only embeds the AppDir as-is into a
SquashFS-backed, runtime-prepended AppImage and never touches the
contents. jpackage already bundles a complete, self-contained app —
we don't need linuxdeploy's dep-discovery. AppRun continues to handle
LD_LIBRARY_PATH at launch.
Verified locally on ubuntu-24.04: a fresh
:desktopApp:createReleaseAppImage now produces a valid 254 MB
Amethyst-1.09.2-x86_64.AppImage in ~30 s on top of an up-to-date
createReleaseDistributable.
Side changes:
- Workflow installs desktop-file-utils (appimagetool 1.9.0 calls
desktop-file-validate on the .desktop entry).
- BUILDING.md updated with the new local-dev fetch command.
- .gitignore replaces the linuxdeploy paths with appimagetool's.
The AppImage build inputs (AppRun, .desktop entry, icon, and the
CI-fetched linuxdeploy binary) are consumed only by desktopApp's
createReleaseAppImage task. Co-locating them under
desktopApp/packaging/appimage/ removes the `../` path escape from the
build script and keeps all desktop packaging assets inside the module.
https://claude.ai/code/session_0137ULcfJkASmfmffFBdW8ac