Files
atheos-blog-lxc/docs/writer-branch-workflow.md
hermes 1c7db911df feat: add writer-branch workflow macros
Adds the branch-based publishing model: the editor writes to a long-lived
`atheos-writer` branch, and merging into `master` stays in Gitea.

Six macros installed into Atheos's macro store (a copy is kept in
files/macros.db.php so a rebuild reproduces them), invoked by right-clicking
the `blog` folder:

  1. Start/Join Writer Branch            idempotent create-or-switch + push -u
  2. Commit + Push Writer Branch         identity guard -> add -A -> commit (only
                                         if something is staged) -> push
  3. DESTRUCTIVE: Reset Writer to Master fetch -> reset --hard origin/master ->
                                         push --force-with-lease
  4. Status + Recent Log                 read-only
  5. Refresh Master (ff-only)            pull master down, return to writer
  6. Merge Master Into Writer Branch     when master has moved ahead

Atheos has no terminal and codegit exposes no branch-switching, so macros are
the only way to run arbitrary git from the UI. Verified: codegit's `checkout`
action takes a FILE argument - it is per-file revert, not branch switching.

Identity handling: the commit macros deliberately do NOT hardcode an identity.
php-fpm runs with HOME=/home/atheos (verified), the same global gitconfig the
operator edits over SSH, so hardcoding `git -c user.name=...` would OVERRIDE the
operator's own identity. Instead macro 2 guards on an identity existing and fails
with an actionable message - which is what design decision D1 intends.

Verified by exercising each command through the web server as the php-fpm user:
  - macro 1, 4, 5 return rc=0 with correct output
  - macro 2's identity guard fires with the exact RUNBOOK STEP 1 message
  - macro 3 was tested with real divergence: committed on the writer branch,
    reset --hard to master, force-pushed; origin/atheos-writer then matched
    origin/master exactly (0 ahead, 0 behind)

Security note recorded in the docs and README: this model only holds if branch
protection is enabled on armistace/blog's master. It is currently NOT enabled,
so the deploy key could force-push master directly - the operator needs to turn
it on (repo-owner action).

Also documents two hazards found while testing:
  - macro 2's `git add -A` stages everything, and a subsequent `reset --hard`
    deletes any untracked file that was staged. Keep scratch files outside
    workspace/blog/.
  - recovery procedure for a file lost that way (git fsck --lost-found ->
    ls-tree -> cat-file), which was used to restore a file during testing.
2026-09-29 16:07:25 +10:00

5.6 KiB

Writer-Branch Workflow

How writing and publishing work from the Atheos editor, and why the branch is structured this way.

The model

The editor never touches master. It writes to a long-lived branch (atheos-writer), and merging into master is done by the operator in Gitea.

                     ┌──────────────────────────────────────────┐
   Atheos editor ────┤  atheos-writer   (long-lived)            │
   (macros)          │  • write here                            │
                     │  • commit + push here                    │
                     │  • can be reset --hard back to master    │
                     └───────────────┬──────────────────────────┘
                                     │  operator opens a PR in Gitea
                                     │  operator reviews
                                     ▼
                              master  (protected)
                                     │
                                     ▼
                        existing Pelican build pipeline

Why: the editor is a web app with shell-exec reach (codegit + macro both run commands as the php-fpm user). Bounding what it can reach is the whole point of design decision D6. Keeping master behind a PR means a compromised or fat-fingered editor cannot publish directly — it can only propose.

Setup — do these once

1. Set your git identity (required, RUNBOOK STEP 1)

ssh root@192.168.178.57
su -s /bin/sh atheos
git config --global user.name  "Your Name"
git config --global user.email "you@example.com"

This is the same global gitconfig php-fpm uses (HOME=/home/atheos — verified), so macros commit as you. If it is not set, macro 2 refuses with an actionable error rather than committing under a bogus identity.

2. ⚠️ Protect master on the blog repo

Not yet enabled — the deploy key can currently force-push master directly, which defeats the whole model. Set it in Gitea (repo owner only):

armistace/blog → Settings → Branches → add rule for master:

Setting Value
Enable push off ← direct pushes blocked, including the deploy key
Enable merge whitelist on → add armistace
Require approvals optional (you're the only human here)

Verify:

curl -s -H "Authorization: token $GITEA_TOKEN" \
  https://git.aridgwayweb.com/api/v1/repos/armistace/blog/branch_protections

3. The macros

Installed directly into Atheos's macro store (data/macros.db.php); a copy is kept in this repo at files/macros.db.php so a rebuild reproduces them. They appear when you right-click the blog folder in the file tree → Macros.

# Macro Command Notes
1 Start/Join Writer Branch git checkout atheos-writer || git checkout -b atheos-writer then push -u idempotent — safe to run repeatedly
2 Commit + Push Writer Branch identity guard → git add -A → commit (only if staged changes) → git push origin atheos-writer the everyday button
3 DESTRUCTIVE: Reset Writer Branch to Master git fetch → git reset --hard origin/master → git push --force-with-lease wipes the branch's commits, keeps your files
4 Status + Recent Log git status --short --branch, git log --oneline -8, git branch read-only
5 Refresh Master (ff-only) git checkout master → git pull --ff-only → back to atheos-writer brings master's merges down
6 Merge Master Into Writer Branch git merge origin/master → push for when master has moved ahead

Output is returned into the UI — you see git's actual stdout/stderr, not a silent success.

Day-to-day

  1. Right-click blog → Macros → 1. Start/Join Writer Branch (first time; also safe later)
  2. Write your post, save
  3. Right-click blog → Macros → 2. Commit + Push Writer Branch
  4. Open a PR: atheos-writer → master
  5. Review the diff in Gitea, merge

⚠️ Important: macro 2 uses git add -A

git add -A stages everything in the repo, including files you may not intend to publish (drafts, scratch files, .orig/.bak leftovers). A reset --hard will also delete any untracked file that has been staged.

Practical consequences:

  • atheos-writer is a publishing branch — everything you leave in the tree gets committed and pushed to Gitea.
  • If you want a scratch area, keep it outside the repo (e.g. workspace/scratch/, not workspace/blog/).
  • To unstage before a reset: git reset (clears the index without touching your files).

Recovering a deleted file

reset --hard does not touch the object database, so a file that was ever staged or committed is still recoverable:

su -s /bin/sh atheos
cd /var/www/atheos/workspace/blog
git fsck --lost-found | grep commit        # find dangling commits
git ls-tree <commit> path/to/file          # confirm it is there
git cat-file -p <blob-sha> > path/to/file  # restore it

git reflog --all also shows recently discarded commits. This is how the empty src/content/please_no_facebook.md was recovered after a reset --hard in testing.

Changing the branch name

Edit macros 1, 2, 3, 5 and 6 in Settings → Macro and replace atheos-writer. The branch name is literal in each command — there is no variable. (Macros have no input prompt, so a branch name that changes per task would need edit-then-run.)