Build/packaging repo for an unofficial Ghostty AppImage. There is no application source here, no package manager, and no test suite — it fetches upstream Ghostty source and produces AppImages for x86_64 and aarch64. The deliverable is a GitHub release.
- All scripts are POSIX
sh(#!/bin/sh) underbin/and must stay POSIX. - Build artifacts are gitignored (
AppDir/,dist/,ghostty-*/,quick-sharun,appinfo,*.AppImage,*.tar.gz). Never try to commit them.
- Lint/format everything:
pre-commit run --all-files(alias:./bin/repo-management.sh lint). Install hooks once withpre-commit install. - Build — must run as root inside an Arch container (e.g.
ghcr.io/pkgforge-dev/archlinux, which CI uses) becausesetup-env.shcallspacman:./bin/setup-env.sh./bin/build-ghostty.sh./bin/bundle-appimage.sh./bin/repo-management.sh validate-appimage
- Focused verification after a build:
./bin/repo-management.sh validate-appimageasserts the embeddedUPINFOmatches the expected channel and the zsyncSHA-1matches the AppImage../dist/*.AppImage --appimage-updateinfoprints the raw update info. ./bin/repo-management.shwith no args prints usage.
All release/CI shell logic lives here as verbs; ci.yaml is orchestration only (triggers, permissions, matrix, artifact actions). Do not inline release shell back into the workflow.
Verbs: tip-version, lint, validate-appimage, detect, validate-tag, resolve-tag, open-pr, publish, publish-tip, tag-tip.
It reads env supplied by the workflow (EVENT_NAME, RELEASE_TAG, INPUT_TAG, GITHUB_REPOSITORY, GITHUB_SHA, GH_TOKEN) and honors UPSTREAM_REPO / RELEASE_BASE_URL overrides for local testing.
VERSIONholds the upstream version. Stable = a cleanX.Y.Z. Nightly:tip-versionwritestip, thenbuild-ghostty.shoverwritesVERSIONwith the tip snapshot (X.Y.Z-main-+hash).+Nreleases:VERSIONstays the upstreamX.Y.Z; only the git tag gets+N(e.g.v1.2.0+1). Never put+NinVERSION— the source URLrelease.files.ghostty.org/<VERSION>/...would break.- Release tags must match
^v<major>.<minor>.<patch>(\+<n>)?$(validate_tagenforces; invalid input fails). Non-version tagstip,glfw,soar-nestare utility and not releases. bundle-appimage.shpicks the UPINFO channel fromVERSION:latestfor cleanX.Y.Z(stable),tipfor snapshot versions (nightly). The asset globGhostty-*<arch>.AppImage.zsyncabsorbs the daily-changing nightly filename.
- Daily schedule builds tip (
tip-version); atagjob force-updates thetiptag and clears its assets, thenrelease_nightlypublishes the prerelease. - The same schedule runs
create_release_pr: if upstreamghostty-org/ghosttyhas a newer stable tag and its CDN artifact exists, it createsrelease/<v>, bumpsVERSION, pushesv<v>, and opens a PR. - Merging that PR (push to
maintouchingVERSION) builds once andrelease_stablepublishes the GitHub release with assets — the only manual step. release: publishedhandles manually published+Nreleases;workflow_dispatchinputstag/publishare the manual override.- PRs are intentionally skipped for VERSION-only changes (
paths-ignore), so the release PR does not build. Thelintjob runs on PRs only.
- Commits: Conventional Commits (
feat(ci): ...,fix(bundle): ...,chore(deps): ...). - Branches:
feature/<x>,fix/<x>,release/<version>.no-commit-to-branchblocks direct commits tomain. - Action updates are handled by Renovate (branches
renovate/*,chore(deps): update ...PRs). Don't hand-edit action pins.
- shellcheck runs at
--severity=warning --shell=sh: bashisms ([[ ]], arrays,local) fail.export VAR="$(cmd)"fails SC2155 — assign first, thenexport. setup-env.shself-installsget-debloated-pkgs/quick-sharunwhen absent (CI provides them viaanylinux-setup-action), so local builds work.GITHUB_REPOSITORYdefaults topkgforge-dev/ghostty-appimageinbuild-ghostty.sh/bundle-appimage.sh; without it local builds embed a brokenUPINFO.setup-env.shstrips.sframe/.rela.sframefrom/usr/lib/*crt*.o— required for GCC 15+ with Zig's self-hosted linker. Keep it.build-ghostty.shderives the Zig version fromghostty-<v>/build.zig.zonand installs it under/opt, symlinking/usr/local/bin/zig.- Workflows are checked by
actionlint+zizmor;.github/actionlint.yamlwhitelists theubuntu-24.04-armlabel. A checkout that persists credentials needs# zizmor: ignore[artipacked], or setpersist-credentials: false.