From abb4f3f20f830d461fab200cb4da085b817aaf7a Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Wed, 8 Jul 2026 21:26:26 -0400 Subject: [PATCH 1/3] Add self-building Nix flake; build via own flake in CI Cross-builds console for every guest arch (.#dist) instead of borrowing penguin-tools' machinery via --override-input. penguin consumes this flake directly, so a console change no longer needs a penguin-tools re-release. CI (tests.yml) builds .#dist on this checkout; build.yml cuts a version tag. --- .github/workflows/build.yml | 8 +-- .github/workflows/tests.yml | 15 ++-- flake.lock | 27 ++++++++ flake.nix | 94 +++++++++++++++++++++++++ src/archs.nix | 134 ++++++++++++++++++++++++++++++++++++ src/mk-guest-c-tool.nix | 60 ++++++++++++++++ 6 files changed, 324 insertions(+), 14 deletions(-) create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 src/archs.nix create mode 100644 src/mk-guest-c-tool.nix diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f5ed820..78bc9bd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,9 +1,9 @@ name: Tag and Publish release -# console is no longer cross-built here -- penguin-tools builds it for every -# guest arch from this source (github:rehosting/penguin-tools#console-*) and -# ships it in penguin-tools.tar.gz. This workflow only cuts a versioned release -# for tracking/provenance; it publishes no binary asset. +# This repo builds itself via its own flake (`nix build .#dist`, see tests.yml). +# penguin consumes it as a flake input pinned to the tag this workflow cuts, +# building the binaries from source at that tag -- so this release is a +# version/provenance marker and publishes no binary asset. on: push: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 361184d..407568d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,8 +1,9 @@ name: Check that the code compiles -# Build console for every guest arch through the penguin-tools Nix cross matrix -# (the same machinery that ships it in penguin-tools.tar.gz), compiling *this* -# checkout via --override-input. Replaces the old embedded-toolchains Docker build. +# Build console for every guest arch through this repo's OWN flake +# (`nix build .#dist` cross-builds all arches into the /igloo_static fragment +# penguin consumes). Self-contained -- no longer borrows penguin-tools' machinery. +# Replaces the old embedded-toolchains Docker build. on: pull_request: @@ -42,10 +43,4 @@ jobs: - name: Build console for all guest arches (this checkout) run: | set -eux - for a in x86_64 armel aarch64 mipsel mipseb mips64el mips64eb \ - powerpc64 powerpc64le riscv64 loongarch64; do - nix build --accept-flake-config \ - "github:rehosting/penguin-tools#console-$a" \ - --override-input console "path:$PWD" \ - --no-link --print-out-paths -L - done + nix build --accept-flake-config .#dist --no-link --print-out-paths -L diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..7047125 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1773201692, + "narHash": "sha256-NXrKzNMniu4Oam2kAFvqJ3GB2kAvlAFIriTAheaY8hw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b6067cc0127d4db9c26c79e4de0513e58d0c40c9", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b6067cc0127d4db9c26c79e4de0513e58d0c40c9", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..cc369ea --- /dev/null +++ b/flake.nix @@ -0,0 +1,94 @@ +{ + description = "console: guest console helper for penguin, cross-built for every guest arch"; + + nixConfig = { + extra-substituters = [ "https://rehosting-tools.cachix.org" ]; + extra-trusted-public-keys = [ + "rehosting-tools.cachix.org-1:iNKSaFwG7MfGn6Fk7oTmIcLHqfffQ+cQIE5gWc6MlY0=" + ]; + }; + + # Pinned to the same nixpkgs commit as penguin / penguin-tools so the cross + # toolchains are byte-identical and shared through Cachix. + inputs.nixpkgs.url = "github:NixOS/nixpkgs/b6067cc0127d4db9c26c79e4de0513e58d0c40c9"; + + outputs = { self, nixpkgs }: + let + system = "x86_64-linux"; + archMatrix = import ./src/archs.nix; + + pkgs = import nixpkgs { + inherit system; + config.allowUnsupportedSystem = true; + }; + lib = pkgs.lib; + + mkMuslCrossPkgs = archKey: + import nixpkgs { + inherit system; + config.allowUnsupportedSystem = true; + crossSystem = archMatrix.${archKey}.muslCrossSystem; + }; + + # Built as static-musl binaries. console's Makefile hardcodes -march on + # MIPS, but archs.nix already pins gcc.arch per arch (mips32r2/mips64r2), + # so we let the cross stdenv set -march rather than fighting it. + mkConsole = archKey: + import ./src/mk-guest-c-tool.nix { + crossPkgs = mkMuslCrossPkgs archKey; + src = self; + pname = "console-${archMatrix.${archKey}.penguinName}"; + sources = [ "console.c" ]; + outName = "console"; + defines = { + SHELL = "/igloo/utils/sh"; + SERIAL = "/igloo/serial"; + }; + }; + consoleBins = lib.mapAttrs (archKey: _: mkConsole archKey) archMatrix; + + perArchPackages = builtins.listToAttrs ( + lib.mapAttrsToList + (archKey: _: { + name = "console-${archMatrix.${archKey}.penguinName}"; + value = consoleBins.${archKey}; + }) + archMatrix + ); + + # dist: the /igloo_static fragment this tool owns -- the per-arch console + # in the melting-pot dir penguin globs (penguin's staging loop then makes + # the flat console/console. links). Mirrors penguin-tools mk-dist-root. + dist = pkgs.runCommand "console-dist" + { + nativeBuildInputs = with pkgs.buildPackages; [ coreutils ]; + } + '' + set -euo pipefail + ${lib.concatStringsSep "\n" ( + lib.mapAttrsToList + (archKey: _: + let + pn = archMatrix.${archKey}.penguinName; + console = consoleBins.${archKey}; + in + '' + mkdir -p "$out/${pn}" + cp ${console}/bin/console "$out/${pn}/console" + '') + archMatrix + )} + chmod -R u+w "$out" + ''; + in + { + packages.${system} = perArchPackages // { + inherit dist; + default = dist; + }; + + checks.${system} = { + inherit dist; + }; + }; +} diff --git a/src/archs.nix b/src/archs.nix new file mode 100644 index 0000000..1b61967 --- /dev/null +++ b/src/archs.nix @@ -0,0 +1,134 @@ +# Per-arch build matrix (copied from penguin-tools/src/archs.nix so this repo +# builds itself standalone -- no penguin-tools dependency). +# +# crossSystem -- glibc cross system (unused here; kept for parity with the +# shared matrix so arch additions stay in sync). +# muslCrossSystem -- musl cross system used to build the fully-static guest +# binary (rustPlatform cross + crt-static). +{ + x86_64 = { + penguinName = "x86_64"; + compatNames = [ "intel64" ]; + crossSystem = { + config = "x86_64-unknown-linux-gnu"; + }; + muslCrossSystem = { + config = "x86_64-linux-musl"; + }; + }; + + armel = { + penguinName = "armel"; + crossSystem = { + config = "armv7l-unknown-linux-gnueabihf"; + }; + muslCrossSystem = { + config = "armv7l-linux-musleabi"; + }; + }; + + arm64 = { + penguinName = "aarch64"; + crossSystem = { + config = "aarch64-unknown-linux-gnu"; + }; + muslCrossSystem = { + config = "aarch64-linux-musl"; + }; + }; + + mipsel = { + penguinName = "mipsel"; + crossSystem = { + config = "mipsel-unknown-linux-gnu"; + gcc.arch = "mips32r2"; + }; + muslCrossSystem = { + config = "mipsel-linux-musl"; + gcc.arch = "mips32r2"; + }; + }; + + mipseb = { + penguinName = "mipseb"; + crossSystem = { + config = "mips-unknown-linux-gnu"; + gcc.arch = "mips32r2"; + }; + muslCrossSystem = { + config = "mips-linux-musl"; + gcc.arch = "mips32r2"; + }; + }; + + mips64el = { + penguinName = "mips64el"; + crossSystem = { + config = "mips64el-unknown-linux-gnuabi64"; + gcc.arch = "mips64r2"; + gcc.abi = "64"; + }; + muslCrossSystem = { + config = "mips64el-linux-musl"; + gcc.arch = "mips64r2"; + gcc.abi = "64"; + }; + }; + + mips64eb = { + penguinName = "mips64eb"; + crossSystem = { + config = "mips64-unknown-linux-gnuabi64"; + gcc.arch = "mips64r2"; + gcc.abi = "64"; + }; + muslCrossSystem = { + config = "mips64-linux-musl"; + gcc.arch = "mips64r2"; + gcc.abi = "64"; + }; + }; + + ppc64 = { + penguinName = "powerpc64"; + crossSystem = { + config = "powerpc64-unknown-linux-gnuabielfv2"; + gcc.abi = "elfv2"; + }; + muslCrossSystem = { + config = "powerpc64-linux-musl"; + gcc.abi = "elfv2"; + }; + }; + + ppc64el = { + penguinName = "powerpc64le"; + compatNames = [ "powerpc64el" ]; + crossSystem = { + config = "powerpc64le-unknown-linux-gnu"; + }; + muslCrossSystem = { + config = "powerpc64le-linux-musl"; + }; + }; + + riscv64 = { + penguinName = "riscv64"; + crossSystem = { + config = "riscv64-unknown-linux-gnu"; + }; + muslCrossSystem = { + config = "riscv64-linux-musl"; + }; + }; + + loongarch = { + penguinName = "loongarch64"; + crossSystem = { + config = "loongarch64-unknown-linux-gnu"; + }; + muslCrossSystem = { + config = "loongarch64-linux-musl"; + }; + }; +} diff --git a/src/mk-guest-c-tool.nix b/src/mk-guest-c-tool.nix new file mode 100644 index 0000000..d5d79a6 --- /dev/null +++ b/src/mk-guest-c-tool.nix @@ -0,0 +1,60 @@ +# Build a small, self-contained C guest utility (e.g. console) as a static +# binary cross-compiled for one target arch. +# +# These tools have no runtime closure -- they are statically linked against +# musl and drop into the guest as a single file -- so unlike the debugging +# tools (mk-arch-closure.nix) there is nothing to bind-mount; the output is +# just $out/bin/. +# +# crossPkgs -- a musl cross nixpkgs (archs.nix muslCrossSystem) so the binary +# is fully static and freestanding in the guest. +# src -- the (forked) source tree of the utility. +# pname -- derivation name. +# sources -- list of .c files to compile (relative to src). +# outName -- installed binary name (what penguin expects in the tarball). +# defines -- attrset of -D macros (string values are quoted for C). +# extraCFlags-- arch-specific flags (e.g. [ "-mips32r3" ]). +{ crossPkgs +, src +, pname +, sources ? [ ] +, outName +, defines ? { } +, extraCFlags ? [ ] +}: + +let + lib = crossPkgs.lib; + # Single-quote the value so bash preserves the inner double quotes and the + # compiler sees a C string literal (-DSERIAL='"/igloo/serial"'), matching + # console's Makefile (-DSERIAL=\"/igloo/serial\"). + defineFlags = + lib.mapAttrsToList (k: v: "-D${k}='\"${toString v}\"'") defines; + cflags = lib.concatStringsSep " " ([ "-static" "-O2" "-Wall" ] ++ extraCFlags ++ defineFlags); +in +crossPkgs.stdenv.mkDerivation { + inherit pname src; + version = "0"; + + dontConfigure = true; + + # Use the cross stdenv's $CC directly rather than the upstream Makefile: the + # Makefile keys its per-arch flags off bespoke triple names (mipsel-linux-musl + # etc.) that don't match nixpkgs' config strings, and nix already selects the + # correct cross-gcc. The flags here mirror console's Makefile verbatim. + buildPhase = '' + runHook preBuild + $CC ${cflags} ${lib.concatStringsSep " " sources} -o ${outName} + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + install -Dm755 ${outName} "$out/bin/${outName}" + runHook postInstall + ''; + + # Static musl binaries have no dynamic deps; skip the (cross-hostile) checks. + dontStrip = true; + dontPatchELF = true; +} From 65efd3fe551c541ab4776f74ab34221c2a344192 Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Thu, 9 Jul 2026 19:15:22 -0400 Subject: [PATCH 2/3] ci: pin Nix 2.31.2 + disable KVM setup for Arc runners install-nix-action's default (Nix 2.22.1 + enable_kvm) fails on the rehosting-arc self-hosted pods (no working udevd; narHash mismatch). Mirror penguin's image build / rehosting-ci nix-setup. --- .github/workflows/tests.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 407568d..8f42ad0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -26,8 +26,14 @@ jobs: - name: Install dependencies run: sudo apt-get update && sudo apt-get install -y curl jq xz-utils + # Arc self-hosted pods expose /dev/kvm without a working udevd, which + # breaks install-nix-action's default KVM setup; and its default Nix + # (2.22.1) computes different tarball narHashes than modern Nix. Pin both + # (mirrors rehosting/ci's nix-setup + penguin's image build). - uses: cachix/install-nix-action@v27 with: + install_url: https://releases.nixos.org/nix/nix-2.31.2/install + enable_kvm: false github_access_token: ${{ secrets.GITHUB_TOKEN }} extra_nix_config: | max-jobs = 8 From 06f159aa2ee52b73de7b5e98612d9277161a2070 Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Thu, 9 Jul 2026 19:29:29 -0400 Subject: [PATCH 3/3] ci: use rehosting/ci nix-setup@v1 (org-standard, matches penguin) --- .github/workflows/tests.yml | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8f42ad0..da1bb16 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -26,25 +26,21 @@ jobs: - name: Install dependencies run: sudo apt-get update && sudo apt-get install -y curl jq xz-utils - # Arc self-hosted pods expose /dev/kvm without a working udevd, which - # breaks install-nix-action's default KVM setup; and its default Nix - # (2.22.1) computes different tarball narHashes than modern Nix. Pin both - # (mirrors rehosting/ci's nix-setup + penguin's image build). - - uses: cachix/install-nix-action@v27 + # Use the org-shared Nix setup (same as penguin's image build): pins a + # modern Nix whose hashing matches the flake.lock and disables the KVM + # setup that Arc pods lack a working udevd for. + - name: Set up Nix + uses: rehosting/ci/actions/nix-setup@v1 with: - install_url: https://releases.nixos.org/nix/nix-2.31.2/install - enable_kvm: false - github_access_token: ${{ secrets.GITHUB_TOKEN }} - extra_nix_config: | + github-token: ${{ secrets.GITHUB_TOKEN }} + install-url: https://releases.nixos.org/nix/nix-2.31.2/install + enable-kvm: false + extra-nix-config: | max-jobs = 8 cores = 8 - - - name: Configure Cachix - uses: cachix/cachix-action@v16 - with: - name: rehosting-tools - authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} - skipPush: ${{ github.event_name == 'pull_request' }} + cache-backend: cachix + cachix-name: rehosting-tools + cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }} - name: Build console for all guest arches (this checkout) run: |