commit 7bbf52a757f4f37fb34a47ab6a029888030b609c Author: Nulo Date: Wed May 31 09:48:26 2023 -0300 hacky diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..97c56e9 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +@nulo:registry=https://gitea.nulo.in/api/packages/nulo/npm/ diff --git a/index.js b/index.js new file mode 100644 index 0000000..8156476 --- /dev/null +++ b/index.js @@ -0,0 +1,67 @@ +import { execFile as _execFile } from "node:child_process"; +import { readFile, stat, symlink, writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import { promisify } from "node:util"; +const execFile = promisify(_execFile); + +/** + * @typedef {object} Alpine + * @prop {(packages: string[]) => Promise} install + * @prop {(runlevel: string, service: string) => Promise} rcUpdate + */ + +/** + * Initialize an Alpine 3.18 rootfs at `root` + * @param {string} root + * @returns {Promise} + */ +export async function init(root) { + await runInContainer( + root, + ` +mkdir -p /rootfs/etc/apk +cp -r /etc/apk/keys /rootfs/etc/apk/ +echo https://dl-cdn.alpinelinux.org/alpine/v3.18/main >> /rootfs/etc/apk/repositories +echo https://dl-cdn.alpinelinux.org/alpine/v3.18/community >> /rootfs/etc/apk/repositories +apk add --initdb --root /rootfs alpine-base + ` + ); + return { + async install(packages) { + const worldPath = join(root, "/etc/apk/world"); + const oldWorld = (await readFile(worldPath, "utf-8")).split("\n"); + const newWorld = oldWorld.concat(packages); + await writeFile(worldPath, newWorld.join("\n")); + + await runInContainer(root, `apk fix --root /rootfs`); + }, + async rcUpdate(runlevel, service) { + const servicePath = `/etc/init.d/${service}`; + try { + await stat(servicePath); + } catch (error) { + throw new Error(`Can't stat service ${service}: ${error}`); + } + await symlink(servicePath, join(root, `/etc/runlevels/`, runlevel)); + }, + }; +} + +/** + * Run script inside an Alpine 3.18 container with `root` mounted in /rootfs + * @param {string} root + * @param {string} script + */ +async function runInContainer(root, script) { + await execFile("podman", [ + "run", + "-it", + "--rm", + "-v", + `${root}:/rootfs:Z`, + "docker.io/alpine:3.18", + "sh", + "-ec", + script, + ]); +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..7efde1e --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "@nulo/apkit", + "type": "module", + "version": "0.0.1", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@tsconfig/node16": "^1.0.4", + "@types/node": "^20.2.5" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..7e4ca0b --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,19 @@ +lockfileVersion: '6.0' + +devDependencies: + '@tsconfig/node16': + specifier: ^1.0.4 + version: 1.0.4 + '@types/node': + specifier: ^20.2.5 + version: 20.2.5 + +packages: + + /@tsconfig/node16@1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + dev: true + + /@types/node@20.2.5: + resolution: {integrity: sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==} + dev: true diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..7105231 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@tsconfig/node16/tsconfig.json", + "exclude": ["build.js/", "build/"], + "compilerOptions": { + "allowJs": true, + "checkJs": true + } +}