Skip to content

Linux distribution (install script, apt, AUR) #44

Open
3 of 4 issues completed
Open

Description

@jamescrosswell

Opportunity

A Linux user can't install or update TuiCode with a tool they already have.

The README's only Linux instruction is brew install mentaldesk/tap/tuicode. That asks a Linux user to install Homebrew — a macOS package manager — for one editor. The alternative it offers is downloading a tarball from Releases and putting it on PATH by hand, which never updates: the user finds out there's a new version when they next visit GitHub, if ever.

This bites exactly the user the vision describes — someone who "lives in the terminal, locally or over SSH". The machines they SSH into are Debian, Ubuntu, Fedora and Arch servers, and none of them have Homebrew. The Reach theme says TuiCode should be "installed through the package manager the user already has"; on Linux, today, it isn't installed through any.

Two unknowns sit under every option below, and neither has ever been measured:

  • glibc floor. The Linux legs build on ubuntu-latest (24.04). A NativeAOT binary links against the build host's glibc, so it likely won't start on anything older than glibc 2.34 — which rules out Ubuntu 20.04 and Debian 11, still common on servers people SSH into. Nobody has run the binary on one to find out.
  • ICU. InvariantGlobalization isn't set, so the binary loads libicu at startup and fails without it. Minimal server images and containers often don't have it, and the package name differs per release (libicu72, libicu74, …).

Options considered

  1. Do nothing. Keep Homebrew and the tarballs. Costs nothing and the tarballs genuinely work, but it leaves the README telling Linux users to install a macOS package manager, and nothing ever auto-updates. Rejected: this is a stated theme, not an edge case.
  2. A one-line install script only. curl … | sh detects architecture, verifies the checksum, installs to ~/.local/bin. Covers every distro at once, needs no signing key, no hosted repo and no third-party account, and updates by re-running. But it isn't the user's package manager — apt list --upgradable won't mention it, and curl | sh puts some people off.
  3. Native packages, hosted. .deb + a signed apt repo on GitHub Pages, .rpm, and AUR tuicode-bin — the original idea. This is the only option where apt upgrade picks TuiCode up with everything else. It's also the only one that needs a GPG key the maintainer creates and holds, and an AUR account listed under their name, both of which block work until they exist.
  4. Hand it to distro packagers. Keep the release assets stable and documented and let Arch/Nix/Alpine maintainers package it. Zero ongoing cost and it's how most tools of this size get into the AUR. But it only happens once enough people want it, which is the thing we're trying to cause.
  5. Native packages, unhosted (part of the proposal). Build .deb and .rpm from the tarballs with nfpm and attach them to each release. sudo dnf install <url> and sudo apt install ./tuicode.deb work immediately, dependencies are declared properly, and dpkg -l knows about it. No key, no account, no hosting — but no auto-update either.

Proposed: 2 + 5, then the apt half of 3. The script and the attached packages reach every distro in the first three slices, with nothing waiting on the maintainer. The signed apt repo on Pages follows once the GPG key exists. The AUR half of 3 is deferred: AUR account registration is closed right now, so it's split out as its own Idea (#383) and the install script covers Arch until then.

Proposal

  1. Measure and fix the compatibility floor first. Run the published binary in debian:11, debian:12, ubuntu:22.04 and fedora containers, record the real glibc and ICU requirements, and fix what rules out a distro we want. Add a CI leg that runs --smoke in the oldest supported one, so a future dependency bump can't silently raise the floor again.
  2. install.sh in the repo root, served over raw.githubusercontent.com. Detects architecture, downloads the matching tarball, verifies the .sha256 sidecar, installs to ~/.local/bin, and says how to update.
  3. .deb and .rpm for x64 and arm64 attached to every release, built with nfpm inside the release run, each installed and smoke-tested in a CI container.
  4. A signed apt repo on GitHub Pages, refreshed as part of the release run, so apt upgrade carries TuiCode along. Needs a GPG key.
  5. README gains a Linux block with these commands.

Flatpak and Snap stay out: both sandbox file access, which fights an editor that opens arbitrary paths passed from a shell. Nix is left to the community.

It has to fit the release flow #163 built

#163 has landed its release work (#215 merged), so this is now settled rather than a moving target:

  • bump-tap.yml is gone. .github/workflows/ is ci.yml and release.yml, nothing else. The "react to release: published" model this draft was originally written against no longer exists to copy.
  • A release: published trigger will not fire. The release is created by a-team's shared release-publish.yml using GITHUB_TOKEN, and GitHub deliberately doesn't start workflows from events raised by that token. Slice 4 runs inside the release run, not as a separate reacting workflow.
  • Publishing is locked to main. Since Release publishes only from main, and CI's token is read-only #326 and The tap token lives in a release environment only main can reach #328 the publish job only runs from main, and its push tokens live in a release environment only main can reach. The GPG key for slice 4 belongs in that same environment.
  • The shared workflow already has the right shape. mentaldesk/a-team/.github/workflows/release-publish.yml (pinned at v0.1.3) takes rendered package templates as inputs (formula for the Homebrew tap, scoop for the bucket) and pushes each to its own repo with a packages token. An apt repo is the same shape: a template in TuiCode, a new input and a push step in the shared workflow. That means an issue on mentaldesk/a-team and a pinned release carrying it — the same route Know which build you're running, and ship a release in one step #163 took.

Mockup

$ curl -fsSL https://raw.githubusercontent.com/mentaldesk/TuiCode/main/install.sh | sh
tuicode 0.0.5 (linux-x64)
  ✓ downloaded tuicode-0.0.5-linux-x64.tar.gz
  ✓ sha256 verified
  ✓ installed to ~/.local/bin/tuicode
Run `tuicode` to start. Re-run this script to update.
# Debian / Ubuntu — attached to the release (slice 3)
$ curl -fsSLO https://github.com/mentaldesk/TuiCode/releases/download/v0.0.5/tuicode_0.0.5_amd64.deb
$ sudo apt install ./tuicode_0.0.5_amd64.deb

# Fedora
$ sudo dnf install https://github.com/mentaldesk/TuiCode/releases/download/v0.0.5/tuicode-0.0.5-1.x86_64.rpm
# Debian / Ubuntu — from the repo, once it exists (slice 4)
$ curl -fsSL https://mentaldesk.github.io/apt/key.asc | sudo tee /etc/apt/keyrings/mentaldesk.asc >/dev/null
$ echo "deb [signed-by=/etc/apt/keyrings/mentaldesk.asc] https://mentaldesk.github.io/apt stable main" \
    | sudo tee /etc/apt/sources.list.d/mentaldesk.list
$ sudo apt update && sudo apt install tuicode

Scope

In

  • Measuring the real glibc floor and ICU dependency, and fixing what rules out a distro we want.
  • install.sh in the repo root, exercised in CI against the latest release.
  • .deb + .rpm for x64 and arm64 on every release, each installed and smoke-tested in a CI container.
  • A signed apt repo on GitHub Pages, refreshed inside the release run.
  • README install instructions for Linux.

Out

  • The AUR tuicode-bin package. AUR registration is closed, so no account can be made; split out as its own Idea (On Arch I can't install or update TuiCode with yay #383). install.sh covers Arch meanwhile.
  • A hosted rpm/dnf repo (Copr or similar). The attached .rpm covers Fedora until someone asks.
  • Flatpak, Snap and Nix.
  • Debian/Ubuntu's official archives — that needs a source build and a sponsor.
  • A --version flag. install.sh reads the version from the release tag; adding the flag is #163's explicitly-excluded scope.
  • Terminal config drops like the iTerm2 profile. One binary, no launcher script.
  • Rebuilding the release flow itself. Slices 3 and 4 extend release.yml and a-team's shared publish workflow; Know which build you're running, and ship a release in one step #163 has already put both in place.

Rough breakdown

  1. TuiCode starts on the distros people actually SSH into. Measure glibc and ICU (objdump -T, plus running the binary in debian:11, debian:12, ubuntu:22.04, fedora), fix what rules out a target, and add a CI leg running --smoke in the oldest supported distro. Nothing else can be trusted until this lands. No dependency; can start now.

  2. One line installs TuiCode on any distro. install.sh plus a CI job that runs it in a clean container and then tuicode --smoke. Needs 1.

  3. apt/dnf install it as a real package. nfpm in the release run producing .deb and .rpm for both architectures with correct Depends (and wl-clipboard/xclip as Recommends, since copy falls back to them where the terminal refuses OSC 52, A copy the clipboard refuses lands through the system's clipboard program (#319) #339), plus an install-and-smoke check per format. Needs 1. The release flow it plugs into is in place — Release TuiCode in one click from the Actions tab #215 has merged.

    A reasonable stopping point. Every distro is covered and nothing above waits on the maintainer.

  4. apt upgrade brings TuiCode along. Signed apt repo on Pages, refreshed inside the release run. Needs 3, a GPG key, and the shared workflow support (a-team issue).

  5. README and CONTRIBUTING carry the Linux commands and the updated release diagram. Folded into whichever of 2–4 ships last.

Decided

  • Oldest distro: Debian 12 / Ubuntu 22.04 (glibc 2.35), built on ubuntu-22.04 runners. Yours. Reaching Debian 11 / Ubuntu 20.04 would mean an older build container on every release for machines out of standard support.
  • apt repo on GitHub Pages, signed with a GPG key you create and keep in the release environment. Yours.
  • AUR deferred. Yours. Registration is closed; split out as On Arch I can't install or update TuiCode with yay #383.
  • ICU: invariant on Linux builds only. Mine. libicu is a separate package on Linux whose name changes each distro release, and without it the binary won't start. The cost: on Linux, counts and file sizes (Document info, (1,234 selected), the Open view's entry cap, revert messages) show en-US separators whatever the locale. macOS and Windows keep following the locale.
  • README and CONTRIBUTING updates ship inside each task, not as a slice of their own. Mine.
  • The install script and the packages run in parallel once TuiCode starts on Debian 12, Ubuntu 22.04 and Fedora, even without libicu #414 lands. Mine. They touch different files; both only add to the README's Linux block.

Original idea

Reviewer's original text

Goal

Reasonable install paths for the main Linux distros so users aren't downloading tarballs from GitHub Releases by hand.

Channels to consider

Channel Audience Effort Notes
.deb in a PPA / apt repo Debian/Ubuntu Medium Most-asked-for; can be hosted via Cloudsmith / packagecloud / self-hosted Pages
AUR (tuicode-bin) Arch Low Community-maintained PKGBUILD pointing at GitHub Release tarball; auto-updateable
Flatpak (Flathub) Distro-agnostic GUI/TUI Medium-High Sandboxed; clipboard access via portals; might be awkward for a terminal app
Snap Ubuntu-default Medium Strict confinement makes clipboard/file I/O painful; probably skip
Nix (nixpkgs) Nix users Low Community-maintainable; just needs a derivation
Tarball + install script Everyone else Low Already covered by the release workflow output

Recommended starting set

  1. AUR — lowest effort, fastest user reach in the Arch community.
  2. .deb via apt repo — biggest user base; pick a hosting option and document it.
  3. Tarball + one-liner install script as the fallback for distros not covered above.

Defer Flatpak / Snap / Nix until users ask.

Dependencies

Blocked by: release workflow (produces Linux binaries for x64 and arm64).

Open questions

  • Do we want a single tuicode binary or split into tuicode + a separate launcher script that handles per-terminal config drops (analogue of the iTerm2 dynamic profile)?
  • Wayland clipboard handling — TG v2 should handle this via OSC 52 + wl-clipboard fallback, but worth a smoke test per packaging channel.

Activity

  1. added
    pitchAn a-team pitch: Lead shapes it, reviewer approves it
    on Sep 18, 2026
  2. changed the title [-]Linux distribution (apt, AUR, Flatpak)[/-] [+]Linux distribution (install script, apt, AUR)[/+] on Sep 18, 2026
  3. jamescrosswell commented on Sep 20, 2026

    @jamescrosswell
    ContributorAuthor

    Moved back to Exploring to make room in Pitched for #197 (Focus issues), which is marked Urgent.

    This keeps its Medium priority and its drafted pitch — it comes back to Pitched by itself once a slot frees, with nothing to redo.

  4. jamescrosswell commented on Sep 20, 2026

    @jamescrosswell
    ContributorAuthor

    Promoted to Pitched. It had sat in Exploring a while, so I rechecked it against the code and against #163, and rewrote it. Three things changed:

    • It's restructured as a pitch, with the options laid out. The one I'm proposing is narrower than the draft: install script + .deb/.rpm attached to each release (slices 1–3), with the hosted apt repo and the AUR kept in the breakdown but not gating it. Those two are the only pieces that can't start until something outside the repo exists — a GPG key and an AUR account, both yours to hold permanently. Stopping after slice 3 still covers every distro; it just doesn't auto-update.
    • The release plumbing it assumed is being deleted. bump-tap.yml goes in Release TuiCode in one click from the Actions tab #215, and a release: published trigger won't fire any more either — the release will be created with GITHUB_TOKEN, and GitHub doesn't start workflows from that token's events. So the apt and AUR steps have to run inside the release, next to the Homebrew and Scoop ones, which means raising them on mentaldesk/a-team rather than building them here.
    • One open question is now answered. ICU: go invariant. Every sort and comparison in the codebase is already StringComparer.Ordinal, and the only two culture-sensitive calls are DocumentInfoView's file-size and thousands-separator formatting. InvariantGlobalization drops the libicu dependency and the per-distro package-name problem outright. The visible cost is that a French user sees 1,234 rather than 1 234 — say so if that's not acceptable.

    Three questions left for you, in the pitch: oldest distro to support (I'd say Debian 12 / Ubuntu 22.04), whether you want slices 4 and 5 given the key and account they need, and Pages vs Cloudsmith for the apt repo (I lean Pages).

  5. jamescrosswell commented on Sep 20, 2026

    @jamescrosswell
    ContributorAuthor

    Moved back to Exploring to make room in Pitched for #133 ("Notice when open files change on disk"), which is priority High against this one's Medium.

    It keeps its priority, so it comes back to Pitched by itself once a slot frees — nothing to do here.

  6. jamescrosswell commented on Sep 21, 2026

    @jamescrosswell
    ContributorAuthor

    Promoted from Exploring — there's room in front of you again.

    Refreshed against what's landed since it was drafted: #215 has merged, so the release flow this plugs into is real now, not anticipated. bump-tap.yml is gone, the release is published by a-team's shared release-publish.yml, and that workflow already takes rendered package templates as inputs (formula for the tap, scoop for the bucket). An apt repo and an AUR package are the same shape — a template here, a new input and push step there — which makes slices 4 and 5 cheaper than when this was written. Slice 3 no longer waits on anything.

    Four questions for you, in the pitch. The one that decides how far this goes is 3: slices 4 and 5 need a GPG signing key and an AUR account that are yours to create and hold permanently. Stopping after slice 3 still reaches every distro — just without apt upgrade picking TuiCode up.

  7. jamescrosswell commented on Sep 21, 2026

    @jamescrosswell
    ContributorAuthor

    Moved back to Exploring to make room in Pitched: #171 ("A diff shows me the change I don't want, then makes me go elsewhere to undo it") is High priority against this one's Medium, so it takes the slot.

    This keeps its priority and its drafted pitch — it comes back to Pitched by itself once a slot frees, with no work lost. If you'd rather see Linux packaging sooner, raise its priority and it'll jump the queue.

  8. a-team-app commented on Oct 1, 2026

    @a-team-app
    Contributor

    Back in Pitched: a slot freed up when #138 was approved.

    Rechecked against main; three small updates, the proposal is unchanged:

    The four open questions are unchanged. Question 3 (will you own a GPG key and an AUR account?) decides how far this goes.

  9. jamescrosswell commented on Oct 3, 2026

    @jamescrosswell
    ContributorAuthor
    • Oldest distro to support? I'd say Debian 12 / Ubuntu 22.04 (glibc 2.35), which is what ubuntu-22.04 runners give us for free. Reaching Ubuntu 20.04 / Debian 11 means building in an older container on every release — real ongoing cost for machines that are themselves out of standard support.

    Agreed.

    • Do you want slices 4 and 5 at all, and will you own the key and the account? The apt repo needs a GPG signing key you create and store as a secret; the AUR package is listed under a personal AUR account with your name on it. Both are yours to hold permanently, and neither can start before they exist. If you'd rather not, stopping after slice 3 still covers every distro — just without auto-update.

    Looks like we can't do the AUR account yet - their registration page currently shows:

    New account registration is temporarily closed
    
    Registration on the AUR is paused while we deal with a wave of automated account creation. This is a temporary measure and it is not specific to you or your network. There's no manual registration queue, and we will not be able to respond to requests for new accounts during this time.
    
    Everything else works normally and we apologize for the inconvenience.
    
    Updates are announced on aur-general and the Arch news feed. Please do not script retries against this page - it will not tell you anything sooner than those will.
    
    • Is Pages the right host for the apt repo? GitHub Pages is free and ours, at the cost of holding the key. Cloudsmith's OSS tier hosts and signs for us but adds an outside dependency the release now needs. I lean Pages.

    Sure, let's go with pages.

  10. a-team-app commented on Oct 3, 2026

    @a-team-app
    Contributor

    Thanks. I've updated the pitch with your answers:

    • Debian 12 / Ubuntu 22.04 is the floor, and the apt repo goes on Pages. Both are now under Assumed, marked as your call. Slice 4 assumes you'll create the GPG key and store it in the release environment.
    • AUR has been taken out of this pitch. The install script covers Arch for now. The AUR package is its own Idea, On Arch I can't install or update TuiCode with yay #383, which mentions the closed registration. Give it a priority once registration reopens.
    • ICU stays invariant (my call; it's under Assumed).

    Nothing is left for you to answer. The pitch is now four slices, with a natural stopping point after slice 3.

  11. jamescrosswell commented on Oct 3, 2026

    @jamescrosswell
    ContributorAuthor
    • ICU: go invariant. Mine. Every comparison and sort is already StringComparer.Ordinal; the only culture-sensitive output is thousands separators (N0) and file sizes in Document info, the selection count and revert messages. Blame dates are already ISO. InvariantGlobalization drops libicu and its per-distro package names. The visible cost: a French user sees 1,234, not 1 234.

    Just checking, how does this relate to the Linux distro? Where are we talking about when we say a French user sees 1,234?

  12. a-team-app commented on Oct 3, 2026

    @a-team-app
    Contributor

    It's a Linux-only problem. macOS and Windows ship ICU with the OS. On Linux it's a separate package, libicu, and the binary won't start without it. Minimal servers and containers often don't have it, and its name changes with each distro release (libicu72, libicu74, …), so one .deb can't name the right one for every release. Turning ICU off ("invariant mode") removes the dependency.

    The cost shows up in the counts TuiCode formats with thousands separators. Those would always use en-US style:

    • Document info (line, word and character counts, file size)
    • the status bar's (1,234 selected)
    • the Open view's "first N entries"
    • the revert messages in a diff

    A French user would see 1,234 there instead of 1 234. Sorting, comparisons and dates don't change.

    I've rewritten the assumption so it says this, and scoped it to Linux builds only: macOS and Windows keep following the locale.

  13. a-team-app commented on Oct 4, 2026

    @a-team-app
    Contributor

    Broken down into four tasks, in the order I expect them to land:

    1. TuiCode starts on Debian 12, Ubuntu 22.04 and Fedora, even without libicu #414 TuiCode starts on Debian 12, Ubuntu 22.04 and Fedora, even without libicu. Ready now.
    2. One line installs or updates TuiCode on any Linux distro #415 One line installs or updates TuiCode on any distro. Waits for TuiCode starts on Debian 12, Ubuntu 22.04 and Fedora, even without libicu #414.
    3. apt and dnf install TuiCode from a package attached to each release #416 apt/dnf install from a .deb/.rpm attached to each release. Waits for TuiCode starts on Debian 12, Ubuntu 22.04 and Fedora, even without libicu #414; runs alongside One line installs or updates TuiCode on any Linux distro #415.
    4. apt upgrade keeps TuiCode up to date from a signed apt repository #417 apt upgrade keeps it current from a signed repo on Pages. Waits for apt and dnf install TuiCode from a package attached to each release #416, and is marked blocked on you: it needs your GPG key and a change to a-team's shared publish workflow. Details on apt upgrade keeps TuiCode up to date from a signed apt repository #417.

    Stopping after 3 still covers every distro. README and CONTRIBUTING updates ship inside each task. Everything I settled myself is under Decided.

  14. added
    theme: ReachThe vision theme the a-team Lead says this pitch serves
    on Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    pitchAn a-team pitch: Lead shapes it, reviewer approves ittheme: ReachThe vision theme the a-team Lead says this pitch serves

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions