From e4f2f46b93ceca409dafb3e06a32c5ffd945f932 Mon Sep 17 00:00:00 2001 From: awalsh128 Date: Sun, 8 Nov 2026 15:17:32 -0800 Subject: [PATCH] fix: find install scripts for package names with regex metacharacters findInstallScript built a regex from the raw package name, so names like g++ or libstdc++6 threw "Nothing to repeat" on cache save and on restore with install scripts, and a '.' in a name could match another package's script. Compare file names as plain strings instead. Co-Authored-By: Claude Opus 5.5 --- src/packages.ts | 9 +++++++-- test/packages.test.ts | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/packages.ts b/src/packages.ts index d197d2f..ff52e5e 100644 --- a/src/packages.ts +++ b/src/packages.ts @@ -66,10 +66,15 @@ export async function findInstallScript( return undefined; } - const pattern = new RegExp(`^${packageName.name}(:.*)?\\.${extension}$`); + // Match on plain strings since package names can hold regex metacharacters + // (e.g. g++, libstdc++6). Scripts are named [:].. + const suffix = `.${extension}`; + const isMatch = (entry: string) => + entry === `${packageName.name}${suffix}` || + (entry.startsWith(`${packageName.name}:`) && entry.endsWith(suffix)); const matches = fs .readdirSync(scriptsDir) - .filter((entry) => pattern.test(entry)) + .filter(isMatch) .sort((a, b) => a.localeCompare(b)); const candidate = matches[0]; if (!candidate) { diff --git a/test/packages.test.ts b/test/packages.test.ts index f7bde72..859646f 100644 --- a/test/packages.test.ts +++ b/test/packages.test.ts @@ -98,6 +98,38 @@ describe("packages", () => { expect(result).toBe(path.join(scriptsDir, "test-package.preinst")); }); + it.each([ + ["g++", "g++.postinst"], + ["libstdc++6", "libstdc++6:amd64.postinst"], + ])( + "findInstallScript name %s with regex metacharacters returns its script", + async (name, scriptFilename) => { + const root = makeTempRoot("pkg-metachar-"); + const scriptsDir = ensureScriptsDir(root); + fs.writeFileSync(path.join(scriptsDir, scriptFilename), "", "utf8"); + + const result = await findInstallScript( + createPackageName(name), + "postinst", + root, + ); + expect(result).toBe(path.join(scriptsDir, scriptFilename)); + }, + ); + + it("findInstallScript name with dot does not match a similarly named package", async () => { + const root = makeTempRoot("pkg-dot-"); + const scriptsDir = ensureScriptsDir(root); + fs.writeFileSync(path.join(scriptsDir, "libaXb.postinst"), "", "utf8"); + + const result = await findInstallScript( + createPackageName("liba.b"), + "postinst", + root, + ); + expect(result).toBeUndefined(); + }); + it("updateAptLists calls the package manager's update method", async () => { const aptListsPath = makeTempRoot("apt-lists-");