diff --git a/.github/workflows/build-iso.yml b/.github/workflows/build-iso.yml index efb4ee7..0575046 100644 --- a/.github/workflows/build-iso.yml +++ b/.github/workflows/build-iso.yml @@ -9,6 +9,14 @@ permissions: jobs: build: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - arch: x86_64 + system: x86_64-linux + - arch: aarch64 + system: aarch64-linux steps: - name: Checkout uses: actions/checkout@v4 @@ -19,19 +27,19 @@ jobs: echo "$HOME/.local/bin" >> "$GITHUB_PATH" - name: Build and export the ISO - run: dagger call build export --path=./out/guix-install-x86_64-linux.iso + run: dagger call build --arch=${{ matrix.arch }} export --path=./out/guix-install-${{ matrix.arch }}-linux.iso - name: Publish ISO as a release asset uses: softprops/action-gh-release@v2 with: - tag_name: "iso-${{ github.run_number }}" - name: "Guix Install ISO (x86_64) — build ${{ github.run_number }}" + tag_name: "iso-${{ github.run_number }}-${{ matrix.arch }}" + name: "Guix Install ISO (${{ matrix.arch }}) — build ${{ github.run_number }}" body: | - Reproducible Guix installation ISO, cross-built from the pinned + Reproducible Guix installation ISO, built from the pinned channel in `channels.scm`. - - Target: x86_64-linux + - Target: ${{ matrix.system }} - Build: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} - files: out/guix-install-x86_64-linux.iso + files: out/guix-install-${{ matrix.arch }}-linux.iso draft: false prerelease: false diff --git a/dagger.json b/dagger.json index a7b2335..deb5592 100644 --- a/dagger.json +++ b/dagger.json @@ -3,6 +3,5 @@ "engineVersion": "v0.20.6", "sdk": { "source": "go" - }, - "source": "." + } } diff --git a/go.mod b/go.mod index 6cd45b6..d03355f 100644 --- a/go.mod +++ b/go.mod @@ -35,8 +35,8 @@ require ( go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect go.opentelemetry.io/proto/otlp v1.9.0 // indirect golang.org/x/net v0.58.0 // indirect - golang.org/x/sync v0.22.0 // indirect - golang.org/x/sys v0.47.0 // indirect + golang.org/x/sync v0.23.0 // indirect + golang.org/x/sys v0.48.0 // indirect golang.org/x/text v0.41.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect diff --git a/go.sum b/go.sum index c5ec384..ed18abc 100644 --- a/go.sum +++ b/go.sum @@ -71,10 +71,10 @@ go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= -golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= -golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= -golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk= +golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0= +golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= +golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= diff --git a/image.sh b/image.sh index ef8c4bd..e5ca5e1 100644 --- a/image.sh +++ b/image.sh @@ -1,6 +1,8 @@ #!/usr/bin/env bash set -euo pipefail +arch="${1:-x86_64}" + export PATH="/root/.config/guix/current/bin:${PATH}" . /root/.config/guix/current/etc/profile @@ -21,5 +23,5 @@ image="$(guix system image -t iso9660 -e '(@ (gnu system install) installation-o echo "built image: ${image}" mkdir -p /out -cp "${image}" /out/guix-install-x86_64-linux.iso +cp "${image}" "/out/guix-install-${arch}-linux.iso" ls -lh /out diff --git a/main.go b/main.go index 35cea74..7b07c13 100644 --- a/main.go +++ b/main.go @@ -1,9 +1,10 @@ // A Dagger module that builds a reproducible Guix installation ISO -// inside a container (cross-built for x86_64). +// inside a container (built for x86_64 by default; pass --arch for arm64). // // Edit channels.scm to change which Guix commit is used, then run: // // dagger call build export --path=./guix-install-x86_64-linux.iso +// dagger call build --arch=aarch64 export --path=./guix-install-aarch64-linux.iso package main @@ -13,12 +14,13 @@ import ( "dagger/guix-iso/internal/dagger" ) -const ( - guixVersion = "1.5.0" - arch = "x86_64" - platform = "linux/amd64" - tarball = "guix-binary-" + guixVersion + "." + arch + "-linux.tar.xz" -) +const guixVersion = "1.5.0" + +// platformFor maps a Guix system string to the Dagger container platform. +var platformFor = map[string]dagger.Platform{ + "x86_64": "linux/amd64", + "aarch64": "linux/arm64", +} // setupScript runs after the tarball is extracted; it creates the build // users and authorizes the substitute servers. It has no dependency on @@ -37,9 +39,11 @@ GUIX=/var/guix/profiles/per-user/root/current-guix/bin/guix type GuixIso struct{} -// setup builds the x86_64 container with Guix installed (daemon not started). -func setup() *dagger.Container { - return dag.Container(dagger.ContainerOpts{Platform: platform}). +// setup builds a container with Guix installed for the given architecture +// (daemon not started). +func setup(arch string) *dagger.Container { + tarball := "guix-binary-" + guixVersion + "." + arch + "-linux.tar.xz" + return dag.Container(dagger.ContainerOpts{Platform: platformFor[arch]}). From("debian:stable-slim"). WithEnvVariable("DEBIAN_FRONTEND", "noninteractive"). WithExec([]string{"apt-get", "update"}). @@ -52,19 +56,34 @@ func setup() *dagger.Container { } // Build assembles the Guix installation ISO and returns it as a file. -func (m *GuixIso) Build(ctx context.Context) *dagger.File { +func (m *GuixIso) Build( + ctx context.Context, + // Architecture of the ISO: "x86_64" or "aarch64". + // +optional + arch string, +) *dagger.File { + if arch == "" { + arch = "x86_64" + } + src := dag.CurrentModule().Source() - return setup(). + return setup(arch). WithMountedDirectory("/workspace", src). WithExec([]string{"bash", "/workspace/pull.sh"}, dagger.ContainerWithExecOpts{InsecureRootCapabilities: true}). - WithExec([]string{"bash", "/workspace/image.sh"}, + WithExec([]string{"bash", "/workspace/image.sh", arch}, dagger.ContainerWithExecOpts{InsecureRootCapabilities: true}). File("/out/guix-install-" + arch + "-linux.iso") } // Debug returns the setup container for inspection without running the build. -func (m *GuixIso) Debug() *dagger.Container { - return setup() +func (m *GuixIso) Debug( + // +optional + arch string, +) *dagger.Container { + if arch == "" { + arch = "x86_64" + } + return setup(arch) }