git.iko.soy/typeable/nix-s3-cache-action
on main help sign in
init
commit20a06c0ee3eba9545a14a621f12319bc3e519de2 authorAnisimov Nikita <anisimoff.nikita@gmail.com> dateFri 2 Oct 2026, 16:32 (1 week ago) parent3ebc689
4 files changed+159
unifiedsplit
action.yml+70 cleanup.sh+8 enable-recorder.sh+22 upload.sh+59
action.ymlnew file+70
@@ -0,0 +1,70 @@
1+name: Nix S3 cache
2+description: Enable Nix build path recording and upload recorded paths to an S3 binary cache.
3+
4+inputs:
5+ operation:
6+ description: "Operation to run: enable-recorder or upload."
7+ required: true
8+ aws-role-to-assume:
9+ description: AWS IAM role to assume before uploading Nix paths.
10+ required: false
11+ aws-region:
12+ description: AWS region for the Nix cache upload role.
13+ required: false
14+ default: us-east-1
15+ role-duration-seconds:
16+ description: AWS role duration for the Nix cache upload role.
17+ required: false
18+ default: "21600"
19+ cache-private-key:
20+ description: Private signing key for the Nix binary cache.
21+ required: false
22+ cache-s3-store-url:
23+ description: Nix S3 store URL used as the nix copy destination.
24+ required: false
25+
26+runs:
27+ using: composite
28+ steps:
29+ - name: Validate operation
30+ shell: bash
31+ env:
32+ OPERATION: ${{ inputs.operation }}
33+ run: |
34+ set -euo pipefail
35+
36+ case "$OPERATION" in
37+ enable-recorder|upload) ;;
38+ *)
39+ echo "Unsupported operation: $OPERATION" >&2
40+ echo "Expected one of: enable-recorder, upload" >&2
41+ exit 1
42+ ;;
43+ esac
44+
45+ - name: Enable Nix remote cache path recorder
46+ if: ${{ inputs.operation == 'enable-recorder' }}
47+ shell: bash
48+ run: bash "$GITHUB_ACTION_PATH/enable-recorder.sh"
49+
50+ - name: Configure AWS credentials for Nix cache upload
51+ if: ${{ inputs.operation == 'upload' }}
52+ uses: aws-actions/configure-aws-credentials@v6
53+ with:
54+ role-to-assume: ${{ inputs.aws-role-to-assume }}
55+ aws-region: ${{ inputs.aws-region }}
56+ role-duration-seconds: ${{ inputs.role-duration-seconds }}
57+
58+ - name: Upload built Nix paths to S3 cache
59+ if: ${{ inputs.operation == 'upload' }}
60+ shell: bash
61+ env:
62+ NIX_CACHE_PRIVATE_KEY: ${{ inputs.cache-private-key }}
63+ NIX_CACHE_S3_STORE_URL: ${{ inputs.cache-s3-store-url }}
64+ run: bash "$GITHUB_ACTION_PATH/upload.sh"
65+
66+ - name: Clean up Nix S3 cache files
67+ if: ${{ always() && inputs.operation == 'upload' }}
68+ shell: bash
69+ run: bash "$GITHUB_ACTION_PATH/cleanup.sh"
70+
cleanup.shnew file+8
@@ -0,0 +1,8 @@
1+set -euo pipefail
2+
3+rm -f "$RUNNER_TEMP/nix-cache-private-key"
4+rm -f "$RUNNER_TEMP/nix-built-paths"
5+rm -f /tmp/nix-record-built-paths
6+rm -f /tmp/nix-built-paths
7+rm -f /tmp/nix-built-paths.lock
8+
enable-recorder.shnew file+22
@@ -0,0 +1,22 @@
1+set -euo pipefail
2+
3+tee /tmp/nix-record-built-paths > /dev/null <<'EOF'
4+#!/bin/sh
5+set -eu
6+set -f
7+
8+if [ -n "${OUT_PATHS:-}" ]; then
9+ touch /tmp/nix-built-paths /tmp/nix-built-paths.lock
10+ (
11+ flock 9
12+ printf '%s\n' $OUT_PATHS >> /tmp/nix-built-paths
13+ ) 9>/tmp/nix-built-paths.lock
14+fi
15+EOF
16+
17+chmod +x /tmp/nix-record-built-paths
18+touch /tmp/nix-built-paths /tmp/nix-built-paths.lock
19+chmod 0666 /tmp/nix-built-paths /tmp/nix-built-paths.lock
20+echo "post-build-hook = /tmp/nix-record-built-paths" | sudo tee -a /etc/nix/nix.conf
21+nix show-config | grep -F "/tmp/nix-record-built-paths"
22+
upload.shnew file+59
@@ -0,0 +1,59 @@
1+set -euo pipefail
2+
3+if [ ! -s /tmp/nix-built-paths ]; then
4+ echo "No Nix paths were recorded for upload."
5+ exit 0
6+fi
7+
8+if [ -z "${NIX_CACHE_PRIVATE_KEY:-}" ]; then
9+ echo "NIX_CACHE_PRIVATE_KEY is not set." >&2
10+ exit 1
11+fi
12+
13+if [ -z "${NIX_CACHE_S3_STORE_URL:-}" ]; then
14+ echo "NIX_CACHE_S3_STORE_URL is not set." >&2
15+ exit 1
16+fi
17+
18+key_file="$RUNNER_TEMP/nix-cache-private-key"
19+paths_file="$RUNNER_TEMP/nix-built-paths"
20+
21+cleanup() {
22+ rm -f "$key_file"
23+ rm -f "$paths_file"
24+}
25+trap cleanup EXIT
26+
27+printf '%s\n' "$NIX_CACHE_PRIVATE_KEY" > "$key_file"
28+chmod 0600 "$key_file"
29+sort -u /tmp/nix-built-paths > "$paths_file"
30+
31+if [ ! -s "$paths_file" ]; then
32+ echo "No Nix paths were recorded for upload."
33+ exit 0
34+fi
35+
36+upload_url="$NIX_CACHE_S3_STORE_URL"
37+append_store_param() {
38+ local key="$1"
39+ local value="$2"
40+ case "$upload_url" in
41+ *\?"$key"=*|*\&"$key"=*) return 0 ;;
42+ esac
43+ case "$upload_url" in
44+ *\?*) upload_url="$upload_url&$key=$value" ;;
45+ *) upload_url="$upload_url?$key=$value" ;;
46+ esac
47+}
48+
49+append_store_param secret-key "$key_file"
50+append_store_param compression zstd
51+append_store_param compression-level 1
52+append_store_param parallel-compression true
53+
54+path_count="$(wc -l < "$paths_file" | tr -d ' ')"
55+echo "Uploading $path_count Nix paths to $NIX_CACHE_S3_STORE_URL"
56+started_at="$SECONDS"
57+xargs -r nix copy --to "$upload_url" < "$paths_file"
58+echo "Uploaded $path_count Nix paths to $NIX_CACHE_S3_STORE_URL in $((SECONDS - started_at))s"
59+