Commit graph halkyon/.forgejo/workflows
Author SHA1 Message Date
b01ba9ab93
ci: check pushed branches only, not a release's tag on the same commit
A release pushes main and then its tag, both on the same commit, and
on: push started a second run for the tag, checking that commit again
while main's run still did: "Checked before?" knows only runs that
finished. A tag deploys nothing, and the release script asks CI about
the commit, not the tag, so tags now start no run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 16:10:05 +02:00
28d22f3e94
chore: the SSH aliases and CI's runner label keep the servers' names
Some checks failed
CI / check (push) Successful in 15m47s
CI / deploy (push) Failing after 1m9s
The servers' names are no secret, so the settings that carry them stay
as they were, and nothing on a server or a laptop needs changing: CI
runs on `sahaquiel` again, the release uploads to `ramiel-fdroid`, and
deploys by hand go through `sahaquiel-deploy`. Prose still names a
server by the domain it serves, which says what a reader needs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 00:33:51 +02:00
93b0c0a6b0
chore: the servers are named by the domain they serve
Files called the servers by their own host names, which are the
operator's and say nothing a reader needs. They now say which domain a
server serves: learn.halkyon.eu's server (the instance and its CI
runner), the halkyon.app server (Forgejo and the F-Droid repository).

Two names are settings rather than words. CI's runner label becomes
`instance` (`runs-on: instance`, `labels: ["instance:host"]`): the
runner reads its installed copy of deploy/runner/config.yml, so jobs
wait in the queue until that copy is updated and the runner restarted.
The SSH aliases the scripts and docs use become `learn-deploy` and
`fdroid-upload`. AGENTS.md says to keep it so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 00:33:50 +02:00
72c15300ed
fix: Serverpod v4.0.4 and serverpod_offline_sync v0.0.10
serverpod_offline_sync v0.0.10 collects outbound changes in one
consistent snapshot, so a sync no longer fails on writes made while it
runs (upstream #84, #163), keeps checkpoints per author, so a device
clock moving backwards no longer stalls sync or resends data, and keeps
unique conflicts and foreign key defaults alike on every device. Its
tables and its messages are unchanged: no migration, and apps on v0.0.8
keep syncing. It needs Serverpod >=4.0.4 <4.1.0 (v4.0.4 is fixes only),
so the root overrides that held the workspace on one Serverpod go: each
package pins v4.0.4 itself. The generated code is identical under the
v4.0.4 CLI, which CI now uses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 18:09:27 +02:00
667180416a
ci: a commit that passed the checks isn't checked again
All checks were successful
CI / check (push) Successful in 5s
CI / deploy (push) Has been skipped
A fast-forward merge brings the commit a branch's push checked to main
unchanged, and CI ran every check a second time before deploying it. The
check job now marks every commit that passed in the runner's home
(checked/, kept 90 days), and a commit marked already skips straight to
its deploy; the sandbox check still runs every time. Only pushes run CI,
so only those who may push can leave a mark.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 22:17:12 +02:00
23b0d01d5e
ci: install exactly the committed lockfile, and try twice for pub's git bug
All checks were successful
CI / check (push) Successful in 7m8s
CI / deploy (push) Successful in 1m1s
`flutter pub get --enforce-lockfile` installs what pubspec.lock says or
fails with pub's own message, so CI never resolves something else on
its own.

The second try works around https://github.com/dart-lang/pub/issues/4912
(Dart v3.13): when the runner's cached copy of a git dependency's
repository lacks the locked commit, pub resolves that package without
its dependencies. That failed the CI run of 079179d with Flutter's
"package_graph.json: dependencies for `universal_platform` missing".
With --enforce-lockfile the first run fails at pub instead, having
fetched the commit, and the second goes through; checked locally on a
copy of the pub cache with the editor fork's commit removed. pub's fix
(ebba0dc) is in no Dart release yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-02 22:31:46 +02:00
4c2a363160
ci: check the generated code; deploy: migrate before the switch
All checks were successful
CI / check (push) Successful in 5m51s
CI / deploy (push) Successful in 5s
Two of Serverpod's own conventions, as its v4 project template and roles have them.

CI now runs `serverpod generate` (CLI pinned to the server's Serverpod version, like
the template's workflow) and fails if the committed generated code differs from its
output, so a model or endpoint changed without regenerating can't reach main. Checked in
a clean clone: today's code passes, a committed stale generated file fails.

redeploy.sh now applies the migrations in a run of their own, with Serverpod's
maintenance role (--role=maintenance --apply-migrations), after the dump and before
switching to the new server. That run exits with 0 only if the migrations worked, and
each migration is one transaction, so a failing one stops the deploy while the old
server keeps serving; before, the new server applied them at start and would have
crash-looped. Tested on the local stack: a deploy through the new step, and a failing
run (wrong database password) that exits with 1 after six attempts. AGENTS.md says
migrations must keep the old server working until the switch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-09-30 22:55:24 +02:00
9877fba155
ci: throwaway test passwords, analytics off every time
All checks were successful
CI / check (push) Successful in 4m15s
CI / deploy (push) Successful in 5s
The first CI run failed at the server tests: they need the test run mode's passwords,
which passwords.yaml holds on a developer's machine and which is never committed. Without
them Serverpod's test tools exit with code 1 and print nothing. Reproduced in a clean
clone; with the three passwords in the environment (SERVERPOD_DATABASE_PASSWORD,
SERVERPOD_PASSWORD_serverSideSessionKeyHashPepper, SERVERPOD_PASSWORD_serviceSecret) all
checks pass. The job now draws new random ones every time: they only guard the test
database it creates in its own checkout.

Also switch off Flutter's and Dart's usage reports on every job, not only when installing
the SDK: the cancelled first run left an SDK behind before getting that far.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-09-30 22:24:05 +02:00
c837cee51f
ci: stop a job whose sandbox leaks, first thing
Some checks failed
CI / check (push) Failing after 2m27s
CI / deploy (push) Has been skipped
Installing the runner on sahaquiel, `useradd --system` gave `runner` user ID 999: the ID
under which PostgreSQL and the server run inside their containers, which Docker doesn't
remap. To the kernel, those processes were the runner's own, so a job could have read the
server's environment (database password, session pepper, GitHub secret) and, through
/proc, PostgreSQL's files and its local socket, which the official image trusts. The
sandbox's other walls don't help there: ProtectProc hides only other users' processes,
IPAddressDeny doesn't cover Unix sockets. Only our own check ran as 999; `runner` now has
user and group ID 61000.

The check job now starts with a Sandbox step that fails unless only the runner's own
processes are visible, all in one process namespace, /home/deploy is invisible, the
Docker socket is unusable and Docker's bridge network unreachable. It would have caught
this; outside a sandbox it fails as it should. The unit and AGENTS.md say how to create
the user.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-09-30 22:12:31 +02:00
ce2cd42ffe
ci: check every push on sahaquiel and deploy a green main
Some checks failed
CI / check (push) Failing after 3m22s
CI / deploy (push) Has been skipped
Pushes to main were deployed by hand. Now Forgejo Actions runs tool/check.sh on every
push, and a green main deploys learn.halkyon.eu by itself.

The runner (forgejo-runner v13.1.0) runs on sahaquiel as the unprivileged user `runner`,
with jobs directly on the host: containers would take Docker access, which is root on the
production server, and Forgejo warns that host jobs have no isolation from the runner's
user. So its systemd unit (deploy/runner/forgejo-runner.service) provides it: /home and
the Docker socket invisible, Docker's container networks unreachable, no capabilities,
two cores and 3 GB at most, lowest priority. It is registered to this repository only,
with the UUID and token of Forgejo's "Create new runner" (the old `register` is
deprecated); the token reaches it as a systemd credential. The configuration
(deploy/runner/config.yml) was dry-run against v13.1.0, the unit rates 1.8 in
`systemd-analyze security`. CI runs on pushes only: a pull request from a fork would run
its code on the production server.

The deploy job calls deploy/redeploy.sh as `deploy`, through a key that authorized_keys
pins to that script and to connections from sahaquiel itself. The script deploys the
commit CI checked while it is still main's tip, as a fast-forward, only if every new
commit is signed by a key in deploy/allowed-signers (as the deployed checkout lists
them), after dumping the database (the last ten are kept), and fails unless the server
answers afterwards; a failure puts the checkout back. Tested against the local stack:
refusals of a malformed argument, a stale commit, a forged unsigned commit and a
non-fast-forward, a real deploy with its dump, a repeat, and a failed build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-09-30 21:49:04 +02:00