1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
# The build, on rickub. GitLab CI next door reads source and no more —
# check-common on every push — and deliberately builds nothing. This is the
# other half: the web bundle, actually compiled.
#
# It is not compiled *here*. The job hands the work to Modal exactly as a
# person at a terminal would, and the Sandbox does it against the `devshell`
# volume. What a runner contributes is a checkout, a python, and somewhere to
# put the result afterwards. The reason it goes to Modal is the volume the
# toolchain is cached on, not the size of the build.
#
# Lives in .rickub/workflows/ rather than .github/workflows/ because rickub
# reads one or the other and never both: with this directory present, a
# .github/workflows/ added later would be silently ignored. There is none
# today, so nothing is being shadowed — see
# https://rickub.com/docs/actions and https://rickub.com/docs/migrating-from-github
name: build
on:
push:
workflow_dispatch:
jobs:
# The same read-only check GitLab runs, for the same reason: common/ compiles
# for two targets, so a host-specific call in shared code breaks one of them
# at a namespace nobody touched. Seconds, no toolchain. Worth having on both
# hosts rather than depending on which one a given push reaches.
check-common:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: python3 tools/check-common.py common
# The Nim core: its own suite, and the library the Dart job needs.
#
# Debian and not the Alpine image this used to name. `libfrqcore.so` links
# OpenSSL and is glibc, so it has to be built somewhere its runtime can load
# it — a musl build would not load in `dart:*` at all.
#
# `libssl-dev` because `nim/nim.cfg` sets `-d:ssl`: std/net links -lssl and
# -lcrypto for the TLS on :6697, and `frq.atproto` uses httpclient over the
# same. Without it the compile fails on a missing -lcrypto, which reads as
# nothing to do with TLS.
nim-test:
runs-on: ubuntu-latest
container: nimlang/nim:2.2.10
steps:
- uses: actions/checkout@v4
- run: apt-get update -qq && apt-get install -y -qq libssl-dev
- name: The Nim suite
run: cd nim && for t in tests/t*.nim; do nim c -r --hints:off --path:src "$t"; done
- name: Build libfrqcore.so
run: |
cd nim && nim c --app:lib --mm:orc -d:release --hints:off --path:src \
--out:../build/nim/libfrqcore.so src/frq_core.nim
# Diagnostic, not a gate: prints what the .so will want at
# runtime, so a missing libssl in the Dart job is obvious
# from this log rather than from a StateError in that one.
objdump -p ../build/nim/libfrqcore.so | grep NEEDED || true
- uses: actions/upload-artifact@v4
with:
name: libfrqcore
path: build/nim/libfrqcore.so
if-no-files-found: error
# The Dart side of the same boundary, on the plain VM — no Flutter, no
# emulator, which is what makes it a second to run. It dlopens the library
# the job above built, so `libssl3` has to be there: the Dart SDK carries
# its own BoringSSL and does not bring OpenSSL with it.
dart-test:
runs-on: ubuntu-latest
container: dart:3.13
needs: [nim-test]
steps:
- uses: actions/checkout@v4
- run: apt-get update -qq && apt-get install -y -qq libssl3
- uses: actions/download-artifact@v4
with:
name: libfrqcore
path: build/nim
- run: cd dart/frq_core && dart pub get && dart test -r expanded
web:
runs-on: ubuntu-latest
needs: [check-common, nim-test, dart-test]
# What it spends its time on is the ClojureDart compile and, on a cold
# toolchain, fetching the pinned Flutter/JDK/Clojure tarballs.
timeout-minutes: 30
steps:
# The container copies `.` — the whole working tree, uncommitted edits
# included. On a runner that is whatever the checkout left, so it wants
# to be the commit and not a shallow surprise.
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install --disable-pip-version-check modal
# Two secrets, set under Settings -> Secrets and variables. A Modal
# token is the whole of this job's configuration: no nix, no builder,
# no cache of its own.
- name: Build the web bundle, on Modal
env:
MODAL_TOKEN_ID: ${{ secrets.MODAL_TOKEN_ID }}
MODAL_TOKEN_SECRET: ${{ secrets.MODAL_TOKEN_SECRET }}
# Unpiped on purpose. The image build streams to this client and
# nowhere else, and `modal app logs` cannot reach an ephemeral run —
# so this terminal is the only place the build is visible. tee, not
# tail: a run killed mid-pipe through tail takes its output with it.
run: modal run .modal/flutter-web/container.py 2>&1 | tee /tmp/frq-build.log
# The Sandbox leaves the bundle on the devshell volume rather than
# anywhere a runner can see, so fetch it back out.
- name: Fetch the bundle out of the volume
env:
MODAL_TOKEN_ID: ${{ secrets.MODAL_TOKEN_ID }}
MODAL_TOKEN_SECRET: ${{ secrets.MODAL_TOKEN_SECRET }}
run: |
modal volume get --force devshell \
frq-flutter-web/flutter/build/web web
- uses: actions/upload-artifact@v4
with:
name: frq-web-${{ github.sha }}
path: web
if-no-files-found: error
# Kept whether or not the build succeeded: a failed run's log is the
# one most worth reading, and it is gone with the runner otherwise.
- uses: actions/upload-artifact@v4
if: always()
with:
name: build-log
path: /tmp/frq-build.log
if-no-files-found: ignore
|