From 1213173bde236f07445d684ab2893a6676db0e13 Mon Sep 17 00:00:00 2001 From: Tiago de Paula Date: Tue, 25 Aug 2026 08:30:38 -0300 Subject: [PATCH] feat(cli): add pacman silent install source No runtime detection for pacman, since different AUR packages may install binaries at different locations (/opt, /usr/bin, /usr/lib). Only viable detection is 'pacman -Qo ${executablePath}' or something like 'rg ${executablePath} /var/lib/pacman/local/*/files'. Both cases could affect startup time, so pacman is only available via the 'HUNK_INSTALL_SOURCE' environment variable. Either bundled with '--env "HUNK_INSTALL_SOURCE*"' or set at runtime from a custom wrapper. Because of how pacman packages are managed, no update notices or specific update instructions should be shown. --- .changeset/olive-impalas-nail.md | 5 +++++ src/core/install/installSource.test.ts | 11 +++++++++++ src/core/install/installSource.ts | 12 ++++++++++-- src/core/install/latestRelease.ts | 2 +- src/core/install/selfUpdate.test.ts | 8 ++++++++ src/core/install/selfUpdate.ts | 4 ++++ src/core/process/updateNotice.test.ts | 15 +++++++++++++++ src/core/process/updateNotice.ts | 4 +++- 8 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 .changeset/olive-impalas-nail.md diff --git a/.changeset/olive-impalas-nail.md b/.changeset/olive-impalas-nail.md new file mode 100644 index 000000000..27ecf98f1 --- /dev/null +++ b/.changeset/olive-impalas-nail.md @@ -0,0 +1,5 @@ +--- +"hunkdiff": patch +--- + +Support pacman/AUR installs via `HUNK_INSTALL_SOURCE=pacman`. diff --git a/src/core/install/installSource.test.ts b/src/core/install/installSource.test.ts index b5f30f99f..7ec220aeb 100644 --- a/src/core/install/installSource.test.ts +++ b/src/core/install/installSource.test.ts @@ -121,6 +121,17 @@ describe("install source detection", () => { ).toBe("curl"); }); + test("accepts pacman as declared install source", () => { + expect( + detectInstallSource({ + env: { HUNK_INSTALL_SOURCE: "pacman" }, + executablePath: join("/", "usr", "lib", "hunkdiff", "hunk"), + version: "1.2.3", + homeDir: HOME_DIR, + }), + ).toBe("pacman"); + }); + test("keeps npm for a .hunk segment that is not followed by bin", () => { expect( detectInstallSource({ diff --git a/src/core/install/installSource.ts b/src/core/install/installSource.ts index 2ed3237fb..72a5a5dc9 100644 --- a/src/core/install/installSource.ts +++ b/src/core/install/installSource.ts @@ -17,12 +17,20 @@ const INSTALL_DIR_ENV = "HUNK_INSTALL_DIR"; /** Path segments Homebrew always puts above its binaries, on macOS and Linux alike. */ const HOMEBREW_PATH_SEGMENTS = ["cellar", "homebrew", "linuxbrew"]; -export type InstallSource = "npm" | "homebrew" | "nix" | "mise" | "curl" | "dev"; +export type InstallSource = "npm" | "homebrew" | "nix" | "mise" | "pacman" | "curl" | "dev"; /** Package-manager clients that can install the global `hunkdiff` npm package. */ export type NpmClient = "npm" | "bun" | "pnpm"; -const INSTALL_SOURCES: readonly InstallSource[] = ["npm", "homebrew", "nix", "mise", "curl", "dev"]; +const INSTALL_SOURCES: readonly InstallSource[] = [ + "npm", + "homebrew", + "nix", + "mise", + "pacman", + "curl", + "dev", +]; export interface InstallSourceFacts { env?: NodeJS.ProcessEnv; diff --git a/src/core/install/latestRelease.ts b/src/core/install/latestRelease.ts index 74d93a09a..cd809b45a 100644 --- a/src/core/install/latestRelease.ts +++ b/src/core/install/latestRelease.ts @@ -158,6 +158,6 @@ export async function fetchChannelVersions( return fetchNpmChannelVersions(deps); } - // Nix, mise, and source builds install from somewhere Hunk cannot query or act on. + // Nix, mise, pacman and source builds install from somewhere Hunk cannot query or act on. return {}; } diff --git a/src/core/install/selfUpdate.test.ts b/src/core/install/selfUpdate.test.ts index a2dfc9ee5..c0ac160e2 100644 --- a/src/core/install/selfUpdate.test.ts +++ b/src/core/install/selfUpdate.test.ts @@ -341,6 +341,14 @@ describe("hunk update", () => { expect(result.stdout).toContain("Run `mise up hunk` to update it."); }); + test("points pacman installs at pacman or an AUR helper", async () => { + const result = await runUpdate({ installSource: "pacman" }); + + expect(result.exitCode).toBe(1); + expect(result.commands).toEqual([]); + expect(result.stdout).toContain("Update it through pacman or your AUR helper."); + }); + test("points source builds at install:bin", async () => { const result = await runUpdate({ installSource: "dev", installedVersion: "0.0.0-unknown" }); diff --git a/src/core/install/selfUpdate.ts b/src/core/install/selfUpdate.ts index 79ea4ef7c..429a14b67 100644 --- a/src/core/install/selfUpdate.ts +++ b/src/core/install/selfUpdate.ts @@ -239,6 +239,10 @@ function unmanagedInstallGuidance(installSource: InstallSource) { return ["Hunk was installed with mise.", "Run `mise up hunk` to update it."]; } + if (installSource === "pacman") { + return ["Hunk was installed with pacman.", "Update it through pacman or your AUR helper."]; + } + return [ "Hunk is running from a local source build.", "Run `bun run install:bin` in your Hunk checkout to update it.", diff --git a/src/core/process/updateNotice.test.ts b/src/core/process/updateNotice.test.ts index 5e168c1ac..153fec81f 100644 --- a/src/core/process/updateNotice.test.ts +++ b/src/core/process/updateNotice.test.ts @@ -270,6 +270,21 @@ describe("startup update notice", () => { }); }); + test("suppresses update notices for pacman-managed installs", async () => { + await withTempStatePath(async (statePath) => { + await expect( + resolveStartupUpdateNotice({ + env: { HUNK_INSTALL_SOURCE: "pacman" }, + fetchImpl: async () => { + throw new Error("should not fetch for pacman installs"); + }, + resolveInstalledVersion: () => "0.7.0", + statePath, + }), + ).resolves.toBeNull(); + }); + }); + test("detects unmarked mise installs from their mise install path", async () => { await withTempStatePath(async (statePath) => { await expect( diff --git a/src/core/process/updateNotice.ts b/src/core/process/updateNotice.ts index 4fa908be5..586716a9f 100644 --- a/src/core/process/updateNotice.ts +++ b/src/core/process/updateNotice.ts @@ -34,8 +34,10 @@ export type { InstallSource, UpdateChannel }; * starts. A notice there would ask the user to fix something mise just fixed. A local source build * is replaced by rebuilding the checkout it came from, which is the developer's own workflow and * not something a published version number should interrupt. + * + * AUR packages installed via pacman should be updated manually or via an AUR helper. */ -const SILENT_INSTALL_SOURCES: readonly InstallSource[] = ["mise", "dev"]; +const SILENT_INSTALL_SOURCES: readonly InstallSource[] = ["mise", "pacman", "dev"]; export interface UpdateNoticeDeps { env?: NodeJS.ProcessEnv;