119 lines
5.3 KiB
JavaScript
Executable file
119 lines
5.3 KiB
JavaScript
Executable file
#!/usr/bin/env node
|
|
import { $, quote } from "zx";
|
|
import path from "node:path";
|
|
import { strict as assert } from "node:assert";
|
|
import { fileURLToPath } from "node:url";
|
|
// This has to be explicitly set for some reason with the way we install zx in CI
|
|
$.quote = quote;
|
|
$.verbose = true;
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
|
/**Pushes the image + tags to registry*/
|
|
export async function dockerPush(registry: string, registryUser: string, registryToken: string, image: string, fullySpecifiedTags: string[]) {
|
|
const registryHost = registry.split("/")[0];
|
|
console.log(`Logging in to ${registry}...`);
|
|
await $`echo ${registryToken} | docker login ${registryHost} -u ${registryUser} --password-stdin`;
|
|
|
|
for (const tag of fullySpecifiedTags) {
|
|
console.log(`Pushing ${tag}...`);
|
|
await $`docker push ${tag}`;
|
|
}
|
|
}
|
|
|
|
/**Builds the image + tags*/
|
|
export async function dockerBuild(buildContext: string, dockerfilePath: string, fullySpecifiedTags: string[], buildArgs?: Record<string, string>) {
|
|
console.log(`Tagging: ${fullySpecifiedTags.join(", ")}`);
|
|
const tagArgs = fullySpecifiedTags.flatMap((tag) => ["-t", tag]);
|
|
const buildArgsBuilt = Object.entries(buildArgs ?? {})
|
|
.filter(([k,v]) => v !== undefined && v !== null && v !== "")
|
|
.flatMap(([k,v]) => [`--build-arg`, `${k}=${v}`]);
|
|
|
|
await $`docker build --progress=plain ${buildArgsBuilt} ${tagArgs} -f ${dockerfilePath} ${buildContext}`;
|
|
}
|
|
|
|
|
|
// Get the current git hash
|
|
const GIT_HASH = (await $`git rev-parse --short HEAD`).stdout.trim();
|
|
const DOTFILES_VERSION = "0.0.1";
|
|
const BUILDDIR = path.join(__dirname, "..");
|
|
const DOCKERFILE = path.join(BUILDDIR, "dev.Dockerfile");
|
|
const REGISTRY = "git.b4t.dev";
|
|
const IMAGE = "cobertos/dotfiles";
|
|
const REGISTRY_TOKEN = process.env.REGISTRY_TOKEN;
|
|
const REGISTRY_USER = process.env.GITHUB_ACTOR;
|
|
|
|
const posArgs = process.argv.filter(a => !a.startsWith("-"));
|
|
// Usage: node build-docker-container.ts [CMD] [NAME] [DOTFILES_ENVIRONMENT] [DOTFILES_ARCHITECTURE] [DOTFILES_USER] [DOTFILES_EMAIL] [DOTFILES_GIT_DEFAULT_BRANCH]
|
|
const [/*node*/, /*script name*/, CMD, NAME, DOTFILES_ENVIRONMENT, DOTFILES_ARCHITECTURE, DOTFILES_USER, DOTFILES_EMAIL, DOTFILES_GIT_DEFAULT_BRANCH] = posArgs;
|
|
const envs = { DOTFILES_ENVIRONMENT, DOTFILES_ARCHITECTURE, DOTFILES_USER, DOTFILES_EMAIL, DOTFILES_GIT_DEFAULT_BRANCH };
|
|
assert(["all", "build"].includes(CMD), `CMD must be one of "all" or "build". Got "${CMD}"`);
|
|
const dotfilesUserForPath = DOTFILES_USER ? DOTFILES_USER : "cobertos";
|
|
|
|
const TAGS = [
|
|
`${NAME}-${GIT_HASH}`,
|
|
`${NAME}-${DOTFILES_VERSION}`,
|
|
`${NAME}-latest`
|
|
]
|
|
// Convert to final tags
|
|
.map(t => `${REGISTRY}/${IMAGE}:${t}`);
|
|
|
|
// TODO: Readd build args
|
|
console.log(`===== BUILDING DOTFILES DOCKERFILE ${NAME} =====`);
|
|
await dockerBuild(BUILDDIR, DOCKERFILE, TAGS, envs);
|
|
|
|
const imgSizeBytes = await $`docker image inspect ${TAGS[0]} --format '{{.Size}}'`;
|
|
// .pipe($`numfmt --to=iec --suffix=B`);
|
|
console.log(`Image built! Image size ${imgSizeBytes}`);
|
|
|
|
if (CMD === "build") {
|
|
process.exit(0);
|
|
}
|
|
|
|
console.log(`===== NIX CLOSURE SIZES =====`)
|
|
// Print closure sizes
|
|
// -s prints NAR size of the thing itself
|
|
// -S prints total size of it + all it's dependencies
|
|
// -r for recursive
|
|
// we omit -h (human readable sizes) so we can sort and then format after
|
|
// TODO: Readd
|
|
// | numfmt --to=iec --field=2 --suffix=B \
|
|
// | numfmt --to=iec --field=3 --suffix=B \
|
|
// ...
|
|
// | column -t
|
|
// Pipe to sed to remove any "/nix/store/ffffff..." prefixes
|
|
await $`docker run --rm ${TAGS[0]} nix path-info -rsS /home/${dotfilesUserForPath}/.nix-profile`
|
|
.pipe($`sort -k2 -n`)
|
|
.pipe($`sed 's|/nix/store/[a-z0-9]\{32\}-||g'`);
|
|
console.log(`\n\n`);
|
|
|
|
// TODO: Put this somewhere else, I like having it in the build but it makes it
|
|
// take so longggg
|
|
// Scan for vulns with vulnix
|
|
// console.log(`===== RUNNING VULNERABILITY SCAN =====`);
|
|
// console.log(`Running the vulnerability scan on just runtime dependencies`);
|
|
// // This runs vulnix in the container against JUST the runtime dependencies, not
|
|
// // all build dependencies. This is why we use the output of nix-store as vulnix
|
|
// // doesnt do this otherwise, but it greatly reduces what vulns come out (and is
|
|
// // much more what I want to check against)
|
|
// // ~/.nix-profile (the current profile, contains the home manager generation)
|
|
// // (we have to do all this with sh so we can xargs to vulnix but it has to be in the container)
|
|
// // ALSO Fuck vulnix but it throws where there's no deriver, so I have to manually
|
|
// // fetch that with nix-store first and sort those out. UGH this is a pain
|
|
// await $`docker run --rm ${TAGS[0]} sh -c ${
|
|
// `nix-store -q --requisites /home/cobertos/.nix-profile \
|
|
// | xargs -n1 nix-store -qd 2>/dev/null \
|
|
// | grep -v unknown-deriver \
|
|
// | sort -u \
|
|
// | xargs nix run nixpkgs#vulnix -- --no-requisites`
|
|
// }`.nothrow();
|
|
// // if this one returns non-zero we break the build
|
|
// console.log(`\n\n\nEnd vulnerability scan\n===================================`);
|
|
|
|
console.log(`===== PUSHING TO REPOSITORY =====`);
|
|
// Push to repository if the above passed
|
|
assert(REGISTRY_TOKEN, "REGISTRY_TOKEN must be set to push, received nothing");
|
|
assert(REGISTRY_USER, "GITHUB_ACTOR must be set to push, received nothing");
|
|
await dockerPush(REGISTRY, REGISTRY_USER, REGISTRY_TOKEN, IMAGE, TAGS);
|
|
console.log(`Finished! :3`);
|
|
console.log(`Image size ${imgSizeBytes}`);
|