3 files changed,
+167,
-7
A
LICENSE
+21,
-0
1@@ -0,0 +1,21 @@
2+MIT License
3+
4+Copyright (c) 2026 BT
5+
6+Permission is hereby granted, free of charge, to any person obtaining a copy
7+of this software and associated documentation files (the "Software"), to deal
8+in the Software without restriction, including without limitation the rights
9+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+copies of the Software, and to permit persons to whom the Software is
11+furnished to do so, subject to the following conditions:
12+
13+The above copyright notice and this permission notice shall be included in all
14+copies or substantial portions of the Software.
15+
16+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+SOFTWARE.
+16,
-0
1@@ -23,6 +23,22 @@ go test ./...
2 nix build .#ci # ./result/bin/ci
3 ```
4
5+To see the appview with something in it:
6+
7+```sh
8+devenv up -d # bridge, runner and appview on 127.0.0.1:8475-8477
9+devenv tasks run ci:seed # pushes a demo commit; its run waits at "Deploy demo?"
10+# open http://127.0.0.1:8477 and sign in with the password "dev"
11+devenv processes logs runner # or: appview, bridge
12+devenv processes down
13+```
14+
15+`ci:seed` creates a demo repo on a directory "forge" (`.devenv/state/ci-dev`) with a post-receive hook for the bridge, so a push goes through the real path: hook, bridge, runner, a gated run. Run it again for another run. Actions use plain bash, not devenv; the dev credentials (`dev`, an agent token for `claude`) are fixed strings and only for this. The first start downloads Go modules, so give it a minute.
16+
17+## License
18+
19+MIT, see [LICENSE](LICENSE).
20+
21 ## Using the engine in another repo
22
23 Add the flake as a devenv input and put the package on `PATH`:
+130,
-7
1@@ -1,8 +1,20 @@
2-{ pkgs, ... }:
3+{ pkgs, config, ... }:
4
5-# Scaffold for a new project. devkit (imported in devenv.yaml) already provides
6-# bug, secrets, local-code-review, jujutsu, and the customized interactive bash
7-# environment, so don't repeat those here.
8+# devkit (imported in devenv.yaml) already provides bug, secrets,
9+# local-code-review, jujutsu, and the customized interactive bash environment,
10+# so don't repeat those here.
11+#
12+# Seeing the appview in development:
13+#
14+# devenv up -d # bridge, runner and appview on 127.0.0.1:8475-8477
15+# devenv tasks run ci:seed # a demo forge repo; pushing to it starts a gated run
16+# open http://127.0.0.1:8477 # password: dev
17+#
18+# Everything lives in .devenv/state/ci-dev (delete it to start over).
19+let
20+ dev = "${config.devenv.state}/ci-dev";
21+ bin = "${dev}/bin/ci";
22+in
23 {
24 languages.go.enable = true;
25
26@@ -10,9 +22,120 @@
27 pkgs.golangci-lint
28 pkgs.just
29 pkgs.jq
30+ pkgs.git
31+ pkgs.mkpasswd
32+ pkgs.util-linux # flock
33 ];
34
35- # enterShell = ''
36- # project-specific setup; concatenated onto devkit's
37- # '';
38+ # Builds the ci binary (and its ci-result alias) into the dev state dir.
39+ # The three processes start together, so builds are serialized.
40+ scripts.ci-dev-build.exec = ''
41+ set -euo pipefail
42+ mkdir -p ${dev}/bin
43+ (
44+ flock 9
45+ cd "$DEVENV_ROOT"
46+ go build -o ${dev}/bin/ci ./cmd/ci
47+ ln -sf ci ${dev}/bin/ci-result
48+ ) 9>${dev}/build.lock
49+ '';
50+
51+ # Dev credentials: the appview password is "dev", the runner token and an
52+ # agent token ("claude") are fixed strings. Never use these anywhere real.
53+ scripts.ci-dev-secrets.exec = ''
54+ set -euo pipefail
55+ mkdir -p ${dev}
56+ [ -f ${dev}/runner-token ] || echo dev-runner-token > ${dev}/runner-token
57+ [ -f ${dev}/agent-tokens ] || echo "claude dev-agent-token-0123456789" > ${dev}/agent-tokens
58+ [ -f ${dev}/password-hash ] || mkpasswd -m bcrypt -R 5 dev > ${dev}/password-hash
59+ '';
60+
61+ processes.bridge.exec = ''
62+ ci-dev-build
63+ exec ${bin} bridge serve --listen 127.0.0.1:8475 \
64+ --db ${dev}/bridge.db --spool ${dev}/spool --forge-host git.dev
65+ '';
66+
67+ # Actions run with plain bash (no devenv) and as child processes: this is a
68+ # UI playground, not the sandboxed production setup.
69+ processes.runner.exec = ''
70+ ci-dev-build
71+ ci-dev-secrets
72+ exec ${bin} runner serve --listen 127.0.0.1:8476 \
73+ --state-dir ${dev}/runner \
74+ --bridge ws://127.0.0.1:8475/events \
75+ --knot git.dev --clone-url '${dev}/forge/{repo}.git' \
76+ --launcher local --shell bash \
77+ --token-file ${dev}/runner-token
78+ '';
79+
80+ processes.appview.exec = ''
81+ ci-dev-build
82+ ci-dev-secrets
83+ exec ${bin} appview serve --listen 127.0.0.1:8477 \
84+ --runner http://127.0.0.1:8476 --runner-token-file ${dev}/runner-token \
85+ --password-hash-file ${dev}/password-hash \
86+ --agent-tokens-file ${dev}/agent-tokens \
87+ --knot git.dev --secure-cookies=false
88+ '';
89+
90+ # A demo repo on a "forge" that is a directory. Each run of this task commits
91+ # to it and pushes; the repo's post-receive hook hands the push to the
92+ # bridge, the runner starts the demo workflow, and the run waits at its
93+ # "Deploy demo?" gate. Run it again for another run.
94+ scripts.ci-dev-seed.exec = ''
95+ set -euo pipefail
96+ ci-dev-build
97+ ci-dev-secrets
98+ forge=${dev}/forge/demo.git
99+ work=${dev}/work/demo
100+ if [ ! -d "$forge" ]; then
101+ git init -q --bare -b main "$forge"
102+ cat > "$forge/hooks/post-receive" <<HOOK
103+ #!/bin/sh
104+ exec ${bin} bridge notify --spool ${dev}/spool --server http://127.0.0.1:8475 --repo demo
105+ HOOK
106+ chmod +x "$forge/hooks/post-receive"
107+ fi
108+ if [ ! -d "$work/.git" ]; then
109+ mkdir -p "$work"
110+ git init -q -b main "$work"
111+ git -C "$work" remote add origin "$forge"
112+ fi
113+ mkdir -p "$work/.tangled/workflows"
114+ cat > "$work/.tangled/workflows/deploy.yml" <<'WF'
115+ # Demo workflow for the dev appview.
116+ when: [{ event: [push, manual], branch: main }]
117+ engine: devenv
118+ steps: [test, build, deploy]
119+ actions:
120+ test:
121+ command: |
122+ for i in 1 2 3; do echo "testing $i"; sleep 1; done
123+ echo "something noisy" >&2
124+ printf '%s\n' '{"suite":"demo","passed":3}' > "$CI_ARTIFACTS/report.json"
125+ ci-result ok --summary "3 tests passed" --attach "report=report.json"
126+ build:
127+ command: |
128+ echo "building $CI_SHA"
129+ echo built > "$CI_STATE/artifact"
130+ deploy:
131+ command: echo "deploying $(cat "$CI_STATE/artifact")"
132+ gate: { prompt: "Deploy demo?" }
133+ rollback:
134+ command: echo "rolling back $(cat "$CI_STATE/artifact")"
135+ manual: true
136+ style: danger
137+ label: Roll back
138+ gate: { prompt: "Roll the demo back?" }
139+ WF
140+ n=$(( $(cat "$work/counter" 2>/dev/null || echo 0) + 1 ))
141+ echo "$n" > "$work/counter"
142+ git -C "$work" add -A
143+ git -C "$work" -c user.name=dev -c [email protected] commit -q -m "demo change $n"
144+ git -C "$work" push -q origin main
145+ echo "pushed demo change $n; open http://127.0.0.1:8477 (password: dev)"
146+ '';
147+
148+ tasks."ci:seed".exec = "ci-dev-seed";
149 }