@yunuseon/kayra 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,75 @@
1
+ ---
2
+ name: kayra-lab
3
+ description: Run kayra's upgrade lab (test/lab/upgrade.sh) - a throwaway Multipass server brought up at the last release, upgraded to the kayra checkout, and taken through every deploy path - and read its result. Use this before merging any change to kayra's platform (ansible/, compose/, scripts/, cli/src/deployer/, terraform/), after a release to prove the full path from it, whenever the user asks to "test this on a VM", "run the lab", "does the upgrade work", or when a bug was found on a real server and needs a reproduction.
4
+ ---
5
+
6
+ # The upgrade lab
7
+
8
+ What broke on kayra's real servers broke on **upgrades**, never on the fresh
9
+ VMs it used to be tested on: Authelia kept rules it had never loaded, an app
10
+ was left unregistered, every image was rebuilt on every run. The lab
11
+ reproduces the upgrade path end to end:
12
+
13
+ 1. A temp fleet pinned to the last release (or `--from <version>`) brings a
14
+ Multipass server up and deploys a fixture app (`test/lab/app`) by a
15
+ signed webhook.
16
+ 2. The fleet's kayra becomes this checkout; `kayra deploy` (which ends with
17
+ `kayra verify`).
18
+ 3. A new commit by webhook; the same commit again must be `CACHED`.
19
+ 4. A commit the compose policy refuses: the run fails naming the bind mount,
20
+ and the old release keeps serving.
21
+ 5. `kayra app rollback` to the first release, the registry GC, `kayra verify`.
22
+
23
+ ## Before you start
24
+
25
+ - Run it from a kayra checkout, or a worktree of one, that holds the change
26
+ under test. The lab links the fleet to **that directory's working tree**,
27
+ uncommitted changes included: say which commit and which changes it tests.
28
+ - `multipass list` shows no `kayra-lab` VM. A kept one from an earlier
29
+ failure is torn down first (its command was printed at the end of that run).
30
+ - Multipass, mkcert, OpenTofu, Ansible, Node 24 and python3 on the Mac.
31
+
32
+ ## Running it
33
+
34
+ About 15 minutes. Run it in the background with its output in a log, and
35
+ follow the phases rather than waiting blind:
36
+
37
+ ```bash
38
+ bash test/lab/upgrade.sh [--from <version>] [--keep] > <log> 2>&1; echo "lab exit $?" >> <log>
39
+ ```
40
+
41
+ Watch for lines starting `── ` (a phase), `✓` and `✗`, `kept for`, and
42
+ `lab exit`. Keep one watcher per log: two watchers on the same file report
43
+ every event twice. A release older than the one that allowed `http://` app
44
+ repos on multipass prints "cannot clone over http://: the app arrives with
45
+ the upgrade". The release then sets up only the platform, which is expected
46
+ for that range.
47
+
48
+ ## When it fails
49
+
50
+ A failure keeps everything, the VM (`kayra-lab`), the temp fleet and the log,
51
+ and prints where. Look before you change anything:
52
+
53
+ - The lab's own log: `grep -nE 'fatal:|FAILED|✗|kayra:|rror' <lab log>`.
54
+ - What the server did: from the kept fleet directory,
55
+ `node node_modules/@yunuseon/kayra/bin/kayra.js app runs lab sample` and
56
+ `… app logs lab sample`.
57
+ - Then decide: **a kayra bug or a lab bug?** Most first-run failures were the
58
+ lab's. Each was fixed in the lab, and twice the fix showed something kayra
59
+ should handle:
60
+ - git's dumb HTTP cannot serve the shallow clones kayra makes, so the lab
61
+ uses smart HTTP (`test/lab/git-http.py`);
62
+ - a repo URL without `owner/name` matches no forge, so its webhooks were
63
+ refused;
64
+ - a bind mount appended to the end of the compose file landed under
65
+ `networks:`, so compose rejected the file before the policy saw it;
66
+ - `verify` did not yet understand an app registered but never deployed:
67
+ that was a gap in kayra, fixed there, with a case added.
68
+ - Tear the kept VM down with the printed command before the next run.
69
+
70
+ ## After a pass
71
+
72
+ The VM and the temp fleet are destroyed unless `--keep`. Report the five
73
+ phases and the final `kayra verify`, and what the run covered. A bug found on
74
+ a real server, which the lab did not catch, gets a step here: the lab is
75
+ only as good as the history it encodes.
@@ -0,0 +1,103 @@
1
+ ---
2
+ name: kayra-release
3
+ description: Cut a release of kayra (the npm package @yunuseon/kayra) - version bump PR, checks, merge, tag vX.Y.Z, the release workflow publishing to npmjs.org, and checking the published package. Use this whenever kayra changes are merged and need to reach fleets, when the user says "release", "cut a release", "tag it", "publish kayra", "ship 0.x", or asks how to get merged kayra work onto servers - a fleet only gets kayra changes through a release.
4
+ ---
5
+
6
+ # Releasing kayra
7
+
8
+ A fleet runs the kayra version its `package.json` pins, from npmjs.org, so
9
+ merged work reaches no server until it is released. A release is a version
10
+ bump on `main`, a tag `v<version>` on the bump's merge commit, and
11
+ `.github/workflows/release.yml`, which publishes that tag to npm.
12
+
13
+ ## Before anything: don't touch the user's checkout
14
+
15
+ The user works in the same checkouts in parallel: their own branch,
16
+ uncommitted changes, sometimes mid-task. Do all of this in a **separate git
17
+ worktree off `origin/main`**, and run every step with absolute paths inside
18
+ it. Write the steps as one script under `set -euo pipefail` with one command
19
+ per line. A guard such as `[ -z "$(git status --short)" ] && …` protects only
20
+ what is inside its `&&` chain: anything after a `;` runs regardless, and that
21
+ is how writes once landed in the user's working tree. If a step needs the
22
+ user's checkout (it rarely does here), stop and ask.
23
+
24
+ ## 1. What goes into this release
25
+
26
+ - `git log --oneline v<last>..origin/main` and the merged PRs since the last tag.
27
+ - Check that every PR you count is really on `main`: a PR stacked on another
28
+ branch merges into **that** branch (`gh pr view <n> --json baseRefName`).
29
+ It happened once: the PR was merged, its work never reached `main`.
30
+ - **Stop when there is nothing to release.** If the log since the last tag is
31
+ empty, or a PR you were told is merged is still open (`gh pr view <n> --json
32
+ state`), the release would be empty: stop and say so, even when asked to
33
+ release. Make the script fail here (`test -n "$(git log …)"`, `test "$state"
34
+ = MERGED`), not print and go on: once a run printed `#41 OPEN` and went on to
35
+ open an empty release PR.
36
+ - Pick the version (0.x):
37
+ - **patch** for fixes;
38
+ - **minor** for a new command, a new setting, a behaviour change (for example
39
+ `deploy` newly ending with `verify`), and anything breaking for fleets.
40
+ - A breaking change lists exactly what a fleet must change, in the release
41
+ commit and the PR. Fleets read it when they bump.
42
+
43
+ ## 2. The bump PR
44
+
45
+ In the worktree:
46
+
47
+ ```bash
48
+ git -C "$W" switch -c release-<version>
49
+ npm --prefix "$W" version <version> --no-git-tag-version # package.json + package-lock.json only
50
+ git -C "$W" add package.json package-lock.json
51
+ git -C "$W" commit -s -F <message file> # DCO sign-off; the message summarises the PRs
52
+ git -C "$W" push -u origin release-<version>
53
+ gh pr create --repo yunuseon/kayra --base main --head release-<version> --title "<version>" --body-file <file>
54
+ gh pr checks release-<version> --repo yunuseon/kayra --watch --interval 20
55
+ ```
56
+
57
+ Write commit messages and PR bodies to a file first (quoted heredoc):
58
+ backticks inside `-m "…"` or a double-quoted `--body` are executed by the
59
+ shell.
60
+
61
+ ## 3. Merge and tag
62
+
63
+ `main` is protected: it changes only through a PR with green `checks`, for
64
+ admins too. Merging and pushing a tag both ask the operator (kayra's guard);
65
+ that is expected, say what you are doing.
66
+
67
+ ```bash
68
+ gh pr merge <n> --repo yunuseon/kayra --merge --delete-branch
69
+ merge="$(gh pr view <n> --repo yunuseon/kayra --json mergeCommit --jq .mergeCommit.oid)"
70
+ git -C "$W" fetch -q origin && git -C "$W" checkout -q "$merge"
71
+ grep '"version"' "$W/package.json" # must be the new version
72
+ git -C "$W" tag v<version> # lightweight, on the merge commit, like every earlier tag
73
+ git -C "$W" push origin v<version>
74
+ ```
75
+
76
+ Release tags are protected (a ruleset): once pushed, a tag cannot be moved or
77
+ deleted. A wrong tag is fixed by the next patch release, not by retagging.
78
+ So check the commit before you tag it.
79
+
80
+ ## 4. Watch it publish
81
+
82
+ ```bash
83
+ run="$(gh run list --repo yunuseon/kayra --workflow release.yml --limit 1 --json databaseId --jq '.[0].databaseId')"
84
+ gh run watch "$run" --repo yunuseon/kayra --exit-status
85
+ npm view @yunuseon/kayra@<version> version # may lag a minute after the run: retry for ~2 minutes
86
+ ```
87
+
88
+ Then look inside the published package for what this release added, not
89
+ just at its version. The fleets get the package, not the repo, and only the
90
+ paths in `package.json`'s `files` ship:
91
+
92
+ ```bash
93
+ d="$(mktemp -d)" && (cd "$d" && npm pack @yunuseon/kayra@<version> --silent && tar -tzf *.tgz | grep -E '<new files>')
94
+ ```
95
+
96
+ Test-only paths (`test/`, `ci/`) are meant to stay out.
97
+
98
+ ## 5. Afterwards
99
+
100
+ - Remove the worktree and the local branch.
101
+ - The release reaches servers through each fleet: that is the **kayra-rollout**
102
+ skill. Say so, and say whether servers need a deploy at all (a release that
103
+ only changes the CLI or the agent tooling changes nothing on servers).
@@ -0,0 +1,92 @@
1
+ ---
2
+ name: kayra-rollout
3
+ description: Bring a kayra fleet's servers to a new kayra release - the version bump PR in each fleet (in a separate git worktree), its checks, the merge, then kayra deploy server by server in a safe order with kayra verify after each. Use this whenever a kayra release exists that the servers don't run yet, when the user says "roll it out", "bump the fleets", "deploy the new kayra", "update the servers", "Renovate opened a kayra bump", or after the kayra-release skill finishes.
4
+ ---
5
+
6
+ # Rolling kayra out to the fleets
7
+
8
+ A fleet pins kayra in its `package.json`; its servers run what that pinned
9
+ kayra copies to them. Rolling out means: bump the pin in the fleet, merge,
10
+ then deploy each server, least important first, checking each before the
11
+ next.
12
+
13
+ ## The rule that matters most: separate worktrees
14
+
15
+ The user works in the same checkouts in parallel: their own branch and
16
+ uncommitted changes. Make every change in a **separate git worktree off
17
+ `origin/main`**, with absolute paths, as one script under
18
+ `set -euo pipefail`, one command per line. Never let a step that writes run
19
+ after `;` behind a guard: `[ -z "$(git status --short)" ] && git switch -c x;
20
+ npm install …` ran the `npm install` in the user's tree. Record the user's
21
+ `git status --short` before and check it is unchanged at the end.
22
+
23
+ Deploying is different: `require-main.sh` refuses a real server unless **the
24
+ fleet's checkout** is a clean `main` equal to `origin/main`, with the pinned
25
+ kayra installed (not a link). If the user's checkout isn't (their branch,
26
+ their changes), don't switch it: tell them, and deploy when they say it is
27
+ on `main`.
28
+
29
+ ## 1. Which fleets, which servers, which order
30
+
31
+ - The fleets are the repositories that pin `@yunuseon/kayra`. Their own
32
+ CLAUDE.md or README names them, how their PRs work (GitHub `gh`, Forgejo
33
+ `tea`), and the rollout order. Read that and follow it.
34
+ - Without an order given: multipass and test servers first, then home
35
+ servers, then real ones (hetzner), the least important first. Platform
36
+ servers get `kayra deploy`; edge and NAS servers get `kayra setup`.
37
+ - Read the release notes between the fleet's version and the new one. A
38
+ breaking release says exactly what a fleet must change (settings renamed or
39
+ removed, new secrets): those changes go into the same bump PR.
40
+
41
+ ## 2. The bump PR, per fleet
42
+
43
+ In a worktree of the fleet:
44
+
45
+ ```bash
46
+ npm install --prefix "$W" --silent -D -E @yunuseon/kayra@<version>
47
+ # …the fleet changes the release asks for (vault edits: decrypt in memory, never print a value)
48
+ cd "$W" && kayra validate
49
+ ```
50
+
51
+ Run the fleet's checks the way its CI does, against **tracked files only**
52
+ (untracked local folders such as a lab server must not count): copy
53
+ `git ls-files` plus `node_modules` into a temp context and
54
+ `docker build -f <kayra checkout>/ci/checks/Dockerfile --build-context fleet=<context> --build-arg CHECKS="--fleet /fleet" <kayra checkout>`,
55
+ with the kayra checkout at the released tag.
56
+
57
+ Commit (the message names the release and anything the fleet changed),
58
+ push, open the PR the fleet's way, wait for its checks (GitHub:
59
+ `gh pr checks --watch`; Forgejo: `tea api /repos/<owner>/<repo>/commits/<sha>/status`
60
+ until `success`), then merge. Merges ask the operator (kayra's guard); that
61
+ is expected. Remove the worktree and the local branch.
62
+
63
+ ## 3. Deploy, server by server
64
+
65
+ From the fleet's own checkout once it is on the merged `main` (`git pull`,
66
+ then `npm ci`, so `node_modules` is the released version):
67
+
68
+ - `kayra deploy <platform server>`, **not** `kayra setup`: a new version may
69
+ change the app role, which only `apps` runs. `deploy` ends with
70
+ `kayra verify` and fails if the server is broken. Read every `✗`.
71
+ - `kayra setup <edge or nas server>`. A NAS's SSH may be on only on demand:
72
+ if it refuses the connection, ask the user to switch it on. Don't retry in
73
+ a loop.
74
+ - One server at a time, in order. Stop at the first failure, find the cause,
75
+ and fix it in kayra if it is kayra's, before touching the next server.
76
+
77
+ Things that have bitten before:
78
+ - **A new platform hostname needs DNS before the deploy.** Traefik asks
79
+ Let's Encrypt once; a name without DNS keeps Traefik's default certificate
80
+ until Traefik restarts. `verify` reports it as `certificate by TRAEFIK
81
+ DEFAULT CERT`.
82
+ - **A Docker upgrade by the docker role restarts every container.** That's
83
+ normal; check the server's public routes afterwards.
84
+ - **Changed platform config on an upgraded server:** `verify` catches what
85
+ did not reload (a 403 from Authelia meant rules it had never loaded).
86
+
87
+ ## 4. Report
88
+
89
+ Per server: the version it runs now, the deploy's recap (nothing failed?),
90
+ the `kayra verify` result, and anything left for the user (a DNS apply, a
91
+ switch in a vendor UI, a manual cleanup). Then whether the user's checkouts
92
+ are as they left them.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yunuseon/kayra",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "A self-hosted app platform and the CLI that runs it: Traefik, Authelia, a registry, rootless BuildKit and a small deployer per server, set up with OpenTofu and Ansible.",
5
5
  "license": "MIT",
6
6
  "author": "Yunus Öztürk",
@@ -22,6 +22,7 @@
22
22
  "compose/",
23
23
  "terraform/",
24
24
  "scripts/",
25
+ "agent/",
25
26
  "cloud-init.yml.tpl",
26
27
  "hosts.conf",
27
28
  "schema/"