Compare commits
No commits in common. "a826cfa41aa27eb197ad8bc778c961833637b3d1" and "8d4e6db7d2a2413034383a188bfd64995c6a2e6e" have entirely different histories.
a826cfa41a
...
8d4e6db7d2
12 changed files with 7 additions and 300 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,4 +1,3 @@
|
||||||
node_modules/
|
node_modules/
|
||||||
build-javascript
|
build-javascript
|
||||||
cache/
|
cache/
|
||||||
artifacts/
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
mkdtemp,
|
mkdtemp,
|
||||||
opendir,
|
opendir,
|
||||||
rm,
|
rm,
|
||||||
|
rmdir,
|
||||||
symlink,
|
symlink,
|
||||||
writeFile,
|
writeFile,
|
||||||
} from "node:fs/promises";
|
} from "node:fs/promises";
|
||||||
|
|
23
helpers.ts
23
helpers.ts
|
@ -3,29 +3,6 @@ import {
|
||||||
execFile as execFileCallback,
|
execFile as execFileCallback,
|
||||||
spawn as spawnCallback,
|
spawn as spawnCallback,
|
||||||
} from "node:child_process";
|
} from "node:child_process";
|
||||||
import { access } from "node:fs/promises";
|
|
||||||
import { getuid } from "node:process";
|
|
||||||
|
|
||||||
export const execFile = promisify(execFileCallback);
|
export const execFile = promisify(execFileCallback);
|
||||||
export const spawn = promisify(spawnCallback);
|
export const spawn = promisify(spawnCallback);
|
||||||
|
|
||||||
export async function canAccess(path: string): Promise<boolean> {
|
|
||||||
try {
|
|
||||||
await access(path);
|
|
||||||
return true;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function sudoChown(path: string, owner: string): Promise<void> {
|
|
||||||
await execFile("sudo", ["chown", owner, path]);
|
|
||||||
}
|
|
||||||
export async function sudoChownToRunningUser(path: string): Promise<void> {
|
|
||||||
if (getuid) {
|
|
||||||
await sudoChown(path, "" + getuid());
|
|
||||||
} else throw new Error("No tengo getuid");
|
|
||||||
}
|
|
||||||
export async function sudoRm(path: string): Promise<void> {
|
|
||||||
await execFile("sudo", ["rm", path]);
|
|
||||||
}
|
|
||||||
|
|
38
index.ts
38
index.ts
|
@ -1,45 +1,15 @@
|
||||||
import { mkdir, mkdtemp } from "node:fs/promises";
|
import { mkdir, mkdtemp } from "node:fs/promises";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { cwd } from "node:process";
|
|
||||||
import { Alpine } from "./alpine.js";
|
import { Alpine } from "./alpine.js";
|
||||||
import { execFile, spawn, sudoChownToRunningUser } from "./helpers.js";
|
import { spawn } from "./helpers.js";
|
||||||
import { setupKernel } from "./kernel.js";
|
|
||||||
import { runQemu } from "./qemu.js";
|
|
||||||
import { Runit } from "./runit/index.js";
|
import { Runit } from "./runit/index.js";
|
||||||
|
|
||||||
{
|
{
|
||||||
console.time("Building");
|
|
||||||
|
|
||||||
const artifactsDir = path.join(cwd(), "artifacts/");
|
|
||||||
const kernelDir = path.join(artifactsDir, "kernel");
|
|
||||||
await mkdir(artifactsDir, { recursive: true });
|
|
||||||
await mkdir(kernelDir, { recursive: true });
|
|
||||||
|
|
||||||
const rootfsDir = await mkdtemp(path.join(tmpdir(), "define-alpine-"));
|
const rootfsDir = await mkdtemp(path.join(tmpdir(), "define-alpine-"));
|
||||||
console.debug(rootfsDir);
|
console.debug(rootfsDir);
|
||||||
const alpine = await Alpine.makeWorld({ dir: rootfsDir });
|
const alpine = await Alpine.makeWorld({ dir: rootfsDir });
|
||||||
const runit = await Runit.setup(alpine);
|
const runit = await Runit.setup(alpine);
|
||||||
const kernel = await setupKernel(alpine, kernelDir);
|
|
||||||
|
|
||||||
const squashfs = path.join(artifactsDir, "image.squashfs");
|
|
||||||
await execFile("sudo", [
|
|
||||||
"mksquashfs",
|
|
||||||
alpine.dir,
|
|
||||||
squashfs,
|
|
||||||
"-comp",
|
|
||||||
"zstd",
|
|
||||||
"-Xcompression-level",
|
|
||||||
"3",
|
|
||||||
"-noappend",
|
|
||||||
"-quiet",
|
|
||||||
]);
|
|
||||||
await sudoChownToRunningUser(squashfs);
|
|
||||||
|
|
||||||
console.timeEnd("Building");
|
|
||||||
|
|
||||||
runQemu(squashfs, kernel, { graphic: true });
|
|
||||||
|
|
||||||
// await makeService({
|
// await makeService({
|
||||||
// parentDir: rootfsDir,
|
// parentDir: rootfsDir,
|
||||||
// name: "grafana",
|
// name: "grafana",
|
||||||
|
@ -47,9 +17,9 @@ import { Runit } from "./runit/index.js";
|
||||||
// setup: async (dir) => {},
|
// setup: async (dir) => {},
|
||||||
// initScript: async (dir) => {},
|
// initScript: async (dir) => {},
|
||||||
// });
|
// });
|
||||||
// try {
|
try {
|
||||||
// await spawn("sudo", ["chroot", rootfsDir], { stdio: "inherit" });
|
await spawn("sudo", ["chroot", rootfsDir], { stdio: "inherit" });
|
||||||
// } catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// interface Service {}
|
// interface Service {}
|
||||||
|
|
91
kernel.ts
91
kernel.ts
|
@ -1,91 +0,0 @@
|
||||||
import { constants } from "node:fs";
|
|
||||||
import { copyFile } from "node:fs/promises";
|
|
||||||
import path from "node:path";
|
|
||||||
import { Alpine } from "./alpine.js";
|
|
||||||
import { canAccess, sudoChownToRunningUser, sudoRm } from "./helpers.js";
|
|
||||||
|
|
||||||
export type Kind = "lts" | "virt";
|
|
||||||
export type Kernel = {
|
|
||||||
initramfs: string;
|
|
||||||
vmlinuz: string;
|
|
||||||
kind: Kind;
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function setupKernel(
|
|
||||||
alpine: Alpine,
|
|
||||||
output: string
|
|
||||||
): Promise<Kernel> {
|
|
||||||
const kind: Kind = "virt";
|
|
||||||
|
|
||||||
await alpine.writeFile(
|
|
||||||
"/etc/update-extlinux.conf",
|
|
||||||
`# configuration for extlinux config builder
|
|
||||||
|
|
||||||
# Overwrite current /boot/extlinux.conf.
|
|
||||||
overwrite=1
|
|
||||||
|
|
||||||
# vesa_menu
|
|
||||||
# use fancy vesa menu (vesamenu.c32) menus, won't work with serial
|
|
||||||
vesa_menu=1
|
|
||||||
|
|
||||||
#default_kernel_opts=quiet
|
|
||||||
default_kernel_opts=
|
|
||||||
modules=loop,squashfs,sd-mod,usb-storage,ext4,vfat
|
|
||||||
|
|
||||||
# root device - if not specified, will be guessed using
|
|
||||||
# blkid -o export /dev/root
|
|
||||||
root=/dev/sda
|
|
||||||
|
|
||||||
# if set to non-zero, update-extlinux will be a lot more verbose.
|
|
||||||
verbose=0
|
|
||||||
|
|
||||||
hidden=0
|
|
||||||
timeout=3
|
|
||||||
default=lts
|
|
||||||
`
|
|
||||||
);
|
|
||||||
|
|
||||||
const features = [
|
|
||||||
"squashfs",
|
|
||||||
"ata",
|
|
||||||
"base",
|
|
||||||
"cdrom",
|
|
||||||
"ext4",
|
|
||||||
"keymap",
|
|
||||||
"kms",
|
|
||||||
"mmc",
|
|
||||||
"nvme",
|
|
||||||
"scsi",
|
|
||||||
"usb",
|
|
||||||
...(kind === "virt" ? ["virtio"] : []),
|
|
||||||
];
|
|
||||||
|
|
||||||
await alpine.writeFile(
|
|
||||||
"/etc/mkinitfs/mkinitfs.conf",
|
|
||||||
`features="${features.join(" ")}"`
|
|
||||||
);
|
|
||||||
|
|
||||||
await alpine.addPackages([`linux-${kind}`]);
|
|
||||||
|
|
||||||
const initramfs = path.join(alpine.dir, `/boot/initramfs-${kind}`);
|
|
||||||
const vmlinuz = path.join(alpine.dir, `/boot/vmlinuz-${kind}`);
|
|
||||||
|
|
||||||
if (!(await canAccess(initramfs)))
|
|
||||||
throw new Error("Didn't generate initramfs");
|
|
||||||
else if (!(await canAccess(vmlinuz)))
|
|
||||||
throw new Error("Didn't generate vmlinuz");
|
|
||||||
|
|
||||||
const kernel: Kernel = {
|
|
||||||
kind,
|
|
||||||
initramfs: path.join(output, "initramfs"),
|
|
||||||
vmlinuz: path.join(output, "vmlinuz"),
|
|
||||||
};
|
|
||||||
|
|
||||||
await sudoChownToRunningUser(initramfs);
|
|
||||||
await sudoChownToRunningUser(vmlinuz);
|
|
||||||
await copyFile(initramfs, kernel.initramfs, constants.COPYFILE_FICLONE);
|
|
||||||
await copyFile(vmlinuz, kernel.vmlinuz, constants.COPYFILE_FICLONE);
|
|
||||||
await sudoRm(initramfs);
|
|
||||||
await sudoRm(vmlinuz);
|
|
||||||
return kernel;
|
|
||||||
}
|
|
|
@ -5,7 +5,7 @@
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"run": "esbuild --log-level=warning --target=node18 --sourcemap --outdir=build-javascript --outbase=. *.ts **/*.ts && node --enable-source-maps build-javascript/index.js"
|
"run": "esbuild --target=node18 --sourcemap --outdir=build-javascript --outbase=. *.ts **/*.ts && node --enable-source-maps build-javascript/index.js"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
|
|
58
qemu.ts
58
qemu.ts
|
@ -1,58 +0,0 @@
|
||||||
import { mkdtemp, rm } from "node:fs/promises";
|
|
||||||
import { tmpdir } from "node:os";
|
|
||||||
import path from "node:path";
|
|
||||||
import { execFile } from "./helpers.js";
|
|
||||||
import { Kernel } from "./kernel.js";
|
|
||||||
|
|
||||||
export async function runQemu(
|
|
||||||
squashfs: string,
|
|
||||||
kernel: Kernel,
|
|
||||||
{ graphic }: { graphic: boolean } = { graphic: true }
|
|
||||||
) {
|
|
||||||
const tmp = await mkdtemp(path.join(tmpdir(), "define-alpine-qemu-"));
|
|
||||||
try {
|
|
||||||
const disk = path.join(tmp, "tmp.ext4");
|
|
||||||
await execFile("fallocate", ["--length", "1G", disk]);
|
|
||||||
await execFile("mkfs.ext4", ["-F", disk]);
|
|
||||||
|
|
||||||
let kernelAppend = [
|
|
||||||
"root=/dev/sda",
|
|
||||||
"rootfstype=squashfs",
|
|
||||||
"modules=ext4",
|
|
||||||
"quiet",
|
|
||||||
"init=/sbin/runit-init",
|
|
||||||
];
|
|
||||||
let qemuAppend: string[] = [];
|
|
||||||
|
|
||||||
if (!graphic) {
|
|
||||||
kernelAppend.push("console=ttyS0");
|
|
||||||
qemuAppend.push("-nographic");
|
|
||||||
}
|
|
||||||
|
|
||||||
// sudo chown root:$(id -u) -R boot/ && sudo chmod g+rw -R boot/
|
|
||||||
await execFile("qemu-system-x86_64", [
|
|
||||||
"-enable-kvm",
|
|
||||||
"-m",
|
|
||||||
"2048",
|
|
||||||
"-drive",
|
|
||||||
`file=${squashfs},media=disk,format=raw`,
|
|
||||||
"-drive",
|
|
||||||
`file=${disk},media=disk,format=raw`,
|
|
||||||
"-net",
|
|
||||||
"nic,model=virtio-net-pci",
|
|
||||||
"-net",
|
|
||||||
"user,hostfwd=tcp:127.0.0.1:8080-:80",
|
|
||||||
"-kernel",
|
|
||||||
kernel.vmlinuz,
|
|
||||||
"-initrd",
|
|
||||||
kernel.initramfs,
|
|
||||||
"-append",
|
|
||||||
kernelAppend.join(" "),
|
|
||||||
...qemuAppend,
|
|
||||||
"-no-shutdown",
|
|
||||||
]);
|
|
||||||
// -append "root=/dev/sda rootfstype=squashfs modules=ext4 quiet init=/sbin/runit-init $append" $qemuappend
|
|
||||||
} finally {
|
|
||||||
await rm(tmp, { recursive: true, force: true });
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -53,12 +53,6 @@ export class Runit {
|
||||||
await this.getScript("05-misc.sh")
|
await this.getScript("05-misc.sh")
|
||||||
);
|
);
|
||||||
|
|
||||||
for (let stage = 1; stage <= 3; stage++)
|
|
||||||
await alpine.writeExecutable(
|
|
||||||
"/etc/runit/" + stage,
|
|
||||||
await this.getScript("" + stage)
|
|
||||||
);
|
|
||||||
|
|
||||||
// https://wiki.gentoo.org/wiki/Runit#Reboot_and_shutdown
|
// https://wiki.gentoo.org/wiki/Runit#Reboot_and_shutdown
|
||||||
await alpine.sudoWriteExecutable(
|
await alpine.sudoWriteExecutable(
|
||||||
"/usr/local/sbin/rpoweroff",
|
"/usr/local/sbin/rpoweroff",
|
||||||
|
|
|
@ -75,5 +75,4 @@ msg "Mounting all non-network filesystems..."
|
||||||
mount -a -t "nosysfs,nonfs,nonfs4,nosmbfs,nocifs" -O no_netdev || emergency_shell
|
mount -a -t "nosysfs,nonfs,nonfs4,nosmbfs,nocifs" -O no_netdev || emergency_shell
|
||||||
# data module
|
# data module
|
||||||
msg "Creating and mounting data directories..."
|
msg "Creating and mounting data directories..."
|
||||||
# TODO: todavía no tenemos modulo de data que genere esto
|
/usr/local/bin/mount-data || emergency_shell
|
||||||
# /usr/local/bin/mount-data || emergency_shell
|
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
PATH=/bin:/usr/bin:/usr/sbin:/sbin
|
|
||||||
|
|
||||||
. /etc/runit/functions
|
|
||||||
|
|
||||||
msg "Welcome to Nulo!"
|
|
||||||
|
|
||||||
[ -r /etc/rc.conf ] && . /etc/rc.conf
|
|
||||||
|
|
||||||
# Start core services: one-time system tasks.
|
|
||||||
detect_virt
|
|
||||||
for f in /etc/runit/core-services/*.sh; do
|
|
||||||
[ -r $f ] && . $f
|
|
||||||
done
|
|
||||||
|
|
||||||
# create files for controlling runit
|
|
||||||
mkdir -p /run/runit
|
|
||||||
install -m000 /dev/null /run/runit/stopit
|
|
||||||
install -m000 /dev/null /run/runit/reboot
|
|
||||||
|
|
||||||
msg "Initialization complete, running stage 2..."
|
|
|
@ -1,19 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
PATH=/bin:/usr/bin:/usr/sbin:/sbin
|
|
||||||
|
|
||||||
runlevel=default
|
|
||||||
for arg in $(cat /proc/cmdline); do
|
|
||||||
if [ -d /etc/runit/runsvdir/"$arg" ]; then
|
|
||||||
echo "Runlevel detected: '$arg' (via kernel cmdline)"
|
|
||||||
runlevel="$arg"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
[ -x /etc/rc.local ] && /etc/rc.local
|
|
||||||
|
|
||||||
runsvchdir "${runlevel}"
|
|
||||||
mkdir -p /run/runit/runsvdir
|
|
||||||
ln -s /etc/runit/runsvdir/current /run/runit/runsvdir/current
|
|
||||||
|
|
||||||
exec env - PATH=$PATH \
|
|
||||||
runsvdir -P /run/runit/runsvdir/current 'log: ................................'
|
|
|
@ -1,43 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
PATH=/bin:/usr/bin:/usr/sbin:/sbin
|
|
||||||
|
|
||||||
. /etc/runit/functions
|
|
||||||
detect_virt
|
|
||||||
[ -r /etc/rc.conf ] && . /etc/rc.conf
|
|
||||||
|
|
||||||
echo
|
|
||||||
msg "Waiting for services to stop..."
|
|
||||||
sv force-stop /etc/service/*
|
|
||||||
sv exit /etc/service/*
|
|
||||||
|
|
||||||
[ -x /etc/rc.shutdown ] && /etc/rc.shutdown
|
|
||||||
|
|
||||||
if [ -z "$VIRTUALIZATION" -a -n "$HARDWARECLOCK" ]; then
|
|
||||||
hwclock --systohc ${HARDWARECLOCK:+--$(echo $HARDWARECLOCK |tr A-Z a-z)}
|
|
||||||
fi
|
|
||||||
|
|
||||||
halt -w # for wtmp
|
|
||||||
|
|
||||||
if [ -z "$VIRTUALIZATION" ]; then
|
|
||||||
msg "Stopping udev..."
|
|
||||||
udevadm control --exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$VIRTUALIZATION" ]; then
|
|
||||||
msg "Unmounting filesystems, disabling swap..."
|
|
||||||
swapoff -a
|
|
||||||
umount -r -a -t nosysfs,noproc,nodevtmpfs,notmpfs
|
|
||||||
fi
|
|
||||||
|
|
||||||
sync
|
|
||||||
|
|
||||||
if [ -z "$VIRTUALIZATION" ]; then
|
|
||||||
deactivate_vgs
|
|
||||||
deactivate_crypt
|
|
||||||
if [ -e /run/runit/reboot ] && command -v kexec >/dev/null; then
|
|
||||||
msg "Triggering kexec..."
|
|
||||||
kexec -e 2>/dev/null
|
|
||||||
# not reached when kexec was successful.
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
Loading…
Reference in a new issue