feat: use @iarna/toml

This commit is contained in:
Jozef Steinhübl 2025-07-08 19:53:49 +02:00
parent 2db092bddd
commit 7a1e37eece
No known key found for this signature in database
GPG key ID: E6BC90C91973B08F
4 changed files with 152 additions and 173 deletions

BIN
bun.lockb

Binary file not shown.

View file

@ -27,7 +27,8 @@
"@actions/exec": "^1.1.1", "@actions/exec": "^1.1.1",
"@actions/glob": "^0.4.0", "@actions/glob": "^0.4.0",
"@actions/io": "^1.1.2", "@actions/io": "^1.1.2",
"@actions/tool-cache": "^2.0.1" "@actions/tool-cache": "^2.0.1",
"@iarna/toml": "^2.2.5"
}, },
"devDependencies": { "devDependencies": {
"@types/bun": "^1.1.13", "@types/bun": "^1.1.13",

View file

@ -1,6 +1,6 @@
import { EOL } from "node:os";
import { existsSync, readFileSync, writeFileSync } from "node:fs"; import { existsSync, readFileSync, writeFileSync } from "node:fs";
import { info } from "@actions/core"; import { info } from "@actions/core";
import { parse, stringify } from "@iarna/toml";
export type Registry = { export type Registry = {
url: string; url: string;
@ -8,164 +8,79 @@ export type Registry = {
token?: string; token?: string;
}; };
enum FieldType { type BunfigConfig = {
GLOBAL_REGISTRY, install?: {
INSTALL_WITH_SCOPE, registry?: {
} url: string;
token?: string;
type Field = { };
type: FieldType; scopes?: Record<string, { url: string; token?: string }>;
value: string; };
[key: string]: any;
}; };
export function createField(registry: Registry): Field {
const { url: registryUrl, scope, token } = registry;
let url: URL | undefined;
if (registryUrl) {
try {
url = new URL(registryUrl);
} catch {
throw new Error(`Invalid registry url ${registryUrl}`);
}
}
let owner: string | undefined;
if (scope) {
owner = scope.startsWith("@")
? scope.toLocaleLowerCase()
: `@${scope.toLocaleLowerCase()}`;
}
if (url && owner) {
return {
type: FieldType.INSTALL_WITH_SCOPE,
value: `'${owner}' = { url = "${url}"${
token ? `, token = "${token}"` : ""
} }`,
};
}
if (url && !owner) {
return {
type: FieldType.GLOBAL_REGISTRY,
value: `registry = "${url}"`,
};
}
return null;
}
export function createBunfig(registries: Registry[]): Field[] | null {
const fields = registries.map(createField).filter((field) => field);
if (fields.length === 0) {
return null;
}
if (
fields.filter((field) => field.type === FieldType.GLOBAL_REGISTRY).length >
1
) {
throw new Error("You can't have more than one global registry.");
}
return fields;
}
export function serializeInstallScopes(
fields: Field[],
header: boolean = false
): string {
const installScopes = fields
.filter((field) => field.type === FieldType.INSTALL_WITH_SCOPE)
.map((field) => field.value)
.join(EOL);
if (!installScopes) {
return "";
}
return `${header ? `[install.scopes]${EOL}` : ""}${installScopes}${EOL}`;
}
export function serializeGlobalRegistry(
fields: Field[],
header: boolean = false
): string {
const globalRegistry = fields
.filter((field) => field.type === FieldType.GLOBAL_REGISTRY)
.map((field) => field.value)
.join(EOL);
if (!globalRegistry) {
return "";
}
return `${header ? `[install]${EOL}` : ""}${globalRegistry}${EOL}`;
}
export function writeBunfig(path: string, registries: Registry[]): void { export function writeBunfig(path: string, registries: Registry[]): void {
const bunfig = createBunfig(registries); if (!registries.length) {
if (!bunfig) {
return; return;
} }
let globalRegistryCount = 0;
registries.forEach((registry) => {
try {
new URL(registry.url);
} catch {
throw new Error(`Invalid registry URL: ${registry.url}`);
}
if (!registry.scope) {
globalRegistryCount++;
}
});
if (globalRegistryCount > 1) {
throw new Error("You can't have more than one global registry.");
}
info(`Writing bunfig.toml to '${path}'.`); info(`Writing bunfig.toml to '${path}'.`);
if (!existsSync(path)) { let config: BunfigConfig = {};
writeFileSync( if (existsSync(path)) {
path, try {
`${serializeGlobalRegistry(bunfig, true)}${serializeInstallScopes( const content = readFileSync(path, { encoding: "utf-8" });
bunfig, config = parse(content) as BunfigConfig;
true } catch (error) {
)}`, info(`Error reading existing bunfig: ${error.message}`);
{ config = {};
encoding: "utf8", }
}
);
return;
} }
let newContent = ""; config.install = config?.install || {};
const contents = readFileSync(path, { config.install.scopes = config?.install.scopes || {};
encoding: "utf-8",
}).split(EOL);
contents.forEach((line, index, array) => { const globalRegistry = registries.find((r) => !r.scope);
if (index > 0 && array[index - 1].includes("[install.scopes]")) { if (globalRegistry) {
newContent += serializeInstallScopes(bunfig); config.install.registry = {
} url: globalRegistry.url,
...(globalRegistry.token ? { token: globalRegistry.token } : {}),
if (index > 0 && array[index - 1].includes("[install]")) { };
newContent += serializeGlobalRegistry(bunfig);
}
if (
line.startsWith("registry = ") ||
!bunfig.some(
(field) =>
field.type === FieldType.INSTALL_WITH_SCOPE &&
(line.startsWith(field.value.split(" ")[0]) ||
((line[0] === "'" || line[0] === '"') &&
line
.toLowerCase()
.startsWith(field.value.split(" ")[0].slice(1).slice(0, -1))))
)
) {
newContent += line + EOL;
}
});
if (!contents.includes("[install.scopes]")) {
newContent += serializeInstallScopes(bunfig, true);
} }
if (!contents.includes("[install]")) { for (const registry of registries) {
newContent += serializeGlobalRegistry(bunfig, true); if (registry.scope) {
const scopeName = registry.scope.startsWith("@")
? registry.scope.toLowerCase()
: `@${registry.scope.toLowerCase()}`;
config.install.scopes[scopeName] = {
url: registry.url,
...(registry.token ? { token: registry.token } : {}),
};
}
} }
writeFileSync(path, newContent, { if (Object.keys(config.install.scopes).length === 0) {
encoding: "utf8", delete config.install.scopes;
}); }
writeFileSync(path, stringify(config), { encoding: "utf8" });
} }

View file

@ -1,5 +1,5 @@
import { afterEach, describe, expect, it } from "bun:test"; import { afterEach, describe, expect, it } from "bun:test";
import { unlinkSync } from "node:fs"; import { existsSync, unlinkSync } from "node:fs";
import { writeBunfig } from "../src/bunfig"; import { writeBunfig } from "../src/bunfig";
import { EOL } from "os"; import { EOL } from "os";
@ -14,7 +14,7 @@ describe("writeBunfig", () => {
} }
afterEach(() => { afterEach(() => {
unlinkSync(filePath); if (existsSync(filePath)) unlinkSync(filePath);
console.log(`${filePath} was deleted`); console.log(`${filePath} was deleted`);
}); });
@ -33,8 +33,9 @@ describe("writeBunfig", () => {
expect(file.exists()).resolves.toBeTrue(); expect(file.exists()).resolves.toBeTrue();
const expectedContents = [ const expectedContents = [
"[install.scopes]", '[install.scopes."@foo-bar"]',
'\'@foo-bar\' = { url = "https://npm.pkg.github.com/", token = "$BUN_AUTH_TOKEN" }', 'url = "https://npm.pkg.github.com"',
'token = "$BUN_AUTH_TOKEN"',
"", "",
]; ];
@ -46,6 +47,31 @@ describe("writeBunfig", () => {
}); });
it("should create a new file with global registry", async () => { it("should create a new file with global registry", async () => {
writeBunfig(filePath, [
{
url: "https://npm.pkg.github.com",
scope: "",
},
]);
const { file, contents } = await getFileAndContents();
expect(file.exists()).resolves.toBeTrue();
const expectedContents = [
"[install.registry]",
'url = "https://npm.pkg.github.com"',
"",
];
contents.forEach((content, index) =>
expect(content).toBe(expectedContents[index])
);
expect(contents.length).toBe(expectedContents.length);
});
it("should create a new file with global registry & token", async () => {
writeBunfig(filePath, [ writeBunfig(filePath, [
{ {
url: "https://npm.pkg.github.com", url: "https://npm.pkg.github.com",
@ -59,8 +85,9 @@ describe("writeBunfig", () => {
expect(file.exists()).resolves.toBeTrue(); expect(file.exists()).resolves.toBeTrue();
const expectedContents = [ const expectedContents = [
"[install]", "[install.registry]",
'registry = "https://npm.pkg.github.com/"', 'url = "https://npm.pkg.github.com"',
'token = "$BUN_AUTH_TOKEN"',
"", "",
]; ];
@ -94,10 +121,12 @@ describe("writeBunfig", () => {
"[install]", "[install]",
"optional = true", "optional = true",
"", "",
"[install.cache]", " [install.cache]",
"disable = true", " disable = true",
"[install.scopes]", "",
'\'@foo-bar\' = { url = "https://npm.pkg.github.com/", token = "$BUN_AUTH_TOKEN" }', '[install.scopes."@foo-bar"]',
'url = "https://npm.pkg.github.com"',
'token = "$BUN_AUTH_TOKEN"',
"", "",
]; ];
@ -129,12 +158,16 @@ describe("writeBunfig", () => {
"[install]", "[install]",
"optional = true", "optional = true",
"", "",
"[install.scopes]", '[install.scopes."@bla-ble"]',
'\'@foo-bar\' = { url = "https://npm.pkg.github.com/", token = "$BUN_AUTH_TOKEN" }', 'token = "$BUN_AUTH_TOKEN"',
'\'@bla-ble\' = { token = "$BUN_AUTH_TOKEN", url = "https://npm.pkg.github.com/" }', 'url = "https://npm.pkg.github.com/"',
"", "",
"[install.cache]", '[install.scopes."@foo-bar"]',
"disable = true", 'url = "https://npm.pkg.github.com"',
'token = "$BUN_AUTH_TOKEN"',
"",
" [install.cache]",
" disable = true",
"", "",
]; ];
@ -166,12 +199,16 @@ describe("writeBunfig", () => {
"[install]", "[install]",
"optional = true", "optional = true",
"", "",
"[install.scopes]", '[install.scopes."@foo-bar"]',
'\'@foo-bar\' = { url = "https://npm.pkg.github.com/", token = "$BUN_AUTH_TOKEN" }', 'url = "https://npm.pkg.github.com"',
'\'@bla-ble\' = { token = "$BUN_AUTH_TOKEN", url = "https://npm.pkg.github.com/" }', 'token = "$BUN_AUTH_TOKEN"',
"", "",
"[install.cache]", '[install.scopes."@bla-ble"]',
"disable = true", 'token = "$BUN_AUTH_TOKEN"',
'url = "https://npm.pkg.github.com/"',
"",
" [install.cache]",
" disable = true",
"", "",
]; ];
@ -206,15 +243,22 @@ describe("writeBunfig", () => {
const expectedContents = [ const expectedContents = [
"[install]", "[install]",
'registry = "https://bun.sh/"',
"optional = true", "optional = true",
"", "",
"[install.scopes]", '[install.scopes."@foo-bar"]',
'\'@foo-bar\' = { url = "https://npm.pkg.github.com/", token = "$BUN_AUTH_TOKEN" }', 'url = "https://npm.pkg.github.com"',
'\'@bla-ble\' = { token = "$BUN_AUTH_TOKEN", url = "https://npm.pkg.github.com/" }', 'token = "$BUN_AUTH_TOKEN"',
"", "",
"[install.cache]", '[install.scopes."@bla-ble"]',
"disable = true", 'token = "$BUN_AUTH_TOKEN"',
'url = "https://npm.pkg.github.com/"',
"",
" [install.cache]",
" disable = true",
"",
" [install.registry]",
' url = "https://bun.sh"',
' token = "$BUN_AUTH_TOKEN"',
"", "",
]; ];
@ -225,4 +269,23 @@ describe("writeBunfig", () => {
expect(contents.length).toBe(expectedContents.length); expect(contents.length).toBe(expectedContents.length);
}); });
}); });
describe("when multiple global registries are provided", () => {
it("should throw an error", () => {
expect(() => {
writeBunfig(filePath, [
{
url: "https://npm.pkg.github.com",
scope: "",
token: "$BUN_AUTH_TOKEN",
},
{
url: "https://bun.sh",
scope: "",
token: "$BUN_AUTH_TOKEN",
},
]);
}).toThrow("You can't have more than one global registry.");
});
});
}); });