nandi/frqpublic Fork 0
41bcb21
Commits
Clone
git clone https://git.rickub.com/nandi/frq.git
git clone ssh://git@rickub.com/nandi/frq.git

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

A runner that builds nothing, and an AppImage anyway

GitLab CI reads source and no more, on purpose. This is the other half:
the AppImage actually realised — not on the runner, which is a container
that would die of libjoltcosmic the way a laptop does, but handed to
Modal exactly as a person at a terminal hands it, and fetched back out of
the nix-cache volume afterwards.

In .rickub/workflows/ rather than .github/workflows/ because rickub reads
one or the other and never both; there is no .github/workflows/ today, so
nothing is being shadowed, but a later one would be ignored in silence.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandi committed 2026-09-18T16:11:53-07:00 Browse files
41bcb21 parent: 5ce66d5
added .rickub/workflows/build.yml +90 -0
new file mode 100644
@@ -0,0 +1,90 @@
1+# The build, on rickub. GitLab CI next door reads source and no more —
2+# check-common on every push, the flake.lock nightly — and deliberately builds
3+# nothing. This is the other half: the AppImage, actually realised, on a
4+# machine that can hold libjoltcosmic's dependency tree.
5+#
6+# It does not realise it *here*. rickub runners are Linux x86-64 containers,
7+# and `nix build .#appimage` on one is the same laptop-shaped death CLAUDE.md
8+# warns about — so this job is a client. It hands the build to Modal, exactly
9+# as a person at a terminal would, and the Sandbox on its real VM does the
10+# work against the nix-cache volume. What a runner contributes is a checkout,
11+# a python, and somewhere to put the result afterwards.
12+#
13+# Lives in .rickub/workflows/ rather than .github/workflows/ because rickub
14+# reads one or the other and never both: with this directory present, a
15+# .github/workflows/ added later would be silently ignored. There is none
16+# today, so nothing is being shadowed — see
17+# https://rickub.com/docs/actions and https://rickub.com/docs/migrating-from-github
18+name: build
19+
20+on:
21+ push:
22+ workflow_dispatch:
23+
24+jobs:
25+ # The same read-only check GitLab runs, for the same reason: common/ compiles
26+ # twice and only the jolt half is on the way to anything anyone runs, so a
27+ # JVM call in shared code breaks the phone at a namespace nobody touched.
28+ # Seconds, no toolchain. Worth having on both hosts rather than depending on
29+ # which one a given push reaches.
30+ check-common:
31+ runs-on: ubuntu-latest
32+ steps:
33+ - uses: actions/checkout@v4
34+ - run: python3 tools/check-common.py common
35+
36+ appimage:
37+ runs-on: ubuntu-latest
38+ needs: check-common
39+ # The Modal side is an hour at its worst (a cold libjoltcosmic); this side
40+ # is waiting on it, plus the upload of the tree.
41+ timeout-minutes: 90
42+ steps:
43+ # The container copies `.` — the whole working tree, uncommitted edits
44+ # included. On a runner that is whatever the checkout left, so it wants
45+ # to be the commit and not a shallow surprise.
46+ - uses: actions/checkout@v4
47+
48+ - uses: actions/setup-python@v5
49+ with:
50+ python-version: "3.12"
51+
52+ - run: pip install --disable-pip-version-check modal
53+
54+ # Two secrets, set under Settings -> Secrets and variables. A Modal
55+ # token is the whole of this job's configuration: no nix, no builder,
56+ # no cache of its own.
57+ - name: nix build .#appimage, on Modal
58+ env:
59+ MODAL_TOKEN_ID: ${{ secrets.MODAL_TOKEN_ID }}
60+ MODAL_TOKEN_SECRET: ${{ secrets.MODAL_TOKEN_SECRET }}
61+ # Unpiped on purpose. The image build streams to this client and
62+ # nowhere else, and `modal app logs` cannot reach an ephemeral run —
63+ # so this terminal is the only place the build is visible. tee, not
64+ # tail: a run killed mid-pipe through tail takes its output with it.
65+ run: modal run .modal/frq/container.py 2>&1 | tee /tmp/frq-build.log
66+
67+ # The Sandbox leaves the AppImage in the nix-cache volume rather than
68+ # anywhere a runner can see, so fetch it back out. Same one file the
69+ # build copied there — a squashed closure, not a store path needing an
70+ # import on the other end.
71+ - name: Fetch the AppImage out of the volume
72+ env:
73+ MODAL_TOKEN_ID: ${{ secrets.MODAL_TOKEN_ID }}
74+ MODAL_TOKEN_SECRET: ${{ secrets.MODAL_TOKEN_SECRET }}
75+ run: modal volume get --force nix-cache artifacts/frq.AppImage frq.AppImage
76+
77+ - uses: actions/upload-artifact@v4
78+ with:
79+ name: frq-AppImage-${{ github.sha }}
80+ path: frq.AppImage
81+ if-no-files-found: error
82+
83+ # Kept whether or not the build succeeded: a failed run's log is the
84+ # one most worth reading, and it is gone with the runner otherwise.
85+ - uses: actions/upload-artifact@v4
86+ if: always()
87+ with:
88+ name: build-log
89+ path: /tmp/frq-build.log
90+ if-no-files-found: ignore
new file mode 100644
@@ -0,0 +1,90 @@
1+# The build, on rickub. GitLab CI next door reads source and no more —
2+# check-common on every push, the flake.lock nightly — and deliberately builds
3+# nothing. This is the other half: the AppImage, actually realised, on a
4+# machine that can hold libjoltcosmic's dependency tree.
5+#
6+# It does not realise it *here*. rickub runners are Linux x86-64 containers,
7+# and `nix build .#appimage` on one is the same laptop-shaped death CLAUDE.md
8+# warns about — so this job is a client. It hands the build to Modal, exactly
9+# as a person at a terminal would, and the Sandbox on its real VM does the
10+# work against the nix-cache volume. What a runner contributes is a checkout,
11+# a python, and somewhere to put the result afterwards.
12+#
13+# Lives in .rickub/workflows/ rather than .github/workflows/ because rickub
14+# reads one or the other and never both: with this directory present, a
15+# .github/workflows/ added later would be silently ignored. There is none
16+# today, so nothing is being shadowed — see
17+# https://rickub.com/docs/actions and https://rickub.com/docs/migrating-from-github
18+name: build
19+
20+on:
21+ push:
22+ workflow_dispatch:
23+
24+jobs:
25+ # The same read-only check GitLab runs, for the same reason: common/ compiles
26+ # twice and only the jolt half is on the way to anything anyone runs, so a
27+ # JVM call in shared code breaks the phone at a namespace nobody touched.
28+ # Seconds, no toolchain. Worth having on both hosts rather than depending on
29+ # which one a given push reaches.
30+ check-common:
31+ runs-on: ubuntu-latest
32+ steps:
33+ - uses: actions/checkout@v4
34+ - run: python3 tools/check-common.py common
35+
36+ appimage:
37+ runs-on: ubuntu-latest
38+ needs: check-common
39+ # The Modal side is an hour at its worst (a cold libjoltcosmic); this side
40+ # is waiting on it, plus the upload of the tree.
41+ timeout-minutes: 90
42+ steps:
43+ # The container copies `.` — the whole working tree, uncommitted edits
44+ # included. On a runner that is whatever the checkout left, so it wants
45+ # to be the commit and not a shallow surprise.
46+ - uses: actions/checkout@v4
47+
48+ - uses: actions/setup-python@v5
49+ with:
50+ python-version: "3.12"
51+
52+ - run: pip install --disable-pip-version-check modal
53+
54+ # Two secrets, set under Settings -> Secrets and variables. A Modal
55+ # token is the whole of this job's configuration: no nix, no builder,
56+ # no cache of its own.
57+ - name: nix build .#appimage, on Modal
58+ env:
59+ MODAL_TOKEN_ID: ${{ secrets.MODAL_TOKEN_ID }}
60+ MODAL_TOKEN_SECRET: ${{ secrets.MODAL_TOKEN_SECRET }}
61+ # Unpiped on purpose. The image build streams to this client and
62+ # nowhere else, and `modal app logs` cannot reach an ephemeral run —
63+ # so this terminal is the only place the build is visible. tee, not
64+ # tail: a run killed mid-pipe through tail takes its output with it.
65+ run: modal run .modal/frq/container.py 2>&1 | tee /tmp/frq-build.log
66+
67+ # The Sandbox leaves the AppImage in the nix-cache volume rather than
68+ # anywhere a runner can see, so fetch it back out. Same one file the
69+ # build copied there — a squashed closure, not a store path needing an
70+ # import on the other end.
71+ - name: Fetch the AppImage out of the volume
72+ env:
73+ MODAL_TOKEN_ID: ${{ secrets.MODAL_TOKEN_ID }}
74+ MODAL_TOKEN_SECRET: ${{ secrets.MODAL_TOKEN_SECRET }}
75+ run: modal volume get --force nix-cache artifacts/frq.AppImage frq.AppImage
76+
77+ - uses: actions/upload-artifact@v4
78+ with:
79+ name: frq-AppImage-${{ github.sha }}
80+ path: frq.AppImage
81+ if-no-files-found: error
82+
83+ # Kept whether or not the build succeeded: a failed run's log is the
84+ # one most worth reading, and it is gone with the runner otherwise.
85+ - uses: actions/upload-artifact@v4
86+ if: always()
87+ with:
88+ name: build-log
89+ path: /tmp/frq-build.log
90+ if-no-files-found: ignore