2023-01-16 15:14:20 +00:00
|
|
|
import {
|
|
|
|
chmod,
|
|
|
|
copyFile,
|
|
|
|
mkdir,
|
|
|
|
mkdtemp,
|
|
|
|
opendir,
|
|
|
|
rm,
|
|
|
|
symlink,
|
|
|
|
writeFile,
|
|
|
|
} from "node:fs/promises";
|
|
|
|
import { tmpdir } from "node:os";
|
|
|
|
import path from "node:path";
|
|
|
|
import { cwd } from "node:process";
|
2023-02-02 22:23:39 +00:00
|
|
|
import { execFile } from "./helpers/better-api.js";
|
2023-01-16 15:14:20 +00:00
|
|
|
|
|
|
|
export class Alpine {
|
|
|
|
dir: string;
|
|
|
|
private constructor({ dir }: { dir: string }) {
|
|
|
|
this.dir = dir;
|
|
|
|
}
|
|
|
|
|
|
|
|
async mkdir(dir: string, opts?: { recursive: boolean }): Promise<void> {
|
|
|
|
await mkdir(path.join(this.dir, dir), opts);
|
|
|
|
}
|
|
|
|
async writeFile(filePath: string, content: string): Promise<void> {
|
|
|
|
await this.mkdir(path.dirname(filePath), { recursive: true });
|
|
|
|
await writeFile(path.join(this.dir, filePath), content);
|
|
|
|
}
|
|
|
|
async writeExecutable(filePath: string, content: string): Promise<void> {
|
|
|
|
await this.writeFile(filePath, content);
|
|
|
|
await chmod(path.join(this.dir, filePath), 700);
|
|
|
|
}
|
|
|
|
async sudoWriteExecutable(filePath: string, content: string): Promise<void> {
|
|
|
|
const dir = await mkdtemp(
|
|
|
|
path.join(tmpdir(), "define-alpine-sudoWriteExecutable-")
|
|
|
|
);
|
|
|
|
try {
|
|
|
|
const tmpFile = path.join(dir, path.basename(filePath));
|
|
|
|
const finalPath = path.join(this.dir, filePath);
|
|
|
|
await writeFile(tmpFile, content);
|
|
|
|
await execFile("sudo", [
|
|
|
|
"mkdir",
|
|
|
|
"--parents",
|
|
|
|
path.join(this.dir, path.dirname(filePath)),
|
|
|
|
]);
|
|
|
|
await execFile("sudo", ["mv", tmpFile, finalPath]);
|
|
|
|
await execFile("sudo", ["chmod", "700", finalPath]);
|
|
|
|
} finally {
|
|
|
|
await rm(dir, { recursive: true, force: true });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private getRelativeSymlink(
|
|
|
|
target: string,
|
|
|
|
filePath: string
|
|
|
|
): { target: string; filePath: string } {
|
|
|
|
const realFilePath = path.join(this.dir, filePath);
|
|
|
|
return {
|
|
|
|
target: path.relative(
|
|
|
|
path.dirname(realFilePath),
|
|
|
|
path.join(this.dir, target)
|
|
|
|
),
|
|
|
|
filePath: realFilePath,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
async symlink(_target: string, _filePath: string): Promise<void> {
|
|
|
|
const { target, filePath } = this.getRelativeSymlink(_target, _filePath);
|
|
|
|
await symlink(target, filePath);
|
|
|
|
}
|
|
|
|
async sudoSymlink(_target: string, _filePath: string): Promise<void> {
|
|
|
|
const { target, filePath } = this.getRelativeSymlink(_target, _filePath);
|
|
|
|
await execFile("sudo", ["ln", "-s", target, filePath]);
|
|
|
|
}
|
|
|
|
|
|
|
|
async addPackages(packages: string[]): Promise<void> {
|
|
|
|
await execFile("sudo", [
|
|
|
|
"apk",
|
|
|
|
"add",
|
|
|
|
"--clean-protected",
|
|
|
|
"--root",
|
|
|
|
this.dir,
|
|
|
|
...packages,
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static async makeWorld({
|
|
|
|
dir,
|
|
|
|
packages,
|
|
|
|
}: {
|
|
|
|
dir: string;
|
|
|
|
packages?: string[];
|
|
|
|
}): Promise<Alpine> {
|
|
|
|
const apkDir = path.join(dir, "/etc/apk");
|
|
|
|
await mkdir(apkDir, { recursive: true });
|
|
|
|
|
|
|
|
// hack
|
|
|
|
{
|
|
|
|
const cacheDir = path.join(cwd(), "cache");
|
|
|
|
await mkdir("cache", { recursive: true });
|
|
|
|
await symlink(cacheDir, path.join(apkDir, "cache"));
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const apkKeysDir = path.join(apkDir, "keys");
|
|
|
|
const keysSrcDir = "alpine/keys";
|
|
|
|
await mkdir(apkKeysDir);
|
|
|
|
for await (const { name } of await opendir(keysSrcDir))
|
|
|
|
await copyFile(
|
|
|
|
path.join(keysSrcDir, name),
|
|
|
|
path.join(apkKeysDir, name)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
await writeFile(
|
|
|
|
path.join(apkDir, "repositories"),
|
|
|
|
[
|
|
|
|
"https://dl-cdn.alpinelinux.org/alpine/v3.17/main",
|
|
|
|
"https://dl-cdn.alpinelinux.org/alpine/v3.17/community",
|
|
|
|
].join("\n")
|
|
|
|
);
|
|
|
|
await execFile("sudo", [
|
|
|
|
"apk",
|
|
|
|
"add",
|
|
|
|
"--initdb",
|
|
|
|
"--clean-protected",
|
|
|
|
"--root",
|
|
|
|
dir,
|
|
|
|
...["alpine-baselayout", "busybox", "libc-utils", "alpine-keys"],
|
|
|
|
...(packages || []),
|
|
|
|
]);
|
|
|
|
|
|
|
|
return new Alpine({ dir });
|
|
|
|
}
|
|
|
|
}
|