nandi/nimstaticpublic Fork 0
95124a2
Commits
Clone
git clone https://git.rickub.com/nandi/nimstatic.git
git clone ssh://git@rickub.com/nandi/nimstatic.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

ci: build and attach release binaries on a v* tag

nimstatic builds itself — a throwaway host build produces the static binary
that ships, so the release artifact doubles as proof the tool works on a clean
machine. Toolchains come from upstream tarballs at pinned versions rather than
setup actions.

Assets go to whatever upload_url the API advertises, and an asset of the same
name is deleted first so a re-run is idempotent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandi committed 2026-09-19T17:59:55-07:00 Browse files
95124a2 parent: 53cd879
added .rickub/workflows/release.yml +120 -0
new file mode 100644
@@ -0,0 +1,120 @@
1+# Cut a release binary when a v* tag is pushed.
2+#
3+# nimstatic builds itself: a throwaway host build of the tool produces the
4+# static one that ships, which means the release artifact is also the test that
5+# the tool works on a clean machine.
6+name: Release
7+
8+on:
9+ push:
10+ tags: ["v*"]
11+ workflow_dispatch:
12+ inputs:
13+ tag:
14+ description: Existing tag to (re)build and attach binaries to
15+ required: true
16+
17+permissions:
18+ contents: write # required — a job is read-only unless it asks
19+
20+env:
21+ NIM_VERSION: "2.2.4"
22+ ZIG_VERSION: "0.15.1"
23+
24+jobs:
25+ release:
26+ runs-on: ubuntu-latest
27+ steps:
28+ - uses: actions/checkout@v4
29+ with:
30+ ref: ${{ inputs.tag || github.ref }}
31+
32+ # Toolchains come straight from upstream tarballs rather than setup
33+ # actions: two curls, pinned versions, nothing else to trust.
34+ - name: Install Nim and zig
35+ run: |
36+ set -euo pipefail
37+ mkdir -p "$HOME/toolchains"
38+ curl -sSfL "https://nim-lang.org/download/nim-${NIM_VERSION}-linux_x64.tar.xz" \
39+ | tar -xJ -C "$HOME/toolchains"
40+ curl -sSfL "https://ziglang.org/download/${ZIG_VERSION}/zig-x86_64-linux-${ZIG_VERSION}.tar.xz" \
41+ | tar -xJ -C "$HOME/toolchains"
42+ echo "$HOME/toolchains/nim-${NIM_VERSION}/bin" >> "$GITHUB_PATH"
43+ echo "$HOME/toolchains/zig-x86_64-linux-${ZIG_VERSION}" >> "$GITHUB_PATH"
44+
45+ - name: Versions
46+ run: |
47+ nim --version | head -1
48+ zig version
49+
50+ # Alpine packages are cached so a re-run does not re-download them.
51+ - uses: actions/cache@v4
52+ with:
53+ path: ~/.cache/nimstatic
54+ key: nimstatic-alpine-${{ runner.os }}-v3.21
55+
56+ - name: Test
57+ id: test
58+ run: nim c -d:ssl --hints:off -r tests/test_nimstatic.nim
59+
60+ - name: Bootstrap nimstatic on the host
61+ run: nim c -d:release -d:ssl --hints:off -o:nimstatic-host src/nimstatic.nim
62+
63+ - name: Build the static binary with itself
64+ run: |
65+ set -euo pipefail
66+ VERSION="${GITHUB_REF_NAME#v}"
67+ NAME="nimstatic-${VERSION}-x86_64-linux"
68+ ./nimstatic-host src/nimstatic.nim -o "$NAME" -- -d:ssl --passL:-s
69+ file "$NAME"
70+ ldd "$NAME" 2>&1 | grep -q "not a dynamic executable"
71+ ./"$NAME" --help > /dev/null
72+ xz -9e -k "$NAME"
73+ sha256sum "$NAME" "$NAME.xz" > SHA256SUMS
74+ cat SHA256SUMS
75+ echo "NAME=$NAME" >> "$GITHUB_ENV"
76+
77+ # The release exists already (the tag was pushed, or it is being rebuilt),
78+ # so this attaches assets to it rather than creating one. The upload host
79+ # is whatever the API itself advertises in upload_url, which is the one
80+ # value that cannot go stale.
81+ # The release exists already (the tag was pushed, or it is being rebuilt),
82+ # so this attaches assets to it rather than creating one. The upload host
83+ # is whatever the API advertises in upload_url — the one value that
84+ # cannot go stale.
85+ - name: Attach the binaries to the release
86+ env:
87+ TAG: ${{ inputs.tag || github.ref_name }}
88+ run: |
89+ set -euo pipefail
90+ api() { curl -sSfL -H "Authorization: Bearer $GITHUB_TOKEN" \
91+ -H "Accept: application/vnd.github+json" "$@"; }
92+ field() { python3 -c "import json,sys; print(json.load(sys.stdin).get(sys.argv[1],''))" "$1"; }
93+
94+ if ! api "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/releases/tags/$TAG" > release.json; then
95+ api -X POST "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/releases" \
96+ -d "{\"tag_name\":\"$TAG\",\"name\":\"nimstatic $TAG\"}" > release.json
97+ fi
98+
99+ upload_url=$(field upload_url < release.json | cut -d'{' -f1)
100+ echo "uploading to $upload_url"
101+
102+ for asset in "$NAME" "$NAME.xz" SHA256SUMS; do
103+ # Replace an asset of the same name, so a re-run is idempotent.
104+ existing=$(python3 -c "import json,sys; print(next((a['url'] for a in (json.load(open('release.json')).get('assets') or []) if a['name']==sys.argv[1]),''))" "$asset")
105+ if [ -n "$existing" ]; then
106+ api -X DELETE "$existing" > /dev/null
107+ fi
108+ api -X POST "$upload_url?name=$asset" \
109+ -H "Content-Type: application/octet-stream" \
110+ --data-binary "@$asset" > /dev/null
111+ echo "uploaded $asset"
112+ done
113+
114+ - uses: actions/upload-artifact@v4
115+ with:
116+ name: nimstatic-x86_64-linux
117+ path: |
118+ nimstatic-*-x86_64-linux
119+ nimstatic-*-x86_64-linux.xz
120+ SHA256SUMS
new file mode 100644
@@ -0,0 +1,120 @@
1+# Cut a release binary when a v* tag is pushed.
2+#
3+# nimstatic builds itself: a throwaway host build of the tool produces the
4+# static one that ships, which means the release artifact is also the test that
5+# the tool works on a clean machine.
6+name: Release
7+
8+on:
9+ push:
10+ tags: ["v*"]
11+ workflow_dispatch:
12+ inputs:
13+ tag:
14+ description: Existing tag to (re)build and attach binaries to
15+ required: true
16+
17+permissions:
18+ contents: write # required — a job is read-only unless it asks
19+
20+env:
21+ NIM_VERSION: "2.2.4"
22+ ZIG_VERSION: "0.15.1"
23+
24+jobs:
25+ release:
26+ runs-on: ubuntu-latest
27+ steps:
28+ - uses: actions/checkout@v4
29+ with:
30+ ref: ${{ inputs.tag || github.ref }}
31+
32+ # Toolchains come straight from upstream tarballs rather than setup
33+ # actions: two curls, pinned versions, nothing else to trust.
34+ - name: Install Nim and zig
35+ run: |
36+ set -euo pipefail
37+ mkdir -p "$HOME/toolchains"
38+ curl -sSfL "https://nim-lang.org/download/nim-${NIM_VERSION}-linux_x64.tar.xz" \
39+ | tar -xJ -C "$HOME/toolchains"
40+ curl -sSfL "https://ziglang.org/download/${ZIG_VERSION}/zig-x86_64-linux-${ZIG_VERSION}.tar.xz" \
41+ | tar -xJ -C "$HOME/toolchains"
42+ echo "$HOME/toolchains/nim-${NIM_VERSION}/bin" >> "$GITHUB_PATH"
43+ echo "$HOME/toolchains/zig-x86_64-linux-${ZIG_VERSION}" >> "$GITHUB_PATH"
44+
45+ - name: Versions
46+ run: |
47+ nim --version | head -1
48+ zig version
49+
50+ # Alpine packages are cached so a re-run does not re-download them.
51+ - uses: actions/cache@v4
52+ with:
53+ path: ~/.cache/nimstatic
54+ key: nimstatic-alpine-${{ runner.os }}-v3.21
55+
56+ - name: Test
57+ id: test
58+ run: nim c -d:ssl --hints:off -r tests/test_nimstatic.nim
59+
60+ - name: Bootstrap nimstatic on the host
61+ run: nim c -d:release -d:ssl --hints:off -o:nimstatic-host src/nimstatic.nim
62+
63+ - name: Build the static binary with itself
64+ run: |
65+ set -euo pipefail
66+ VERSION="${GITHUB_REF_NAME#v}"
67+ NAME="nimstatic-${VERSION}-x86_64-linux"
68+ ./nimstatic-host src/nimstatic.nim -o "$NAME" -- -d:ssl --passL:-s
69+ file "$NAME"
70+ ldd "$NAME" 2>&1 | grep -q "not a dynamic executable"
71+ ./"$NAME" --help > /dev/null
72+ xz -9e -k "$NAME"
73+ sha256sum "$NAME" "$NAME.xz" > SHA256SUMS
74+ cat SHA256SUMS
75+ echo "NAME=$NAME" >> "$GITHUB_ENV"
76+
77+ # The release exists already (the tag was pushed, or it is being rebuilt),
78+ # so this attaches assets to it rather than creating one. The upload host
79+ # is whatever the API itself advertises in upload_url, which is the one
80+ # value that cannot go stale.
81+ # The release exists already (the tag was pushed, or it is being rebuilt),
82+ # so this attaches assets to it rather than creating one. The upload host
83+ # is whatever the API advertises in upload_url — the one value that
84+ # cannot go stale.
85+ - name: Attach the binaries to the release
86+ env:
87+ TAG: ${{ inputs.tag || github.ref_name }}
88+ run: |
89+ set -euo pipefail
90+ api() { curl -sSfL -H "Authorization: Bearer $GITHUB_TOKEN" \
91+ -H "Accept: application/vnd.github+json" "$@"; }
92+ field() { python3 -c "import json,sys; print(json.load(sys.stdin).get(sys.argv[1],''))" "$1"; }
93+
94+ if ! api "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/releases/tags/$TAG" > release.json; then
95+ api -X POST "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/releases" \
96+ -d "{\"tag_name\":\"$TAG\",\"name\":\"nimstatic $TAG\"}" > release.json
97+ fi
98+
99+ upload_url=$(field upload_url < release.json | cut -d'{' -f1)
100+ echo "uploading to $upload_url"
101+
102+ for asset in "$NAME" "$NAME.xz" SHA256SUMS; do
103+ # Replace an asset of the same name, so a re-run is idempotent.
104+ existing=$(python3 -c "import json,sys; print(next((a['url'] for a in (json.load(open('release.json')).get('assets') or []) if a['name']==sys.argv[1]),''))" "$asset")
105+ if [ -n "$existing" ]; then
106+ api -X DELETE "$existing" > /dev/null
107+ fi
108+ api -X POST "$upload_url?name=$asset" \
109+ -H "Content-Type: application/octet-stream" \
110+ --data-binary "@$asset" > /dev/null
111+ echo "uploaded $asset"
112+ done
113+
114+ - uses: actions/upload-artifact@v4
115+ with:
116+ name: nimstatic-x86_64-linux
117+ path: |
118+ nimstatic-*-x86_64-linux
119+ nimstatic-*-x86_64-linux.xz
120+ SHA256SUMS