ci: use push-charts nodejs tool to manage push to GHCR.io (#23)
This commit is contained in:
parent
8994c62db5
commit
394ce954f7
13 changed files with 1220 additions and 56 deletions
79
scripts/push-charts.test.ts
Normal file
79
scripts/push-charts.test.ts
Normal file
|
@ -0,0 +1,79 @@
|
|||
import { describe, assert, expect, test, vi, beforeEach } from "vitest";
|
||||
import { mockDeep } from "vitest-mock-extended";
|
||||
import { getChangedChartsArchives, main } from "./push-charts.ts";
|
||||
import { Dirent } from "node:fs";
|
||||
|
||||
vi.mock("node:fs/promises");
|
||||
import * as _fs from "node:fs/promises";
|
||||
const fs = vi.mocked(_fs);
|
||||
|
||||
vi.mock("node:child_process");
|
||||
import * as _child_process from "node:child_process";
|
||||
import { exec } from "child_process";
|
||||
const child_process = vi.mocked(_child_process);
|
||||
|
||||
process.env.GITHUB_REPOSITORY = "lore/ipsum";
|
||||
|
||||
describe("push-charts", () => {
|
||||
const myChartDirentMock = mockDeep<Dirent>();
|
||||
myChartDirentMock.name = "my-chart-1.0.0.tgz";
|
||||
|
||||
const fooChartDirentMock = mockDeep<Dirent>();
|
||||
fooChartDirentMock.name = "foo-2.0.0.tgz";
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
|
||||
myChartDirentMock.isFile.mockReturnValue(true);
|
||||
fooChartDirentMock.isFile.mockReturnValue(true);
|
||||
});
|
||||
|
||||
describe("main", () => {
|
||||
test("should push single chart", async () => {
|
||||
fs.readdir.mockResolvedValue([myChartDirentMock]);
|
||||
await main(["node", "push-chart.ts", "my-chart"]);
|
||||
expect(child_process.exec).toHaveBeenCalledWith(
|
||||
`helm push ".cr-release-packages/my-chart-1.0.0.tgz" "oci://ghcr.io/lore/ipsum"`,
|
||||
);
|
||||
});
|
||||
|
||||
test("should push multiple charts", async () => {
|
||||
fs.readdir.mockResolvedValue([myChartDirentMock, fooChartDirentMock]);
|
||||
await main(["node", "push-chart.ts", "my-chart,foo"]);
|
||||
expect(child_process.exec).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
`helm push ".cr-release-packages/my-chart-1.0.0.tgz" "oci://ghcr.io/lore/ipsum"`,
|
||||
);
|
||||
expect(child_process.exec).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
`helm push ".cr-release-packages/foo-2.0.0.tgz" "oci://ghcr.io/lore/ipsum"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getChangedCharts", () => {
|
||||
test("should return changed chart if only one exists", async () => {
|
||||
fs.readdir.mockResolvedValue([myChartDirentMock]);
|
||||
await expect(getChangedChartsArchives(["my-chart"])).resolves.toEqual([
|
||||
".cr-release-packages/my-chart-1.0.0.tgz",
|
||||
]);
|
||||
});
|
||||
|
||||
test("should return changed chart if multiple exists", async () => {
|
||||
fs.readdir.mockResolvedValue([fooChartDirentMock, myChartDirentMock]);
|
||||
await expect(getChangedChartsArchives(["foo"])).resolves.toEqual([
|
||||
".cr-release-packages/foo-2.0.0.tgz",
|
||||
]);
|
||||
});
|
||||
|
||||
test("should return multiple changed charts if multiple exists and changed", async () => {
|
||||
fs.readdir.mockResolvedValue([fooChartDirentMock, myChartDirentMock]);
|
||||
await expect(getChangedChartsArchives(["my-chart,foo"])).resolves.toEqual(
|
||||
[
|
||||
".cr-release-packages/my-chart-1.0.0.tgz",
|
||||
".cr-release-packages/foo-2.0.0.tgz",
|
||||
],
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
74
scripts/push-charts.ts
Normal file
74
scripts/push-charts.ts
Normal file
|
@ -0,0 +1,74 @@
|
|||
import * as fs from "node:fs/promises";
|
||||
import * as process from "node:process";
|
||||
import * as path from "node:path";
|
||||
import { exec } from "node:child_process";
|
||||
|
||||
const CR_RELEASE_PACKAGE_PATH = ".cr-release-packages";
|
||||
|
||||
export async function main(rawArgs: string[]) {
|
||||
// remove file path node and script name
|
||||
const args = rawArgs.slice(2);
|
||||
const archives = await getChangedChartsArchives(args);
|
||||
|
||||
console.log("Pushing charts:");
|
||||
for (const archive of archives) {
|
||||
console.log(`- ${archive}`);
|
||||
exec(
|
||||
`helm push "${archive}" "oci://ghcr.io/${process.env.GITHUB_REPOSITORY}"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getChangedChartsArchives(
|
||||
args: string[],
|
||||
): Promise<string[]> {
|
||||
if (args.length === 0) {
|
||||
console.log("Usage: push-charts <chart-list>");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const changedCharts = parseChartList(args[0]);
|
||||
const chartArchives = await getChartArchives();
|
||||
|
||||
// translate chart names to chart archives
|
||||
const changedChartArchives: string[] = [];
|
||||
for (const chart of changedCharts) {
|
||||
if (chartArchives[chart]) {
|
||||
const archive = chartArchives[chart];
|
||||
changedChartArchives.push(archive);
|
||||
}
|
||||
}
|
||||
return changedChartArchives;
|
||||
}
|
||||
|
||||
function parseChartList(chartListArg: string): string[] {
|
||||
const parsed: string[] = [];
|
||||
for (const chartPath of chartListArg.split(",")) {
|
||||
const name = path.parse(chartPath).name;
|
||||
parsed.push(name);
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
async function getChartArchives(): Promise<Record<string, string>> {
|
||||
const archiveList = await fs.readdir(CR_RELEASE_PACKAGE_PATH, {
|
||||
withFileTypes: true,
|
||||
});
|
||||
const chartNames: Record<string, string> = {};
|
||||
for (const dirent of archiveList) {
|
||||
if (dirent.isFile() && dirent.name.endsWith(".tgz")) {
|
||||
const parsed = /^(?<name>[\w-]+?)-\d/.exec(dirent.name);
|
||||
// immich --> .cr-release-packages/immich-1.0.0.tgz
|
||||
chartNames[parsed.groups.name] = path.join(
|
||||
CR_RELEASE_PACKAGE_PATH,
|
||||
dirent.name,
|
||||
);
|
||||
}
|
||||
}
|
||||
return chartNames;
|
||||
}
|
||||
|
||||
// do not run main if this script is being tested
|
||||
if (!process.env.VITEST_WORKER_ID) {
|
||||
main(process.argv);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue