Three things that made this hard to stand up, all found by actually installing it.
install.sh
Installing meant copying six files by hand and editing values across three of
them, with nothing saying which. Now sudo ./install.sh prompts for each
value, offering whatever is already configured as the default, and installs
the program, the units and the timer.
Idempotent — re-running and pressing Enter at every prompt reports every
file unchanged, which makes it the way to change one setting later rather than
a one-shot. Verified by running it twice against a temp prefix:
An existing token is kept unless you type a new one, and it is never echoed.
Configuration problems now name themselves
This was the reported symptom, and it was worse than "didn't name which one".
A missing token_file produced three messages — a raw bash error with a line
number, a claim that the token was empty when it was absent, and a generic
failure — none of which named the setting pointing there:
dejima-ci-allowlist: line 116: /etc/dejima-ci/forge-token: No such file or directory
dejima-ci-allowlist: empty token in /etc/dejima-ci/forge-token
dejima-ci-allowlist: collection failed; leaving the existing allowlist in place
Now:
dejima-ci-allowlist: cannot run - 2 problem(s) with the configuration:
- token_file points at /etc/dejima-ci/forge-token, which does not exist - a
file holding a READ-ONLY forge token, mode 0600
- repos_file points at /etc/dejima-ci/repos, which does not exist - one
owner/repo per line - the repositories allowed to widen this runner's
allowlist
Fix all of them, or re-run install.sh, which prompts for each.
Every setting and file is checked before any is used, and all problems arrive
in one run — reporting them one at a time costs a run per problem.
Exit codes were ambiguous in a way that matters
check uses 1 for "there is a pending change", which is an ordinary answer. die also used 1. So on a timer, an unreachable forge was
indistinguishable from the allowlist having moved.
0
nothing to do
1
check only — pending change
2
a fault: bad config, unreachable forge, malformed entry
install.sh's closing message stops hedging as a result.
The upstream setting
"A resolver" has three plausible readings and the wrong two both look
reasonable. It is now explained in the config example, the README and the
installer's prompt: the resolver dnsmasq forwards allowed queries to —
not the dnsmasq being configured, not the bridge gateway — and it has to
resolve your forge, so an internal forge means internal DNS. The installer
offers what the host already uses (resolvectl / resolv.conf) and flags a
local stub if that is what it finds.
Two smaller ones found on the way
The root check was really a writability check, and was wrong whenever the
directories were overridden — which also made the installer untestable. It
now checks what it needs, and skips the unit install with a note if UNIT_DIR is not writable.
Two list displays expanded unquoted, so a list could word-split or glob.
Tests
20 passing, six new: that the preflight names the setting/path/remedy, that
all problems arrive in one run, that an empty token reads as empty rather than
missing, the upstream hint's content, the exit-code split, and that show
still works when the configuration is the thing being diagnosed.
**Summary generated by LLM agent**
Three things that made this hard to stand up, all found by actually installing it.
## `install.sh`
Installing meant copying six files by hand and editing values across three of
them, with nothing saying which. Now `sudo ./install.sh` prompts for each
value, offering whatever is already configured as the default, and installs
the program, the units and the timer.
**Idempotent** — re-running and pressing Enter at every prompt reports every
file unchanged, which makes it the way to change one setting later rather than
a one-shot. Verified by running it twice against a temp prefix:
```
unchanged /tmp/inst/etc/repos
unchanged /tmp/inst/etc/base-domains
unchanged /tmp/inst/etc/config
unchanged /tmp/inst/bin/dejima-ci-allowlist
unchanged /tmp/inst/units/dejima-ci-allowlist.service
```
An existing token is kept unless you type a new one, and it is never echoed.
## Configuration problems now name themselves
This was the reported symptom, and it was worse than "didn't name which one".
A missing `token_file` produced three messages — a raw bash error with a line
number, a claim that the token was *empty* when it was absent, and a generic
failure — none of which named the setting pointing there:
```
dejima-ci-allowlist: line 116: /etc/dejima-ci/forge-token: No such file or directory
dejima-ci-allowlist: empty token in /etc/dejima-ci/forge-token
dejima-ci-allowlist: collection failed; leaving the existing allowlist in place
```
Now:
```
dejima-ci-allowlist: cannot run - 2 problem(s) with the configuration:
- token_file points at /etc/dejima-ci/forge-token, which does not exist - a
file holding a READ-ONLY forge token, mode 0600
- repos_file points at /etc/dejima-ci/repos, which does not exist - one
owner/repo per line - the repositories allowed to widen this runner's
allowlist
Fix all of them, or re-run install.sh, which prompts for each.
```
Every setting and file is checked before any is used, and all problems arrive
in one run — reporting them one at a time costs a run per problem.
## Exit codes were ambiguous in a way that matters
`check` uses `1` for "there is a pending change", which is an ordinary answer.
`die` also used `1`. So on a timer, **an unreachable forge was
indistinguishable from the allowlist having moved.**
| | |
| --- | --- |
| `0` | nothing to do |
| `1` | `check` only — pending change |
| `2` | a fault: bad config, unreachable forge, malformed entry |
`install.sh`'s closing message stops hedging as a result.
## The `upstream` setting
"A resolver" has three plausible readings and the wrong two both look
reasonable. It is now explained in the config example, the README and the
installer's prompt: **the resolver dnsmasq forwards *allowed* queries to** —
not the dnsmasq being configured, not the bridge gateway — and it has to
resolve your forge, so an internal forge means internal DNS. The installer
offers what the host already uses (`resolvectl` / `resolv.conf`) and flags a
local stub if that is what it finds.
## Two smaller ones found on the way
- The root check was really a writability check, and was wrong whenever the
directories were overridden — which also made the installer untestable. It
now checks what it needs, and skips the unit install with a note if
`UNIT_DIR` is not writable.
- Two list displays expanded unquoted, so a list could word-split or glob.
## Tests
20 passing, six new: that the preflight names the setting/path/remedy, that
all problems arrive in one run, that an empty token reads as empty rather than
missing, the upstream hint's content, the exit-code split, and that `show`
still works when the configuration is the thing being diagnosed.
Three things that made this hard to stand up.
An install meant copying six files by hand and then editing values across
three of them, with nothing saying which. install.sh now prompts for each,
offering whatever is already configured as the default, and installs the
program and the units. It is idempotent: re-running and pressing Enter at
every prompt reports every file unchanged, which also makes it the way to
change one setting later rather than a one-shot. --non-interactive
re-installs from the existing configuration.
Configuration problems were reported one at a time, and the worst of them
barely at all. A missing token_file surfaced as bash's own "No such file or
directory" with a line number, then "empty token" - it was absent, not empty -
then "collection failed": three messages, none naming the setting that pointed
there or what belongs in it. A preflight now checks every setting and every
file before any of them is used and reports all of it at once, each with the
key, the path and what it should contain.
Exit codes were ambiguous in a way that matters for a timer. `check` uses 1
for "there is a pending change", which is an ordinary answer, and `die` also
used 1 - so an unreachable forge was indistinguishable from the allowlist
having moved. Faults are 2 now, and 1 means only what it says. install.sh's
closing message stops hedging as a result.
The upstream setting gets a real explanation in the config example, the
README and the installer's prompt, because "a resolver" has three plausible
readings and the wrong two both look reasonable. It is the resolver dnsmasq
forwards ALLOWED queries to - not the dnsmasq being configured, not the bridge
gateway - and it has to resolve the forge, so an internal forge means internal
DNS. The installer offers what the host already uses, and flags a local stub
if that is what it finds.
Two smaller ones on the way past. The root check was really a writability
check, and was wrong whenever the directories were overridden - which also
made the installer impossible to test; it now checks what it actually needs.
And two list displays expanded unquoted, so a list could word-split or glob.
Six new tests cover the preflight's naming, that all problems arrive in one
run, that an empty token reads as empty rather than missing, the exit-code
split, and that `show` still works when the configuration is the thing being
diagnosed. 20 passing.
branderson
approved these changes 2026-08-05 14:28:49 -07:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary generated by LLM agent
Three things that made this hard to stand up, all found by actually installing it.
install.shInstalling meant copying six files by hand and editing values across three of
them, with nothing saying which. Now
sudo ./install.shprompts for eachvalue, offering whatever is already configured as the default, and installs
the program, the units and the timer.
Idempotent — re-running and pressing Enter at every prompt reports every
file unchanged, which makes it the way to change one setting later rather than
a one-shot. Verified by running it twice against a temp prefix:
An existing token is kept unless you type a new one, and it is never echoed.
Configuration problems now name themselves
This was the reported symptom, and it was worse than "didn't name which one".
A missing
token_fileproduced three messages — a raw bash error with a linenumber, a claim that the token was empty when it was absent, and a generic
failure — none of which named the setting pointing there:
Now:
Every setting and file is checked before any is used, and all problems arrive
in one run — reporting them one at a time costs a run per problem.
Exit codes were ambiguous in a way that matters
checkuses1for "there is a pending change", which is an ordinary answer.diealso used1. So on a timer, an unreachable forge wasindistinguishable from the allowlist having moved.
01checkonly — pending change2install.sh's closing message stops hedging as a result.The
upstreamsetting"A resolver" has three plausible readings and the wrong two both look
reasonable. It is now explained in the config example, the README and the
installer's prompt: the resolver dnsmasq forwards allowed queries to —
not the dnsmasq being configured, not the bridge gateway — and it has to
resolve your forge, so an internal forge means internal DNS. The installer
offers what the host already uses (
resolvectl/resolv.conf) and flags alocal stub if that is what it finds.
Two smaller ones found on the way
directories were overridden — which also made the installer untestable. It
now checks what it needs, and skips the unit install with a note if
UNIT_DIRis not writable.Tests
20 passing, six new: that the preflight names the setting/path/remedy, that
all problems arrive in one run, that an empty token reads as empty rather than
missing, the upstream hint's content, the exit-code split, and that
showstill works when the configuration is the thing being diagnosed.