deploy: add nix-built runner image
Authored by
mfwolffe <wolffemf@dukes.jmu.edu>
- SHA
0bffad9142435847e7c224a343a8843522723b78- Parents
-
cf7b55f - Tree
12e23d2
0bffad9
0bffad9142435847e7c224a343a8843522723b78cf7b55f
12e23d2| Status | File | + | - |
|---|---|---|---|
| A |
.github/workflows/runner-image.yml
|
75 | 0 |
| A |
deploy/runner-images/README.md
|
22 | 0 |
| A |
deploy/runner-images/flake.lock
|
27 | 0 |
| A |
deploy/runner-images/flake.nix
|
93 | 0 |
.github/workflows/runner-image.ymladded@@ -0,0 +1,75 @@ | ||
| 1 | +name: runner image | |
| 2 | + | |
| 3 | +on: | |
| 4 | + workflow_dispatch: | |
| 5 | + inputs: | |
| 6 | + image: | |
| 7 | + description: "Destination image name; blank publishes under this repo's GHCR namespace" | |
| 8 | + required: false | |
| 9 | + default: "" | |
| 10 | + tag: | |
| 11 | + description: "Destination image tag" | |
| 12 | + required: true | |
| 13 | + default: "1.0" | |
| 14 | + | |
| 15 | +permissions: | |
| 16 | + contents: read | |
| 17 | + id-token: write | |
| 18 | + packages: write | |
| 19 | + | |
| 20 | +env: | |
| 21 | + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" | |
| 22 | + | |
| 23 | +jobs: | |
| 24 | + build: | |
| 25 | + runs-on: ubuntu-latest | |
| 26 | + steps: | |
| 27 | + - uses: actions/checkout@v4 | |
| 28 | + | |
| 29 | + - uses: DeterminateSystems/determinate-nix-action@v3 | |
| 30 | + | |
| 31 | + - name: Resolve destination image | |
| 32 | + id: image | |
| 33 | + env: | |
| 34 | + INPUT_IMAGE: ${{ inputs.image }} | |
| 35 | + INPUT_TAG: ${{ inputs.tag }} | |
| 36 | + REPOSITORY: ${{ github.repository }} | |
| 37 | + run: | | |
| 38 | + set -euo pipefail | |
| 39 | + image="$INPUT_IMAGE" | |
| 40 | + if [ -z "$image" ]; then | |
| 41 | + image="ghcr.io/${REPOSITORY,,}/runner-nix" | |
| 42 | + fi | |
| 43 | + case "$image" in | |
| 44 | + *[!a-z0-9/:._-]* | "") | |
| 45 | + echo "invalid image name: $image" >&2 | |
| 46 | + exit 2 | |
| 47 | + ;; | |
| 48 | + esac | |
| 49 | + case "$INPUT_TAG" in | |
| 50 | + *[!A-Za-z0-9_.-]* | "") | |
| 51 | + echo "invalid image tag: $INPUT_TAG" >&2 | |
| 52 | + exit 2 | |
| 53 | + ;; | |
| 54 | + esac | |
| 55 | + printf 'image=%s\n' "$image" >> "$GITHUB_OUTPUT" | |
| 56 | + printf 'tag=%s\n' "$INPUT_TAG" >> "$GITHUB_OUTPUT" | |
| 57 | + | |
| 58 | + - name: Build image tarball | |
| 59 | + run: nix build ./deploy/runner-images#runnerImage --print-build-logs | |
| 60 | + | |
| 61 | + - name: Load image | |
| 62 | + run: docker load < result | |
| 63 | + | |
| 64 | + - name: Tag image | |
| 65 | + run: docker tag ghcr.io/shithub/runner-nix:1.0 "${{ steps.image.outputs.image }}:${{ steps.image.outputs.tag }}" | |
| 66 | + | |
| 67 | + - name: Login to GHCR | |
| 68 | + uses: docker/login-action@v3 | |
| 69 | + with: | |
| 70 | + registry: ghcr.io | |
| 71 | + username: ${{ github.actor }} | |
| 72 | + password: ${{ secrets.GITHUB_TOKEN }} | |
| 73 | + | |
| 74 | + - name: Push image | |
| 75 | + run: docker push "${{ steps.image.outputs.image }}:${{ steps.image.outputs.tag }}" | |
deploy/runner-images/README.mdadded@@ -0,0 +1,22 @@ | ||
| 1 | +# shithub runner image | |
| 2 | + | |
| 3 | +`flake.nix` builds the default S41d runner container image: | |
| 4 | + | |
| 5 | +```sh | |
| 6 | +nix build ./deploy/runner-images#runnerImage | |
| 7 | +docker load < result | |
| 8 | +``` | |
| 9 | + | |
| 10 | +The image tag is `ghcr.io/shithub/runner-nix:1.0`, matching | |
| 11 | +`internal/runner/config`'s default. `flake.lock` pins nixpkgs so the | |
| 12 | +image input set is reviewable and repeatable. The image intentionally | |
| 13 | +contains only the baseline tools needed for v1 `run:` steps and checkout | |
| 14 | +plumbing: | |
| 15 | +`bash`, coreutils, git, curl, CA certificates, gnupg, gcc, gnumake, | |
| 16 | +archive tools, OpenSSH, and `shithub-shallow-checkout`. | |
| 17 | + | |
| 18 | +Publishing is handled by `.github/workflows/runner-image.yml`. That | |
| 19 | +workflow is manual because the GHCR namespace may differ between the | |
| 20 | +upstream project and self-hosted forks. Leave the image input blank to | |
| 21 | +publish under the current repository's GHCR namespace, or override it | |
| 22 | +with `ghcr.io/shithub/runner-nix` for the upstream package. | |
deploy/runner-images/flake.lockadded@@ -0,0 +1,27 @@ | ||
| 1 | +{ | |
| 2 | + "nodes": { | |
| 3 | + "nixpkgs": { | |
| 4 | + "locked": { | |
| 5 | + "lastModified": 1778003029, | |
| 6 | + "narHash": "sha256-q/nkKLDtHIyLjZpKhWk3cSK5IYsFqtMd6UtXF3ddjgA=", | |
| 7 | + "owner": "NixOS", | |
| 8 | + "repo": "nixpkgs", | |
| 9 | + "rev": "0c88e1f2bdb93d5999019e99cb0e61e1fe2af4c5", | |
| 10 | + "type": "github" | |
| 11 | + }, | |
| 12 | + "original": { | |
| 13 | + "owner": "NixOS", | |
| 14 | + "ref": "nixos-25.11", | |
| 15 | + "repo": "nixpkgs", | |
| 16 | + "type": "github" | |
| 17 | + } | |
| 18 | + }, | |
| 19 | + "root": { | |
| 20 | + "inputs": { | |
| 21 | + "nixpkgs": "nixpkgs" | |
| 22 | + } | |
| 23 | + } | |
| 24 | + }, | |
| 25 | + "root": "root", | |
| 26 | + "version": 7 | |
| 27 | +} | |
deploy/runner-images/flake.nixadded@@ -0,0 +1,93 @@ | ||
| 1 | +{ | |
| 2 | + description = "shithub Actions default runner image"; | |
| 3 | + | |
| 4 | + inputs = { | |
| 5 | + nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; | |
| 6 | + }; | |
| 7 | + | |
| 8 | + outputs = { self, nixpkgs }: | |
| 9 | + let | |
| 10 | + systems = [ "x86_64-linux" "aarch64-linux" ]; | |
| 11 | + forAllSystems = nixpkgs.lib.genAttrs systems; | |
| 12 | + in | |
| 13 | + { | |
| 14 | + packages = forAllSystems (system: | |
| 15 | + let | |
| 16 | + pkgs = import nixpkgs { inherit system; }; | |
| 17 | + checkoutHelper = pkgs.writeShellApplication { | |
| 18 | + name = "shithub-shallow-checkout"; | |
| 19 | + runtimeInputs = [ | |
| 20 | + pkgs.git | |
| 21 | + pkgs.coreutils | |
| 22 | + ]; | |
| 23 | + text = '' | |
| 24 | + set -euo pipefail | |
| 25 | + | |
| 26 | + if [ "$#" -ne 3 ]; then | |
| 27 | + echo "usage: shithub-shallow-checkout <repo-url> <sha> <dest>" >&2 | |
| 28 | + exit 2 | |
| 29 | + fi | |
| 30 | + | |
| 31 | + repo_url="$1" | |
| 32 | + sha="$2" | |
| 33 | + dest="$3" | |
| 34 | + | |
| 35 | + mkdir -p "$dest" | |
| 36 | + cd "$dest" | |
| 37 | + git init | |
| 38 | + git remote add origin "$repo_url" | |
| 39 | + git fetch --depth=1 origin "$sha" | |
| 40 | + git checkout --detach FETCH_HEAD | |
| 41 | + ''; | |
| 42 | + }; | |
| 43 | + imageRoot = pkgs.buildEnv { | |
| 44 | + name = "shithub-runner-nix-root"; | |
| 45 | + paths = [ | |
| 46 | + pkgs.bashInteractive | |
| 47 | + pkgs.cacert | |
| 48 | + pkgs.coreutils | |
| 49 | + pkgs.curl | |
| 50 | + pkgs.findutils | |
| 51 | + pkgs.gcc | |
| 52 | + pkgs.git | |
| 53 | + pkgs.gnugrep | |
| 54 | + pkgs.gnused | |
| 55 | + pkgs.gnutar | |
| 56 | + pkgs.gzip | |
| 57 | + pkgs.gnupg | |
| 58 | + pkgs.gnumake | |
| 59 | + pkgs.openssh | |
| 60 | + pkgs.xz | |
| 61 | + checkoutHelper | |
| 62 | + ]; | |
| 63 | + pathsToLink = [ "/bin" "/etc" ]; | |
| 64 | + }; | |
| 65 | + in | |
| 66 | + { | |
| 67 | + runnerImage = pkgs.dockerTools.buildLayeredImage { | |
| 68 | + name = "ghcr.io/shithub/runner-nix"; | |
| 69 | + tag = "1.0"; | |
| 70 | + contents = [ imageRoot ]; | |
| 71 | + maxLayers = 80; | |
| 72 | + config = { | |
| 73 | + Cmd = [ "${pkgs.bashInteractive}/bin/bash" ]; | |
| 74 | + Env = [ | |
| 75 | + "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" | |
| 76 | + "GIT_SSL_CAINFO=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" | |
| 77 | + "PATH=/bin:${imageRoot}/bin" | |
| 78 | + ]; | |
| 79 | + WorkingDir = "/workspace"; | |
| 80 | + Labels = { | |
| 81 | + "org.opencontainers.image.title" = "shithub runner-nix"; | |
| 82 | + "org.opencontainers.image.description" = "Default container image for shithub Actions run steps."; | |
| 83 | + "org.opencontainers.image.source" = "https://github.com/tenseleyFlow/shithub"; | |
| 84 | + "org.opencontainers.image.version" = "1.0"; | |
| 85 | + "org.opencontainers.image.licenses" = "AGPL-3.0-or-later"; | |
| 86 | + }; | |
| 87 | + }; | |
| 88 | + }; | |
| 89 | + | |
| 90 | + default = self.packages.${system}.runnerImage; | |
| 91 | + }); | |
| 92 | + }; | |
| 93 | +} | |