b59c6148ffc3784b71d048331f137d173202c24a
72
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5d16b5713d |
Give BuildKit the host's network, so it can reach the runner's cache
Secret Scan / scan (push) Successful in 4s
Build App (Preview) / compute-version (pull_request) Successful in 6s
Secret Scan / scan (pull_request) Successful in 6s
Build App (Preview) / create-release (pull_request) Successful in 2s
Build App (Preview) / build-macos (pull_request) Successful in 2m45s
Build App (Preview) / build-windows (pull_request) Successful in 4m45s
Build App (Preview) / build-linux (pull_request) Successful in 7m38s
Build App (Preview) / prune-previews (pull_request) Successful in 4s
Build Container / build-container (pull_request) Successful in 14m48s
The multi-arch build needs the `docker-container` driver — the plain `docker` driver cannot do linux/amd64+linux/arm64 — and that driver runs BuildKit in its own container on Docker's default bridge. act_runner advertises ACTIONS_CACHE_URL as an address the *job* container can reach, and nothing teaches the BuildKit container about it. So the job could reach 192.168.1.126:40649 while the container actually making the cache request could not. That is also why no other workflow here hit this: it is the only one using buildx. The rest make their cache calls from the job container act_runner set up. `no route to host` is EHOSTUNREACH — a firewall rejecting, not a missing route — which is what a default firewalld zone does to traffic from the docker bridge, and the runner registers under the stock RHEL/Fedora hostname. Sharing the host's namespace sidesteps it: the cache address becomes local to BuildKit. No effect on runners where this already worked. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0145mQi9NZiCDrznBUEEDE4n |
||
|
|
c02c02cbfc |
Never fail a container build because the cache was unreachable
Secret Scan / scan (push) Successful in 4s
Build App (Preview) / compute-version (pull_request) Successful in 3s
Secret Scan / scan (pull_request) Successful in 4s
Build App (Preview) / create-release (pull_request) Successful in 1s
Build App (Preview) / build-macos (pull_request) Successful in 2m41s
Build App (Preview) / build-windows (pull_request) Successful in 4m52s
Build App (Preview) / build-linux (pull_request) Successful in 8m0s
Build App (Preview) / prune-previews (pull_request) Successful in 2s
Build Container / build-container (pull_request) Successful in 10m21s
Every layer of both architectures built. The job then died exporting to act_runner's emulated GitHub Actions cache service, which it could not route to: `GetCacheEntryDownloadURL ... dial tcp 192.168.1.126:40649: no route to host`. On a pull_request `push:` is false, so this job pushes nothing and the cache is its only output — which means a network problem between the buildx `docker-container` builder and the runner host threw away a complete, successful validation of the Dockerfile on linux/amd64 and linux/arm64. A cache is an optimisation; it must degrade to "slow", never to "red". Only the exporter needs the flag. The import is already non-fatal — the build ran all 37 layers after warning it could not read the cache. This does not fix the routing itself, so builds stay uncached until that is sorted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0145mQi9NZiCDrznBUEEDE4n |
||
|
|
019fb403d5 |
Merge remote-tracking branch 'origin/main' into fix/update-channel-durability
Secret Scan / scan (push) Successful in 4s
Build App (Preview) / compute-version (pull_request) Successful in 3s
Secret Scan / scan (pull_request) Successful in 3s
Build App (Preview) / create-release (pull_request) Successful in 1s
Build App (Preview) / build-macos (pull_request) Successful in 2m42s
Build App (Preview) / build-linux (pull_request) Successful in 4m51s
Build App (Preview) / build-windows (pull_request) Successful in 4m54s
Build App (Preview) / prune-previews (pull_request) Successful in 1s
|
||
|
|
f41b1d9054 |
Install from the lockfile, so CI cannot be broken by someone else's release
Secret Scan / scan (push) Successful in 4s
Build App (Preview) / compute-version (pull_request) Successful in 3s
Secret Scan / scan (pull_request) Successful in 3s
Build App (Preview) / create-release (pull_request) Successful in 1s
Build App (Preview) / build-macos (pull_request) Successful in 2m40s
Build App (Preview) / build-windows (pull_request) Successful in 4m52s
Build App (Preview) / build-linux (pull_request) Successful in 5m0s
Build App (Preview) / prune-previews (pull_request) Successful in 2s
`build-linux` fails before `tauri build` runs, on every workflow, at "Install
frontend dependencies":
npm error Cannot read properties of null (reading 'edgesOut')
Reproduced exactly on the first attempt by running the step's own commands
locally on the same Node 22.23.2 the runner installs. The debug log gives the
frame the CI output omits:
at #loadPeerSet (.../@npmcli/arborist/lib/arborist/build-ideal-tree.js:1289:38)
It is a null dereference in npm 10.9.8's peer-set resolver, reached through
vite → @vitejs/devtools → @vitejs/devtools-vitest → vitest@* →
@vitest/browser-playwright → vitest@4.1.11 → jsdom@* → canvas.
**Nothing in this repo changed to cause it.** The step deleted
`package-lock.json` before installing, so every build re-resolved the entire
tree against the registry against ranges like `vitest@*`. A dependency
published a version that produces a peer graph npm cannot resolve, and our CI
broke — the same command succeeded fifteen hours earlier for 0.4.21. That is
the real defect: the build was never reproducible, and the crash is only how we
found out.
So Linux installs with `npm ci`, from the committed lockfile, like Windows
already did. macOS moves too — it kept the lockfile but still ran `npm
install`, which is free to re-resolve; all three platforms now install
identically and none can re-resolve mid-release.
**The reason the lockfile was being deleted is obsolete, not ignored.**
|
||
|
|
63f282bef6 |
Fix the review findings: never destroy a working anchor
Secret Scan / scan (push) Successful in 4s
Build App (Preview) / compute-version (pull_request) Successful in 3s
Secret Scan / scan (pull_request) Successful in 4s
Build App (Preview) / create-release (pull_request) Successful in 1s
Build App (Preview) / build-linux (pull_request) Failing after 1m49s
Build App (Preview) / build-macos (pull_request) Successful in 2m41s
Build App (Preview) / build-windows (pull_request) Successful in 4m55s
Build App (Preview) / prune-previews (pull_request) Skipped
An adversarial review of the previous commit found six real problems and corrected one of my claims. Taking all of it. **The anchoring could kill the channel it exists to protect.** It did DELETE-then-POST so the tag would name the current build. If the POST failed for any transient reason the script aborted having already deleted the anchor a previous run put there, and the next mirror run pruned GitHub's copy — a transient Gitea error converting a healthy channel into a dead one, which is strictly worse than the step not existing. There was also a real window between the two calls with no tag at all. The DELETE bought nothing. The update string resolves the tag by *name* and the assets hang off the release object, so nothing about the channel depends on which commit the tag points at; moving it changes only the source-zip link. It existed solely to get past a 409, since Gitea's POST /tags has no force semantics. Now the tag is created if absent and otherwise left alone, which removes the window too. **My "no window where the two disagree" claim was wrong, and it is the third time in this area I have asserted something I had not established.** The release POST sets no `target_commitish`, so GitHub creates its tag at its own default-branch HEAD, not at `GITEA_SHA`; the two agree only because `sync_on_commit` pushes main minutes earlier. And the DELETE actively created the window. What the ordering genuinely buys is narrower: if anchoring fails, the script aborts before creating a GitHub release that would be orphaned. **Orphaned drafts were invisible to the release lookup.** GitHub demotes a release to a draft when its tag is deleted, and `/releases/tags/` never returns drafts — precisely the state every mirror run left behind. The by-tag lookup reported "absent" while 86 MB drafts accumulated, one per release. The lookup now reads the authenticated list, republishes the newest, and deletes the rest. **A guard that could not catch what it named.** The update-info assertion was a substring match on the tag, so it passed for a wrong host, path, filename or transport — verified: an `evil.example.com/.../linux-latest/...` string passes the old check and fails the new one. Now a fixed full-string match. Also from the review: an absent bundled library no longer exits early, because that skipped the metadata *and* left `update-channel/` uncreated, killing the publish step on a missing directory and taking the tag and mirror jobs with it; the Categories guard asserts the absence of an empty value rather than the presence of any filled one; the channel directory is cleared before use so a stale zsync cannot satisfy an existence check while describing the previous build; the AppImage count uses a glob array, since `ls | wc -l` aborted under pipefail before the message it promised could print; uploads carry the retry/http1.1 hardening this repo's other upload steps already learned to need; verification compares served size against built size, because a status code only proves something is served; and the release workflow now fails on empty artifacts instead of publishing a release with no AppImage. The metainfo file is installed as `Triple-C.appdata.xml`. appimagetool derives the name it looks for from the .desktop basename, so under the id-based name it warned the metadata was missing on every build while this script reported it present. Now it prints "AppStream upstream metadata found in usr/share/metainfo/Triple-C.appdata.xml" — the AppStream id inside the file is unchanged and is what identifies the component. Two review hypotheses did not hold and nothing was changed for them: `set -e` does not abort on a failing `&&` list mid-script, and my claim of a `trap` reassignment was wrong — there is one trap, installed once. Verified against the real 0.4.19 artifact: exit 0, one AppImage beside the release, channel pair in its own directory, appimagetool reporting the metadata found, and the wayland fallback intact. Guards exercised individually — the duplicate one bites, the exact-match one rejects an impostor carrying the tag, the empty directory reports cleanly, and all four publisher preconditions refuse rather than half-publishing. Header parsing for the size check was tested against a real redirecting GitHub asset URL. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011YPqHpjV4EL6RNEwrRKqQm |
||
|
|
d561ce03d5 |
Anchor the update channel tag, and stop shipping a duplicate AppImage
Secret Scan / scan (push) Successful in 6s
Build App (Preview) / compute-version (pull_request) Successful in 3s
Secret Scan / scan (pull_request) Successful in 4s
Build App (Preview) / create-release (pull_request) Successful in 1s
Build App (Preview) / build-linux (pull_request) Failing after 1m49s
Build App (Preview) / build-macos (pull_request) Successful in 2m57s
Build App (Preview) / build-windows (pull_request) Successful in 16m16s
Build App (Preview) / prune-previews (pull_request) Skipped
Two defects in the update channel, both visible in 0.4.20 and 0.4.21. **The channel tag does not survive.** `publish-update-channel.sh` created the GitHub release, uploaded both assets and verified each URL returned 200 — the job log shows it succeeding at 00:38. By 13:04 the tag was gone and every installed copy was checking a 404. Gitea push-mirrors this repo to GitHub every four hours, and a mirror push deletes remote refs with no local counterpart. `linux-latest` was created by GitHub's release API and never existed as a Gitea tag, so the mirror removed it. Versioned tags were never affected because `create-tag` creates them in Gitea first. So the tag is now anchored in Gitea, and before the GitHub release rather than after, so there is no window where the two disagree. Its absence fails the step instead of warning, because it is the only thing keeping the channel alive. Worth stating plainly: publishing correctly is not evidence the channel still works, and the verification that passed at 00:38 could not have caught a failure that arrives twelve hours later. **Every release carried the AppImage twice.** The channel's stable-named copy sat beside the versioned one, where the release job's `*.AppImage` glob picked it up — so v0.4.21 published `Triple-C_0.4.21_amd64.AppImage` and `Triple-C_x86_64.AppImage`, byte-identical at 86,686,200 bytes each, and `sync-to-github` copied both to the mirror. 80 MB of duplicate per release, under a name that reads like a different build. That is how it was noticed. The channel pair now lives in `bundle/appimage/update-channel/`, out of the glob's reach, and a guard fails the build if more than one AppImage is left beside the release. Verified by planting a second one: it fails. One appimagetool quirk found while moving it — zsyncmake writes the .zsync into the working directory, not beside the image it describes, so it has to be collected rather than assumed in place. The existing guard caught that too. Verified against the real 0.4.19 artifact: exactly one AppImage at top level, the channel pair in its own directory, update string still resolving to the fixed tag, and the wayland fallback intact. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011YPqHpjV4EL6RNEwrRKqQm |
||
|
|
9fadfbc37a |
Make the AppImage updatable, and drop the deb and rpm
Secret Scan / scan (push) Successful in 4s
Build App (Preview) / compute-version (pull_request) Successful in 4s
Secret Scan / scan (pull_request) Successful in 3s
Build App (Preview) / create-release (pull_request) Successful in 2s
Build App (Preview) / build-macos (pull_request) Successful in 2m43s
Build App (Preview) / build-windows (pull_request) Successful in 4m51s
Build App (Preview) / build-linux (pull_request) Successful in 5m19s
Build App (Preview) / prune-previews (pull_request) Successful in 1s
An AppImage manager can adopt the current build but never update it: the image carries no update information, which is the string that tells such a tool where to look for a newer one. It also carries no AppStream metadata, so a manager has nothing to show but a filename — appimagetool has been warning about that on every build — and linuxdeploy leaves `Categories=` empty, which files the app nowhere in a desktop menu. All three are fixed while the image is already unpacked for the wayland fix, so the cost is a few lines rather than a second pass. `unbundle-wayland-client.sh` is now `finalize-appimage.sh`, since it does more than unbundle. The update URL is a **fixed** `linux-latest` tag on the GitHub mirror, which is where updates are pulled from — deliberately not `releases/latest`. `latest` follows whichever release is newest, and the Gitea-to-GitHub backfill creates one GitHub release per Gitea tag, including the `-win` and `-mac` tags that carry no AppImage. A URL that can resolve to a release with no AppImage in it fails on users' machines and nowhere else. The output is named for that tag too, and that is not cosmetic: zsync records a *relative* filename which a client resolves against the .zsync URL it fetched, so a versioned name would send every client after the build it already has. Verified by reading the generated header — `Filename: Triple-C_x86_64 .AppImage` — and the image's own `.upd_info` section, which is where the tag actually lives. My first guard checked the .zsync for the tag and failed correctly, which is how that distinction got found rather than shipped. Range requests were confirmed against the mirror before building on them: 206 with a correct content-range, so updates are real deltas rather than an 85 MB re-download. The .deb and .rpm go. They are two more artifacts to build, publish and keep working for an audience already served by the one file that runs on every distribution, and neither could ever self-update — which is now the difference that matters. Older releases keep theirs. The Linux job passes `--bundles appimage` rather than changing `tauri.conf.json`, so macOS and Windows are untouched. Verified against the real 0.4.19 artifact: it repacks, the AppStream file and filled-in Categories land inside the image, the update string resolves to the fixed tag, and the wayland fallback still holds. Both publisher failure paths refuse rather than half-publishing — no token, and missing artifacts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011YPqHpjV4EL6RNEwrRKqQm |
||
|
|
a3bdf6f4da |
Let the host's libwayland-client win in the AppImage
Secret Scan / scan (push) Successful in 3s
Build App (Preview) / compute-version (pull_request) Successful in 3s
Secret Scan / scan (pull_request) Successful in 3s
Build App (Preview) / create-release (pull_request) Successful in 1s
Build App (Preview) / build-macos (pull_request) Successful in 2m52s
Build App (Preview) / build-linux (pull_request) Successful in 5m20s
Build App (Preview) / build-windows (pull_request) Successful in 5m21s
Build App (Preview) / prune-previews (pull_request) Successful in 1s
The AppImage came up blank on CachyOS with `Could not create default EGL display: EGL_BAD_PARAMETER. Aborting...`, and the DMA-BUF workaround already in `main.rs` did not help — verified by finding the flag compiled into the shipped 0.4.19 binary, where it runs unconditionally on Linux. It is a different fault with the same error text. linuxdeploy bundles `libwayland-client.so.0` as a GTK dependency and `AppRun.wrapped` puts the bundled directory ahead of the host's, so the host's Mesa resolves against our copy. `libEGL_mesa.so.0` — the driver libglvnd's `libEGL.so.1` dlopens — has a hard DT_NEEDED on that library, so when its symbols will not resolve the driver never loads, glvnd is left with none, and `eglGetDisplay` reports no display. That is why `GDK_BACKEND=x11` does not dodge it, and why the symptom is a bad-parameter error rather than a link failure. Bisected on the reporter's machine against the released artifact — removing `libwayland-client.so.0` from the AppDir cleared the abort, while removing `libwayland-egl` or `libepoxy` did not. The bundled copy (Ubuntu 22.04, wayland 1.20) is missing eleven symbols their wayland 1.26 exports, including `wl_proxy_get_display`, `wl_proxy_get_queue`, `wl_display_create_queue_with_name` and `wl_fixes_interface`. Bundling a newer wayland would defer this, not fix it: the floor is set by the host's Mesa, which updates independently of our releases, so any version we pick is one release away from being too old again. This is a host-coupled library like libGL and libdrm — the only correct version is the host's. So the copy is demoted rather than deleted. It moves off the loader path into `usr/lib/wayland-fallback`, and a hook adds that directory back only when the host has no libwayland-client of its own — so a host without one still starts. The ordering is safe because `AppRun.wrapped` appends the inherited LD_LIBRARY_PATH after its own entries, making the hook a fallback and never an override. AppRun sources hooks by name rather than globbing, so it is patched to source this one. X11-only hosts are unaffected: libwayland-client is a package dependency of Mesa and GTK, so it is present even on a machine with no display server at all — confirmed on a headless container with neither DISPLAY nor WAYLAND_DISPLAY set. And the AppImage already runs as an X11 client everywhere, since linuxdeploy's own hook forces GDK_BACKEND=x11. Verified against the real 0.4.19 artifact rather than a synthetic AppDir: it repacks, the binary and AppRun survive, and both hook branches were exercised — host-has-it leaves LD_LIBRARY_PATH untouched, and a debian:12-slim container with no wayland at all engages the fallback. The comment in `main.rs` quoted this exact error as one the DMA-BUF flag fixes. That claim sent this investigation down the wrong path first, so it is corrected rather than left to do it again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011YPqHpjV4EL6RNEwrRKqQm |
||
|
|
7a5c0c1f13 |
Retire the Arch package, document AppImage desktop integration
The `triple-c-bin` package was never on the AUR, so installing it meant
downloading a file and running `pacman -U` — the same gesture as making an
AppImage executable, for a second artifact to keep building. And being
`workflow_dispatch`-only it reached 1 release in 28 (only v0.4.16 has a
`.pkg.tar.zst`), while HOW-TO-USE.md told Arch and CachyOS users to download
it from every release. A distribution channel that is absent 27 times out of
28 is worse than not promising one.
`packaging/arch/` and `.gitea/workflows/publish-arch-package.yml` are
preserved whole on `hold/arch-packaging`, the same way the disk panel and
drag-out work were held rather than deleted. What would make an Arch package
worth having is an AUR account and its SSH key as a repo secret — both
one-time manual steps that never happened; the workflow's own header already
said as much about its AUR push step.
This also closes the gap that prompted the review: nothing validated the
PKGBUILD until someone manually dispatched the workflow, making it the only
packaging path with no CI coverage. Removing it removes the untested surface
rather than adding a job to test something nobody installs.
In its place, `scripts/install-appimage.sh` does what a package manager's
install hooks would. An AppImage carries a `.desktop` entry and icons inside
itself, but nothing on the host reads them, so it never appears in the app
launcher. The script extracts the bundled icons into the user's icon theme
and writes a launcher entry — no sudo, nothing outside `~/.local/share`, and
the AppImage itself is never copied or moved.
Two details it gets right on purpose:
* The `Exec` line is rewritten, not copied. The bundled entry says
`Exec=triple-c`, which resolves only inside the running AppImage's own
mount — a verbatim copy gives a launcher entry that starts nothing.
* Extraction uses `--appimage-extract`, which needs no FUSE, so the script
works on a machine where *running* the AppImage would first need
`fuse2` installed. That requirement is now documented too: Arch and
CachyOS do not ship FUSE 2 by default.
Verified against the real artifact — the AppImage from this repo's own
preview-3a49a67 release: 4 icon sizes install, `desktop-file-validate` passes
with no warnings, `--uninstall` leaves nothing behind, and shellcheck is
clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ApLYH6ybHwQFkMCtKuHrrV
|
||
|
|
f2cfc0be8f |
Also attach the Arch package to the matching Gitea release
The workflow only ever uploaded to the GitHub release — the Gitea release for the same version (the plain, unsuffixed vX.Y.Z tag build-app.yml's Linux job creates, which already holds the .deb/.rpm/.AppImage) never got it, so it looked missing to anyone checking releases on Gitea instead of GitHub. New step mirrors build-app.yml's own Gitea upload step exactly: same get-or-create-by-tag, delete-existing-asset, upload-as-octet-stream shape, same REGISTRY_TOKEN secret. Verified the read side (release lookup, asset listing) against the real v0.4.16 release before writing this — resolves to the correct release id and correctly finds no existing asset yet. |
||
|
|
3c8296843f |
Skip AUR for now — attach the built Arch package to the GitHub release
Publishing to the AUR needs a maintainer AUR account and its SSH key registered as a secret here, neither of which exists yet. Rather than leave the workflow permanently failing at that last step, it now stops short of AUR and instead uploads the built .pkg.tar.zst to the same GitHub release it built from, as a plain downloadable asset (`pacman -U` to install). The AUR-push step is still in this file's git history if that setup happens later. Renamed publish-aur-package.yml -> publish-arch-package.yml to match. The render/validate steps are unchanged; new here is capturing the exact built package filename from inside the build container (makepkg is the only thing that actually knows it) and an upload step that follows the same create-or-reuse-release, strip-upload_url, POST-octet-stream pattern build-app.yml and backfill-releases.yml already use for GitHub assets, plus a delete-existing-asset-first step so a re-dispatch for an already-packaged version replaces rather than 422s. Verified with a real Docker run end to end: rendered a real PKGBUILD, built a real (synthetic) .deb through makepkg + namcap in an archlinux container, confirmed the container exits 0, and confirmed the exact package filename it captures (triple-c-bin-<version>-1-x86_64.pkg.tar.zst) round-trips out via docker cp intact. |
||
|
|
6dcdeb89cb |
Fix PKGBUILD render silently no-op'ing on every AUR publish run
The "Render PKGBUILD" step's Python heredoc built its old_source match
string via an f-string, escaping literal braces as `${{pkgver}}` — which
put that exact four-character sequence directly in this workflow file's
own YAML text. Gitea Actions scans a run: block for `${{ ... }}` and tries
to evaluate whatever's inside as one of its own expressions before the
shell ever sees the script; "pkgver" isn't a valid expression context, so
every run has been failing that interpolation and emptying the step
instead of raising anything visible there. The next step's `makepkg` then
failed with "PKGBUILD does not exist" — the actual point of failure was
one step earlier and unrelated to AUR credentials.
Rebuilt the same match string with a "$" variable and plain concatenation
so the file's own text never contains the trigger sequence. Verified by
extracting the exact heredoc and running it standalone against the real
PKGBUILD template — renders identically to the intended output.
|
||
|
|
b3d07bda09 |
Fix real workflow bugs a review found: dead bind mount, blind error gate
A review found the "Validate with makepkg and namcap" step's bind mount
(docker run -v "$PWD/rendered:/work") would very likely fail on Gitea's
own act_runner: a containerized job's $PWD isn't a path the daemon's host
can resolve, so the mount would silently attach an empty directory
instead of failing loudly — the same class of problem noted elsewhere for
this exact environment. Switched to docker create + docker cp (in and
back out) + docker start -a, the pattern already validated locally, which
works regardless of where the daemon actually lives.
Also found and fixed, most severe first:
- The namcap error gate (`grep -q "^[a-zA-Z0-9_-]*bin E:"`) only matched
one of namcap's two line shapes for reporting an error
("triple-c-bin E: ...") and missed the other ("PKGBUILD
(triple-c-bin) E: ...") entirely — confirmed by reproducing both against
a real namcap run. The PKGBUILD-level half of the safety net was dead.
Replaced with a plain `grep -q " E: "`, confirmed to match both real
shapes (and a split-package variant) and nothing else.
- package()'s `ar x "Triple-C_${pkgver}_amd64.deb"` named the asset
literally, defeating the whole point of the resolve step discovering
the real filename from the release instead of assuming a pattern — a
future Tauri bundler naming change would still break here with an
opaque error. Changed to `ar x ./*_amd64.deb`, which `source=()` already
guarantees matches exactly one file.
- `pacman -Sy` before installing packages is the canonical Arch partial-
upgrade footgun; changed to `pacman -Syu --noconfirm --needed`.
- `${{ inputs.version }}` was interpolated directly into a shell step
instead of routed through `env:`, unlike every other step in the file.
- `git push origin master` assumes the local branch name after cloning a
brand-new (not-yet-created) AUR repo's empty state is `master`, which
depends on the runner's own `init.defaultBranch` if the server sends no
symref. `git push origin HEAD:master` is unambiguous either way.
- The private key was written with a plain redirect then chmod'd after,
leaving a window where it's world-readable; now created at its final
mode first via `install -m 600 /dev/null`. Added `-o IdentitiesOnly=yes`
so a runner ssh-agent can't offer a different key first.
- Added GH_PAT auth to the api.github.com calls, matching every other
workflow in this repo, to avoid the unauthenticated 60/hour rate limit.
- Fixed two comments: the `options` comment credited `!debug` for
suppressing the empty debug-package directory, when it's actually
`!strip` doing that (verified in a real build); and documented in the
README that a hand-edit made directly in the AUR repo is silently
reverted by the next dispatch, since every run renders fresh from this
repo's template.
All of the above re-verified with the same real end-to-end methodology as
the original commit: real makepkg build, real namcap lint (clean), and
the exact updated docker create/cp/start sequence run against a live
container.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FGjXq6fqtAFHdbhk4f3PfZ
|
||
|
|
e025a7441a |
Add a native Arch/CachyOS package via its own AUR publish workflow
Part of triple-c#34's third ask ("I would like to also have an
Arch/CachyOS native version as well"), addressed separately from the
Wayland crash fix (fix/wayland-webkit-egl-crash) since it's an unrelated
feature, not a bug.
packaging/arch/PKGBUILD is a "-bin" AUR package repackaging the same .deb
build-app.yml already produces — no Rust/Node toolchain needed to install
it, and the user gets exactly the binary the project ships and tests.
Verified end to end against a real release (v0.4.14) rather than going by
Tauri's generic docs: downloaded the actual .deb, ldd'd the actual binary
to ground-truth `depends` (dropped `pango` and `libayatana-appindicator`
from an earlier draft — the first is already pulled in transitively by
gtk3, the second was never linked at all since this app has no tray icon
or menu), and ran a real makepkg/namcap/pacman -U cycle. namcap caught a
real issue this way (missing license file under
/usr/share/licenses/triple-c-bin/), now fixed by fetching LICENSE
alongside the .deb.
.gitea/workflows/publish-aur-package.yml does the actual publishing:
given a version (or "latest"), it finds that release's real Linux asset
on GitHub, downloads it, computes real checksums, renders the PKGBUILD
template, validates the result with makepkg and namcap inside a real
Arch container, and pushes to AUR. workflow_dispatch only, deliberately —
the same reasoning that killed sync-release.yml in triple-c#32 (releases
are assembled by build-app.yml across three separate platform jobs, so
there's no single automatic event that fires only once the Linux .deb
this needs actually exists) applies here too.
Requires a repo secret this workflow cannot set up itself:
AUR_SSH_PRIVATE_KEY, from an AUR account that has already created (or
been given co-maintainer access to) triple-c-bin — both one-time manual
steps on aur.archlinux.org. Until that secret exists, the workflow fails
loudly at the push step rather than silently doing nothing. See
packaging/arch/README.md for the full maintenance flow.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FGjXq6fqtAFHdbhk4f3PfZ
|
||
|
|
945883bb9d |
Actually offer a preview the release it precedes, and fix two more gaps
Secret Scan / scan (push) Successful in 4s
Build App (Preview) / compute-version (pull_request) Successful in 3s
Secret Scan / scan (pull_request) Successful in 4s
Build App (Preview) / create-release (pull_request) Successful in 1s
Build App (Preview) / build-macos (pull_request) Successful in 2m39s
Build App (Preview) / build-windows (pull_request) Successful in 4m50s
Build App (Preview) / build-linux (pull_request) Successful in 7m25s
Build App (Preview) / prune-previews (pull_request) Successful in 6s
An Opus review of the previous commit found its headline claim didn't hold: a preview and the release it precedes compute to the identical numeric version by construction, but check_for_updates compared with a strict `>` against the bare CARGO_PKG_VERSION (never the suffixed display string), so `(0,4,13) > (0,4,13)` is false and the release was never offered. Plain semver ordering doesn't make a `-preview.<sha>` suffix sort below the same numeric release on its own here, since the comparison never sees the suffix at all. pick_update now takes is_preview_build, derived from whether TRIPLE_C_BUILD_SUFFIX was baked in, and relaxes that one comparison to `>=` — so "a release exists at my own number" reads as an update. A production build still requires strictly newer. Also: ported build-app.yml's `git tag --points-at HEAD` guard into the preview version computation. Without it, workflow_dispatch (which this workflow allows on main, not just PR builds) run on a commit a release was already cut from would compute one past that release — reintroducing "preview outranks production" through the manual-dispatch door. And corrected two comments that claimed the prerelease filter was currently a no-op: backfill-releases.yml mirrors every Gitea release to GitHub unfiltered, prerelease flag included, so it's real defence-in-depth against a dispatched backfill leaking a preview release, not a no-op. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FGjXq6fqtAFHdbhk4f3PfZ |
||
|
|
b71e15c2c0 |
Make preview versions monotonic and distinguishable from production
Secret Scan / scan (push) Successful in 6s
Build App (Preview) / compute-version (pull_request) Successful in 3s
Secret Scan / scan (pull_request) Successful in 3s
Build App (Preview) / create-release (pull_request) Successful in 1s
Build App (Preview) / build-macos (pull_request) Successful in 2m41s
Build App (Preview) / build-windows (pull_request) Successful in 4m51s
Build App (Preview) / build-linux (pull_request) Successful in 6m26s
Build App (Preview) / prune-previews (pull_request) Successful in 4s
build-app-preview.yml computed its patch number as `git rev-list --count <latest tag>..HEAD` — the exact formula build-app.yml itself documents as broken and replaced (#26): a distance from whichever tag sorts highest, not a counter, so it resets to zero on every release and previews went backwards (0.4.62 -> 0.4.0) the moment one landed. Ported the same "one past the highest patch already used" computation build-app.yml uses for real releases, reading the same tags (including -mac/-win suffixes), so a preview built right before a release now computes the exact number that release is about to take — semver already orders `0.4.12-preview.<sha> < 0.4.12`, so a preview user is offered the release the moment it ships instead of being silently pinned forever. The installed preview's reported version was also indistinguishable from production: the bundle's own version field strips the `-preview.<sha>` suffix before touching tauri.conf.json/Cargo.toml/package.json, since the Windows MSI's ProductVersion has no room for one. Rather than risk that (unverifiable without an actual Windows build), preview builds now bake the suffix into the binary separately via a TRIPLE_C_BUILD_SUFFIX build-time env var, and get_app_version() appends it when present — a production build sets nothing, so this is a no-op there. Also: added `prerelease` to `GitHubRelease` and filter on it in check_for_updates (currently a no-op against real data — nothing mirrored to GitHub is ever prerelease:true — but the updater is no longer structurally incapable of enforcing a channel split if one is ever made explicit). And deleted sync-release.yml: workflow_dispatch-only, reading gitea.event.release.* fields a manual dispatch never populates, so it could never have actually run; build-app.yml's inline mirror already does the same job. Refactored check_for_updates' filtering into a pure, testable pick_update helper (this file had no tests before), and added tests for it and the new get_app_version suffix handling. Fixes #32. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FGjXq6fqtAFHdbhk4f3PfZ |
||
|
|
68b73a9102 |
Refuse a commit that adds something shaped like a credential
Secret Scan / scan (push) Successful in 4s
Build App (Preview) / compute-version (pull_request) Successful in 4s
Secret Scan / scan (pull_request) Successful in 5s
Build App (Preview) / create-release (pull_request) Successful in 2s
Build App (Preview) / build-macos (pull_request) Successful in 2m34s
Build App (Preview) / prune-previews (pull_request) Canceled after 0s
Build App (Preview) / build-linux (pull_request) Canceled after 3m23s
Build App (Preview) / build-windows (pull_request) Canceled after 3m26s
The companion to the fixture removal. A site-admin token sat in a test file for
92 commits and fourteen days on a public mirror, past five audit rounds and two
independent reviews, because all of them read the code under change and this was
not under change. A grep would have caught it the first day.
`scripts/scan-secrets.sh` is that grep, in three rules:
* vendor-prefixed credentials — `ghp_`, `github_pat_`, `glpat-`, `xox*-`,
`sk-`, `AKIA`/`ASIA`, `ya29.`, `AIza`, `npm_`, `dckr_pat_`. Shape alone
identifies these, so there is no context to get wrong.
* `BEGIN … PRIVATE KEY` blocks.
* an opaque literal assigned to a secret-shaped name — the rule that would
have caught this one.
The third rule needs **both** halves, and that is what makes it usable rather
than another disabled check. Measured before writing it: an entropy-only rule
flags 317 literals in this tree, and name-proximity alone flags four, three of
which are `secure::get_project_secret(&id, "aws-secret-access-key")` — a
keychain *key name* sitting next to the word `secret`. Requiring the literal
itself to be hex or base64 with no word structure is what excludes those.
Validated rather than asserted:
* **0 false positives** across every tracked file.
* **Catches the real incident** — `--range 9b2f4fe~1..9b2f4fe` is refused.
* Twelve shaped cases pass and fail as intended, including a sha256 in an
`assert_eq!`, a git sha in a comment and the new dummy fixture, none of
which trip it.
* The hook was proved to block an actual `git commit`, not just to exist.
Two halves, because each covers the other's gap:
* `.githooks/pre-commit`, enabled per clone by `npm run hooks`. Git will not
let a repository set its own hooks path — cloning would then be enough to
run its code — so this is opt-in everywhere and `--no-verify` skips it.
* `Secret Scan`, which nobody can bypass. It carries **no `paths:` filter** on
purpose: the leak lived in `app/**` and `build.yml` only runs for
`container/**`, so a path-filtered scan would have missed the very thing it
exists for. It scans the whole tracked tree rather than a range, because a
wrong range fails *open* and the full pass takes 0.5s.
Also fixed while here: `core.hooksPath` in this clone pointed at
`/workspace/.git/hooks`, a directory that does not exist — so git hooks were
disabled outright and anything dropped in `.git/hooks` would have been ignored
in silence. A hook that never runs is worse than no hook, because the checklist
says it is there.
`--tracked` skips binaries. Feeding a blob to grep gets "binary file matches"
instead of the line, so a genuine finding inside one would arrive as a sentence
nobody can act on.
A line ending `pragma: allowlist secret` is skipped — wordy on purpose, so it
reads as a claim and leaves something greppable.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHL9ty7arp8FHwvE77ne7y
|
||
|
|
3741e0fef5 |
Number releases by the highest one already published, not by drift
The Linux release upload failed with a bare "exitcode '1'" and no output. The cause was not the upload: compute-version handed it a version that had already been released three days earlier. The patch number was `git rev-list --count <highest tag>..HEAD` — how far HEAD has drifted from whichever tag sorts highest, which resets to zero every time a tag is cut. It is not a counter, and the published history is what the old formula returned at each point: v0.4.0 -> 3 commits -> v0.4.3 looked fine v0.4.3 -> 4 commits -> v0.4.4 fine by luck, 4 > 3 v0.4.4 -> 2 commits -> v0.4.2 went backwards v0.4.4 -> 6 commits -> v0.4.6 jumped, skipping .5 v0.4.6 -> 3 commits -> v0.4.3 already taken So the line published 0.4.0, 0.4.3, 0.4.4, 0.4.2, 0.4.6 in that order, never used 0.4.1 or 0.4.5, and then came back round to 0.4.3. The patch is now one past the highest already used. Suffixed tags count towards that: create-tag is skipped whenever a platform job fails, so a run can publish v0.4.7-mac and never create the plain v0.4.7, and reading only unsuffixed tags would hand the same number out twice. A commit that is already tagged reuses its own tag, so re-running a build does not mint a version. Reusing a number was doing real damage, not just failing. macOS and Windows delete-then-upload each asset, so they took the duplicate in their stride and rewrote v0.4.3-mac and v0.4.3-win — public since Aug 11 — with today's binaries. Linux is the only platform that failed, and failing was the correct outcome; its v0.4.3 assets are the only ones still original. Linux also gets the idempotent get-or-create the other two already had, plus `set -euo pipefail` and `-fsS`. Its `curl -s` with no `-f` is why a 409 produced no diagnostic at all: the HTTP error was swallowed, the id grep came back empty, and the step died without ever printing why. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
ab747ce53d |
Say what "open in container" is doing, and land on the pane doing it
Build App (Preview) / compute-version (pull_request) Successful in 4s
Build App (Preview) / create-release (pull_request) Successful in 1s
Build App (Preview) / build-macos (pull_request) Successful in 2m40s
Build App (Preview) / build-linux (pull_request) Successful in 5m33s
Build App (Preview) / build-windows (pull_request) Successful in 5m40s
Build App (Preview) / prune-previews (pull_request) Successful in 1s
Opening a page is a container probe, a browser launch, a page load and often a viewer start — several seconds during which the only feedback was the click itself. Worse from a terminal, where the result appears in a pane the user is not looking at. So: the backend emits progress on the existing `container-progress` channel at each step, the Browser tab renders that line whenever it is set — the progress belongs to the project, not to whoever pressed the button, which is what lets a terminal-initiated open report anywhere at all — and the terminal's "In container" now selects the project's Browser tab before starting, so the line has somewhere to appear. Selecting a sub-tab from outside needed a route: `ProjectHome` keeps it in local state, so `openProjectHomeTab` parks a request in the store and the pane consumes it once. Consumed once, so it cannot fight the user's own clicking afterwards. Preview releases now prune themselves to the newest KEEP_PREVIEWS (2), in a job that runs only if all three platforms published — a half-finished run must not evict a good older build. The cleanup workflow's manual sweep stays as the backstop. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
5bd80a05bc |
One build per push: previews carry the PR check
Build App (Preview) / compute-version (pull_request) Successful in 3s
Build App (Preview) / create-release (pull_request) Successful in 1s
Build App (Preview) / build-macos (pull_request) Successful in 2m44s
Build App (Preview) / build-windows (pull_request) Successful in 5m27s
Build App (Preview) / build-linux (pull_request) Successful in 5m36s
Every push to the PR started two workflows on the same commit. build-app.yml ran on pull_request and compiled all three platforms — then published nothing, because every publishing step in it is gated on `gitea.event_name == 'push'`. build-app-preview.yml compiled the same three and published them. Six OS builds per push, half of them unreachable. So the PR trigger moves to the preview workflow, which was already doing the identical compilation and has something to show for it. build-app.yml is now push-to-main and manual dispatch only: releases. Two things a pull_request event changes, handled rather than inherited: `gitea.sha` can be the merge ref — not the commit anyone is testing, and not something to hang a tag on — so the release's target comes from `git rev-parse HEAD` in the checkout; and `gitea.ref_name` is the PR number, so the release body uses `gitea.head_ref` when there is one. The cost is one prerelease per PR commit touching app/**, which the existing Cleanup Old Releases sweep already prunes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
5b18ce804f |
Start the 0.4 line, and give previews the version they are previewing
Build App / compute-version (pull_request) Successful in 5s
Build App / build-macos (pull_request) Successful in 2m34s
Build App / build-windows (pull_request) Successful in 5m17s
Build App / build-linux (pull_request) Successful in 5m23s
Build App / create-tag (pull_request) Skipped
Build App / sync-to-github (pull_request) Skipped
Two version problems, one of them mine. **Previews claimed x.y.0.** The preview workflow hard-coded the patch number, so every preview installer reported 0.3.0 whatever it contained, while the real build computes the patch from tags. It now runs the same computation, so a preview is labelled with the version the release it previews would carry. **A new minor line started at the wrong number.** `compute-version`'s fallback for "no tag matches this line yet" counted every commit in the repository — fine as a bootstrap, wrong the moment a minor version is bumped: the first 0.4 build would have been 0.4.234. A line nobody has tagged is a new line, and a new line starts at .0. With those fixed, VERSION moves to 0.4 — tab reordering, the browser pop-out, opening pages in the container's browser and the Playwright install fix are more than a patch bump. The next release is v0.4.0; today's HEAD would have been 0.3.90 on the old line. `app/package.json`, `package-lock.json`, `tauri.conf.json` and `Cargo.toml` follow to 0.4.0. CI patches all four per build, so they are the dev-time defaults rather than the source of truth — but a local `tauri dev` shows them, so they should not still say 0.3. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
63f3c54b95 |
Publish preview builds as a prerelease instead of workflow artifacts
Build App / compute-version (pull_request) Successful in 4s
Build App / build-macos (pull_request) Successful in 2m33s
Build App / build-windows (pull_request) Successful in 5m20s
Build App / build-linux (pull_request) Successful in 5m30s
Build App / create-tag (pull_request) Skipped
Build App / sync-to-github (pull_request) Skipped
Workflow artifacts do not work on this Gitea, in two different ways:
* upload-artifact@v4 cannot run at all. @actions/artifact v2's isGhes()
treats any GITHUB_SERVER_URL that is not github.com / *.ghe.com /
*.localhost as GitHub Enterprise Server and throws before making a
single request. act_runner sets it to this instance, so all three
platforms died with GHESNotSupportedError — after paying for the
whole Tauri build (run #265).
* @v3 uploads succeed and the files are downloadable by direct URL,
but Gitea does not *list* them: /api/v1/…/runs/<id>/artifacts returns
total_count 0 and the run page shows nothing (verified on run #267).
A build nobody can find is not a build.
So previews publish the way every other workflow here does: curl to the
releases API. One prerelease per preview, tagged `preview-<sha>`, with
all three platforms' bundles as assets — visible on the Releases page
with stable links.
The release is created in a job the three builds depend on rather than
get-or-created in each. They run concurrently, so per-job creation races
on one tag: the loser gets a 409, and the id parse then yields empty
while the step still reports success — the failure build-app.yml's
macOS job was hardened against after it happened for real. One creator
removes the race instead of handling it.
Asset upload keeps that hardening: delete-then-upload so a re-dispatch
replaces rather than 409s, --http1.1 and retries for the mid-stream
drops the macOS runner has produced (curl exit 92, exit 28), and an
explicit failure when a platform produced no bundles at all.
The `preview-` prefix is load-bearing: cleanup-releases.yml keeps recent
`v<x>.<y>.<z>` releases and separately deletes every release whose tag
does not start with `v[0-9]`, so previews are pruned by the cleanup
already in use and never crowd the real release list. sync-release.yml
is dispatch-only, so none of this reaches GitHub.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
1207a21aae |
Pin the preview build's uploads to upload-artifact@v3
Build App / compute-version (pull_request) Successful in 5s
Build App / build-macos (pull_request) Successful in 2m32s
Build App / build-windows (pull_request) Successful in 5m20s
Build App / build-linux (pull_request) Successful in 5m32s
Build App / create-tag (pull_request) Skipped
Build App / sync-to-github (pull_request) Skipped
Run #265 — this workflow's first ever run — built the app on all three platforms and then lost every bundle at the upload step: GHESNotSupportedError: @actions/artifact v2.0.0+, upload-artifact@v4+ and download-artifact@v4+ are not currently supported on GHES. v4 bundles @actions/artifact v2, whose isGhes() treats any GITHUB_SERVER_URL that is not github.com, *.ghe.com or *.localhost as GitHub Enterprise Server and throws before making a single request. act_runner sets that variable to this Gitea instance, so v4 cannot work here on any runner or any OS — and it fails *after* the whole Tauri build has been paid for. v3 uses the v1 artifact API, which Gitea implements. Both options this workflow relies on, `if-no-files-found: error` and `retention-days`, exist in v3. The other workflows never hit this because they publish by curling the Gitea releases API instead. Noted at the top of the file, with the isGhes rule, so the pin is not "upgraded" back. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
2c014fd752 |
CI: create the WOW64 junctions in the workflow instead of by hand
Build App / compute-version (pull_request) Successful in 6s
Build App / build-macos (pull_request) Successful in 2m29s
Build App / build-windows (pull_request) Successful in 5m52s
Build App / build-linux (pull_request) Successful in 6m12s
Build App / create-tag (pull_request) Skipped
Build App / sync-to-github (pull_request) Skipped
The Windows fix was the only part of it living outside git — two junctions created by hand on the build VM. Rebuild that VM, add a second Windows runner, or reset the SYSTEM profile and Windows builds break again with an error that points nowhere near the cause. Tauri downloads candle.exe, light.exe and makensis.exe, and all three are 32-bit. A runner running as SYSTEM has %LOCALAPPDATA% under C:\Windows\System32\config\systemprofile, and WOW64 redirection serves 32-bit processes reading System32 from SysWOW64, where those directories do not exist. The bundlers cannot see their own folder: candle exits 0x80131700, makensis reports "Unable to start child process, error 0x2", and Tauri surfaces neither — only "failed to run candle.exe". The job now junctions the SysWOW64 view onto the System32 originals when it detects a profile inside System32, and skips entirely otherwise, so a runner running as a normal user is unaffected. Idempotent, and written with goto rather than nested blocks to avoid the delayed-expansion trap that already bit the MSVC step. Verified rather than assumed: the hand-made junctions were deleted from the build VM before this was pushed, so this run has to recreate them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
0aa8315514 |
CI: restore the MSI now that the 32-bit bundlers can resolve their paths
Build App / compute-version (pull_request) Successful in 3s
Build Container / build-container (pull_request) Successful in 32s
Build App / build-macos (pull_request) Successful in 2m26s
Build App / build-windows (pull_request) Successful in 4m51s
Build App / build-linux (pull_request) Successful in 6m11s
Build App / create-tag (pull_request) Skipped
Build App / sync-to-github (pull_request) Skipped
Dropping the MSI did not help: makensis.exe is 32-bit like candle.exe
and failed the same way ("Unable to start child process, error 0x2").
The cause was WOW64 redirection sending 32-bit processes reading
C:\Windows\System32 to SysWOW64, where the toolset directory does not
exist.
The build VM now carries junctions from the SysWOW64 view of
systemprofile\AppData\Local\tauri and systemprofile\.cache to the
System32 originals. Verified on the runner: candle.exe reports WiX
3.14.1.8722 and makensis reports v3.11, both exiting 0 from the path
that previously failed.
Both targets build again, so the .msi comes back. Artifact collection
fails if either installer is missing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
fdc161fd9c |
CI: build NSIS only on Windows, dropping the MSI target
Build App / compute-version (pull_request) Successful in 17s
Build Container / build-container (pull_request) Successful in 1m22s
Build App / build-macos (pull_request) Successful in 2m22s
Build App / build-windows (pull_request) Failing after 4m41s
Build App / build-linux (pull_request) Successful in 5m51s
Build App / create-tag (pull_request) Skipped
Build App / sync-to-github (pull_request) Skipped
WiX's candle.exe/light.exe are 32-bit. On a SYSTEM-run runner Tauri caches WiX under C:\Windows\system32\config\systemprofile\..., and WOW64 redirection sends 32-bit processes to SysWOW64 where that directory does not exist, so candle exits 0x80131700. Tauri aborts the whole bundle on one target's failure, so the MSI was suppressing the NSIS installer too and Windows produced no artifact at all. NSIS is what the project already relies on for Windows upgrades. Drops the .NET 3.5 gate, which existed only for WiX; keeps the MSVC step, which is what makes the app link. Artifact collection now fails when no installer is produced rather than tolerating an empty directory. To restore the MSI, run the runner as a normal user and set --bundles msi,nsis. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
763af91042 |
Revert: LOCALAPPDATA override does not move Tauri's WiX cache
Build App / compute-version (pull_request) Successful in 3s
Build Container / build-container (pull_request) Successful in 32s
Build App / build-macos (pull_request) Successful in 2m23s
Build App / build-windows (pull_request) Failing after 4m35s
Build App / build-linux (pull_request) Successful in 5m6s
Build App / create-tag (pull_request) Skipped
Build App / sync-to-github (pull_request) Skipped
Rust's `dirs` crate resolves LOCALAPPDATA on Windows through SHGetKnownFolderPath, which reads the process token rather than the environment, so the override changed nothing and 32-bit candle.exe still hit WOW64 redirection under the SYSTEM profile. Removing it rather than leaving a plausible-looking non-fix in the workflow. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
03384409e7 |
CI: keep the WiX toolset out of System32 so 32-bit candle.exe can run
Build App / compute-version (pull_request) Successful in 13s
Build Container / build-container (pull_request) Successful in 44s
Build App / build-macos (pull_request) Successful in 2m24s
Build App / build-windows (pull_request) Failing after 4m39s
Build App / build-linux (pull_request) Successful in 6m44s
Build App / create-tag (pull_request) Skipped
Build App / sync-to-github (pull_request) Skipped
WiX's candle.exe/light.exe are 32-bit. A SYSTEM-run runner has %LOCALAPPDATA% under C:\Windows\system32\config\systemprofile, where Tauri caches the WiX toolset — and WOW64 redirection sends 32-bit processes reading System32 to SysWOW64, which has no such directory. The CLR then fails to start with 0x80131700 and Tauri reports only "failed to run candle.exe". Verified: the same binary and identity exits 0 from C:\wixtest and 0x80131700 from the systemprofile path. Pointing LOCALAPPDATA outside System32 avoids redirection, needs no stored credential, and is a no-op for runners already running as a normal user. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
c6bb7fdf1d |
CI: fix the MSVC exit-code check and verify .NET 3.5 before bundling
Build App / compute-version (pull_request) Successful in 6s
Build Container / build-container (pull_request) Successful in 1m48s
Build App / build-windows (pull_request) Failing after 5m14s
Build App / build-linux (pull_request) Successful in 6m2s
Build App / build-macos (pull_request) Successful in 2m24s
Build App / create-tag (pull_request) Skipped
Build App / sync-to-github (pull_request) Skipped
%VSEXIT% and %ERRORLEVEL% inside a parenthesised cmd block are substituted at parse time, not run time, so the installer's real exit code was never read. Uses delayed expansion now. Also checks for the .NET 3.5 runtime before building: WiX candle.exe needs it, and Tauri aborts the whole bundle when the MSI target fails, which silently suppresses the NSIS installer too. Fails early with the exact dism command rather than at bundle time. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
ca0f944712 |
CI: install MSVC build tools on Windows runners that lack them
Build App / compute-version (pull_request) Successful in 4s
Build Container / build-container (pull_request) Successful in 30s
Build App / build-linux (pull_request) Successful in 5m24s
Build App / build-windows (pull_request) Failing after 15m29s
Build App / build-macos (pull_request) Successful in 2m24s
Build App / create-tag (pull_request) Skipped
Build App / sync-to-github (pull_request) Skipped
build-windows failed on this PR with "linker `link.exe` not found", while build-linux and build-container passed — the code was fine, the runner environment was not. The job installs Rust and Node conditionally but assumed the MSVC C++ toolchain was hand-provisioned. A runner without it registers normally, advertises windows-latest, accepts the job, downloads the entire crate graph and only then fails at link time. That also means a bare runner coming online turns a job that would have queued for a capable machine into a failed build. Installs the VC++ workload when vswhere cannot find it, matching the existing conditional Rust and Node steps. rustc locates MSVC through vswhere and the registry rather than PATH, so no dev-shell activation is needed. Installer exit 3010 (success, reboot pending) is treated as success. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
997e1ab3a9 |
Fix Windows release upload: idempotent get-or-create + fail-loud
Build App / compute-version (pull_request) Successful in 9s
Build App / build-macos (pull_request) Successful in 2m36s
Build App / build-windows (pull_request) Successful in 2m50s
Build App / build-linux (pull_request) Successful in 6m26s
Build App / create-tag (pull_request) Has been skipped
Build App / sync-to-github (pull_request) Has been skipped
The cmd-batch upload step POSTed to /releases unconditionally. On a
re-run the v{VERSION}-win tag already exists, so Gitea returns 409, the
findstr id parse yields an empty RELEASE_ID, and uploads go to a
malformed .../releases//assets URL -- all silently swallowed by cmd and
`curl -s`, so the step reported success while attaching no assets.
Rewrite in PowerShell mirroring the macOS job: look the release up by
tag first and create only on 404, throw if the id can't be resolved,
delete same-named assets left over from partial runs before re-upload,
and fail loudly (ErrorActionPreference=Stop, curl.exe -fsS with retries,
$LASTEXITCODE check).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
4588bdf40c |
Make macOS release upload idempotent across re-runs
Build App / compute-version (push) Successful in 2s
Build App / build-macos (push) Successful in 2m25s
Build App / build-windows (push) Successful in 4m42s
Build App / build-linux (push) Successful in 8m54s
Build App / create-tag (push) Successful in 3s
Build App / sync-to-github (push) Successful in 10s
Previous fix only addressed the network flake; a re-run after any upload failure still tripped over the leftover release record. The naive POST /releases got 409 from Gitea, the grep-pipe parser yielded an empty RELEASE_ID, and pipefail aborted with an opaque exit 1. Now: - Look up the release by tag first; reuse on 200, create on 404, fail loudly on anything else. - Validate RELEASE_ID is non-empty and surface the response body if parsing fails. - Before uploading each asset, check whether the release already has an asset with that name (from a partial prior run) and DELETE it so the POST is replace-not-conflict. - Set -euo pipefail explicitly so the script's failure modes are predictable rather than dependent on the runner's default flags. Network hardening from the previous commit (HTTP/1.1, retries, -f) is preserved. Linux and Windows blocks unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
b607cf3681 |
Harden macOS release upload against curl exit 92
Build App / compute-version (push) Successful in 3s
Build App / build-windows (push) Successful in 4m5s
Build App / build-linux (push) Successful in 9m53s
Build App / build-macos (push) Failing after 2m30s
Build App / create-tag (push) Has been skipped
Build App / sync-to-github (push) Has been skipped
macOS upload has been intermittently failing with curl exit 92
("HTTP/2 stream not closed cleanly") for several releases (v0.3.12,
v0.3.10, v0.3.1 all landed with empty asset arrays despite the per-tag
release record being created). It is not a size issue — Linux uploads
the 81MB AppImage on the same Gitea instance without trouble while the
Mac dmg is only 13.6MB.
Adds `--http1.1` to sidestep HTTP/2 stream multiplexing flakes on the
macOS runner, `-f` so HTTP errors no longer fail silently under `-s`,
and `--retry 5 --retry-all-errors --retry-delay 5 --max-time 600` to
absorb transient drops. Linux and Windows blocks unchanged; an inline
note in the YAML calls out where to mirror this if those start
failing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
5360f22b65 |
Make preview build workflow manual-only
Trigger is workflow_dispatch exclusively so builds happen only when explicitly requested from the Actions UI, not on every branch push. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
0316234329 |
Add preview build workflow for non-main branches
Mirrors build-app.yml's three-platform matrix (Linux/macOS/Windows) but uploads the bundles as workflow artifacts instead of creating Gitea releases or syncing to GitHub, so feature branches can be smoke-tested without cluttering the release streams. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
d60124f1bd |
Fix CI: harden version computation and Dockerfile apt retries
Build App / compute-version (push) Successful in 3s
Build App / build-macos (push) Successful in 2m44s
Build App / build-windows (push) Successful in 5m18s
Build App / build-linux (push) Successful in 46m30s
Build App / create-tag (push) Successful in 2s
Build App / sync-to-github (push) Successful in 11s
Build Container / build-container (push) Failing after 3m14s
Two fixes for the v0.3.x initial build failures: 1. **Compute Version step**: When no tags match v0.3.*, `grep` returns exit 1 which under `pipefail` killed the step before the empty-tag fallback could run. Added `|| true` to the pipeline so the fallback (`git rev-list --count HEAD`) runs correctly on first 0.3.x build. 2. **Dockerfile apt-get update**: Transient archive.ubuntu.com mirror sync failures (stale Packages.gz with mismatched hash) broke the GitHub CLI install step. Added a shell retry loop (5 attempts with 10s sleep, clearing /var/lib/apt/lists/* between retries) to both the main system packages step and the GitHub CLI step, plus Acquire::Retries=3 on the other apt-get update calls for transient network failures. Also includes the Cargo.lock 0.2.0 → 0.3.0 rev that went with the previous version bump commit. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
532de77927 |
Add speech-to-text feature using Faster Whisper container
Build App / compute-version (pull_request) Successful in 3s
Build App / build-macos (pull_request) Successful in 2m28s
Build STT Container / build-stt-container (pull_request) Successful in 3m18s
Build App / build-windows (pull_request) Successful in 4m40s
Build App / build-linux (pull_request) Failing after 1m46s
Build App / create-tag (pull_request) Has been skipped
Build App / sync-to-github (pull_request) Has been skipped
Adds a mic button to the terminal UI that captures speech, transcribes it via a Faster Whisper sidecar container, and injects the text into the terminal input. Includes settings panel for model selection (tiny/small/medium), port config, and container lifecycle management. - stt-container/: Dockerfile + FastAPI server for Whisper transcription - Rust backend: STT container management, transcribe_audio IPC command - Frontend: useSTT hook, SttButton, SttSettings, WAV encoder - CI: Gitea Actions workflow for multi-arch STT image builds Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
8301fd3690 |
Add workflow to clean up old releases
Manual workflow that deletes old Gitea and GitHub releases, keeping only the N most recent versions. Defaults to dry-run mode for safe preview before deletion. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
bab1df1c57 |
Switch distribution from Gitea to GitHub/GHCR
Build App / compute-version (push) Successful in 6s
Build Container / build-container (push) Successful in 1m44s
Build App / build-macos (push) Successful in 2m21s
Build App / build-windows (push) Successful in 3m30s
Build App / build-linux (push) Successful in 4m52s
Build App / create-tag (push) Successful in 3s
Build App / sync-to-github (push) Successful in 10s
Work VPN blocks repo.anhonesthost.net, breaking update checks and image pulls. Move all user-facing distribution to GitHub (releases API) and GHCR (container images) while keeping Gitea as the source of truth for development and CI. - CI: push container images to GHCR alongside Gitea registry - App updates: switch releases API to api.github.com, filter by asset filename instead of tag suffix for unified releases - Image updates: switch registry to ghcr.io with anonymous token auth - Container pull: point REGISTRY_IMAGE to ghcr.io/shadowdao/triple-c-sandbox - Rename GiteaRelease/GiteaAsset structs to GitHubRelease/GitHubAsset Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
beae0942a1 |
Add dynamic versioning with VERSION file and tag-based patch counting
Build App / compute-version (push) Successful in 3s
Build App / build-macos (push) Successful in 2m19s
Build App / build-windows (push) Successful in 3m22s
Build App / build-linux (push) Successful in 6m14s
Build App / create-tag (push) Successful in 8s
Build App / sync-to-github (push) Successful in 10s
- Create VERSION file (currently `0.2`) as the single source of truth for major.minor
- Add compute-version job that reads VERSION and counts commits since the last
matching v{major.minor}.N tag to derive the patch number
- Patch resets automatically when VERSION is bumped (no matching tags exist yet)
- Add create-tag job that tags the repo after all platform builds succeed,
so subsequent builds count from the new tag
- All platform jobs now consume the shared version instead of computing their own
- VERSION file changes trigger the build workflow
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
||
|
|
6b49981b3a |
Update build workflow version from 0.1.x to 0.2.x
The computed build version was hardcoded as 0.1.${COMMIT_COUNT} across all
three platform jobs (Linux, macOS, Windows), producing 0.1.x releases even
though the source files were bumped to 0.2.0.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
||
|
|
b7585420ef |
Reconcile project statuses against Docker on startup, update docs and CI
Build App / build-macos (push) Successful in 2m40s
Build App / build-windows (push) Successful in 4m12s
Build App / build-linux (push) Successful in 5m4s
Build Container / build-container (push) Successful in 2m41s
Build App / sync-to-github (push) Successful in 10s
- Add reconcile_project_statuses command that checks actual Docker container state on startup, preserving Running status for containers that are genuinely still running and resetting stale statuses to Stopped - Add is_container_running helper using Docker inspect API - Frontend calls reconciliation after Docker is confirmed available - Update TECHNICAL.md project structure, auth modes, and file listings to match current codebase - Update README.md and HOW-TO-USE.md with MCP servers, Mission Control, file manager, bash shells, clipboard/audio shims, and progress modal docs - Add workflow file self-triggers to CI path filters for build-app.yml and build.yml - Install Mission Control skills to ~/.claude/skills/ in entrypoint Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
574fca633a |
fix: sync build artifacts to GitHub instead of empty source archives
Move GitHub release sync into build-app.yml as a final sync-to-github job that runs after all 3 platform builds complete. This eliminates the race condition where sync-release.yml triggered before artifacts were uploaded to Gitea. The old sync-release.yml is changed to manual-only. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
d642cc64de |
fix: use browser_download_url for Gitea asset downloads
The API endpoint /releases/assets/{id} returns JSON metadata, not the
binary file. Use the browser_download_url from the asset object instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
||
|
|
4f41f0d98b |
feat: add Gitea actions to sync releases to GitHub
Add two new workflows: - sync-release.yml: automatically mirrors releases (with assets) to GitHub when published on Gitea - backfill-releases.yml: manual workflow to bulk-sync all existing Gitea releases to GitHub Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
c9dc232fc4 |
fix: remove Node.js from actual path location on Act runner
The Act runner has Node 18 at /opt/acttoolcache/node/18.20.3/x64/bin/, not at /usr/local/bin/. Use $(dirname "$(which node)") to find and remove the actual binary location before installing Node 22. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
2d4fce935f |
fix: remove old Node.js before installing v22 on Linux runner
The Act runner has Node 18 at /usr/local/bin/node which takes precedence over the apt-installed /usr/bin/node. Even after running nodesource setup and apt-get install, the old Node 18 binary remained in the PATH. Now removes old binaries and uses hash -r to force path re-lookup. Also removes package-lock.json before npm install to ensure correct platform-specific bindings. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
e739f6aaff |
fix: check Node.js version, not just presence, in CI
The Act runner has Node.js v18 pre-installed, so the check `command -v node` passes and skips installing v22. Node 18 is too old for dependencies like vitest, jsdom, and tailwindcss/oxide. Now checks the major version and upgrades if < 22. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
550159fc63 |
Fix native binding error: use npm install instead of npm ci
@tailwindcss/oxide has platform-specific native bindings. The package-lock.json was generated on a different platform, so npm ci installs the wrong native binary. Switching to rm -rf node_modules + npm install lets npm resolve the correct platform-specific optional dependency (e.g., @tailwindcss/oxide-linux-x64-gnu on Linux, oxide-darwin-arm64 on macOS). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
e3c874bc75 |
Fix cargo PATH: use explicit export in every step that needs it
The Gitea Act runner's Docker container does not reliably support $GITHUB_PATH or sourcing ~/.cargo/env across steps. Both mechanisms failed because the runner spawns a fresh shell for each step. Adopted the same pattern that already works for the Windows job: explicitly set PATH at the top of every step that calls cargo or npx tauri. This is the most portable approach across all runner environments (Act Docker containers, bare metal macOS, Windows). Build history shows Linux succeeded through run#46 (JS-based actions) and failed from run#49 onward (shell-based installs). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |