From 4489fe4492900803a94558a913f492acb1d5bd94 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sat, 28 Mar 2026 07:52:17 -0700 Subject: [PATCH 1/3] Update docs for Forgejo source build migration Update Forgejo reference card with source-build details (binary path, data location, LaunchAgent, build instructions). Update restart-indri to replace brew services commands with launchctl for Forgejo. Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/how-to/operations/restart-indri.md | 6 ++--- docs/reference/services/forgejo.md | 34 ++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/docs/how-to/operations/restart-indri.md b/docs/how-to/operations/restart-indri.md index 768ec9a..a3f29a0 100644 --- a/docs/how-to/operations/restart-indri.md +++ b/docs/how-to/operations/restart-indri.md @@ -37,10 +37,8 @@ ssh indri 'minikube status' Native services managed by launchd will stop automatically during macOS shutdown. However, if you want to stop them explicitly first: ```bash -# Forgejo (managed by brew services) -ssh indri 'brew services stop forgejo' - # LaunchAgent services +ssh indri 'launchctl unload ~/Library/LaunchAgents/mcquack.eblume.forgejo.plist' ssh indri 'launchctl unload ~/Library/LaunchAgents/mcquack.eblume.caddy.plist' ssh indri 'launchctl unload ~/Library/LaunchAgents/mcquack.eblume.zot.plist' ssh indri 'launchctl unload ~/Library/LaunchAgents/mcquack.jellyfin.plist' @@ -68,7 +66,7 @@ Or if you're at the console, use the Apple menu. After indri boots, most services recover automatically. Only a few things need manual attention. -**What autostarts:** Docker Desktop, brew services (Forgejo), and all mcquack LaunchAgent services (Caddy, Zot, Jellyfin, Alloy, Borgmatic, metrics collectors). +**What autostarts:** Docker Desktop and all mcquack LaunchAgent services (Forgejo, Caddy, Zot, Jellyfin, Alloy, Borgmatic, metrics collectors). **What needs manual action:** Amphetamine, AutoMounter, and minikube (including its Tailscale serve port). diff --git a/docs/reference/services/forgejo.md b/docs/reference/services/forgejo.md index e96c247..dcd0878 100644 --- a/docs/reference/services/forgejo.md +++ b/docs/reference/services/forgejo.md @@ -1,6 +1,6 @@ --- title: Forgejo -modified: 2026-03-03 +modified: 2026-03-28 tags: - service - git @@ -11,6 +11,8 @@ tags: Git forge and CI/CD platform. **Primary source of truth for blumeops** (mirrored to GitHub). +Built from source on indri, managed via Ansible + mcquack LaunchAgent. Source cloned from Codeberg with a forge mirror as secondary remote. + ## Quick Reference | Property | Value | @@ -20,6 +22,36 @@ Git forge and CI/CD platform. **Primary source of truth for blumeops** (mirrored | **SSH** | `ssh://forgejo@forge.ops.eblu.me:2222` | | **Local Ports** | 3001 (HTTP), 2200 (SSH) | | **Config** | `ansible/roles/forgejo/templates/app.ini.j2` | +| **Binary** | `~/code/3rd/forgejo/forgejo` (source-built) | +| **Data** | `~/forgejo` | +| **LaunchAgent** | `mcquack.eblume.forgejo` | +| **Source** | `~/code/3rd/forgejo` (cloned from Codeberg) | + +## Building from Source + +Forgejo is built from source on indri, matching the pattern used by [[zot]], [[caddy]], and [[alloy]]. + +**One-time setup:** + +```fish +# Clone from Codeberg (avoids circular dependency with forge) +ssh indri 'git clone https://codeberg.org/forgejo/forgejo.git ~/code/3rd/forgejo' + +# Add forge mirror as secondary remote +ssh indri 'cd ~/code/3rd/forgejo && git remote add forge https://forge.eblu.me/mirrors/forgejo.git' +``` + +**Building a specific version:** + +```fish +ssh indri 'cd ~/code/3rd/forgejo && git fetch --tags && git checkout v14.0.3' +ssh indri 'cd ~/code/3rd/forgejo && mise use go@1.25 node@24' +ssh indri 'cd ~/code/3rd/forgejo && TAGS="bindata timetzdata sqlite sqlite_unlock_notify" mise x -- make build && mise x -- make forgejo' +``` + +Build tags: `bindata` (embed assets), `timetzdata` (embed timezone data), `sqlite sqlite_unlock_notify` (SQLite support). The `make build` target produces `./gitea`; `make forgejo` creates a `./forgejo` hardlink. + +After building, run `mise run provision-indri -- --tags forgejo` to deploy the config and restart the service. ## Repositories From 521cec5fde05e21df063f264de52d4cf8a5e6e3e Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sat, 28 Mar 2026 07:53:49 -0700 Subject: [PATCH 2/3] Migrate Forgejo Ansible role from Homebrew to source build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace brew install/services with source-built binary + mcquack LaunchAgent, matching the zot/caddy/alloy pattern. Key changes: - defaults: new paths (~/forgejo, ~/code/3rd/forgejo), run_user → erichblume - tasks: binary stat check instead of brew install, LaunchAgent deployment - handlers: launchctl unload/load instead of brew services restart - new forgejo.plist.j2 LaunchAgent template Also stamps frigate-notify, cloudnative-pg, blumeops-pg as reviewed (all up to date) and updates forgejo tracking to v14.0.3. Co-Authored-By: Claude Opus 4.6 (1M context) --- ansible/roles/forgejo/defaults/main.yml | 11 +++- ansible/roles/forgejo/handlers/main.yml | 4 +- ansible/roles/forgejo/tasks/main.yml | 56 +++++++++++++++---- .../roles/forgejo/templates/forgejo.plist.j2 | 26 +++++++++ .../build-forgejo-from-source.infra.md | 1 + service-versions.yaml | 12 ++-- 6 files changed, 89 insertions(+), 21 deletions(-) create mode 100644 ansible/roles/forgejo/templates/forgejo.plist.j2 create mode 100644 docs/changelog.d/build-forgejo-from-source.infra.md diff --git a/ansible/roles/forgejo/defaults/main.yml b/ansible/roles/forgejo/defaults/main.yml index 435caf1..a178d99 100644 --- a/ansible/roles/forgejo/defaults/main.yml +++ b/ansible/roles/forgejo/defaults/main.yml @@ -4,16 +4,21 @@ forgejo_app_name: Forgejo forgejo_app_slogan: "Beyond coding. We Forge." -forgejo_run_user: forgejo +forgejo_run_user: erichblume forgejo_run_mode: prod -# Paths (brew-managed for now, will change to mcquack per migrate-forgejo-from-brew) -forgejo_work_path: /opt/homebrew/var/forgejo +# Source build paths +forgejo_repo_dir: /Users/erichblume/code/3rd/forgejo +forgejo_binary: "{{ forgejo_repo_dir }}/forgejo" + +# Data paths (migrated from brew to ~/forgejo) +forgejo_work_path: /Users/erichblume/forgejo forgejo_config_path: "{{ forgejo_work_path }}/custom/conf/app.ini" forgejo_data_path: "{{ forgejo_work_path }}/data" forgejo_repo_root: "{{ forgejo_data_path }}/forgejo-repositories" forgejo_lfs_path: "{{ forgejo_data_path }}/lfs" forgejo_log_path: "{{ forgejo_work_path }}/log" +forgejo_log_dir: /Users/erichblume/Library/Logs # Server settings forgejo_http_addr: 0.0.0.0 diff --git a/ansible/roles/forgejo/handlers/main.yml b/ansible/roles/forgejo/handlers/main.yml index dc67cbc..c00a3dd 100644 --- a/ansible/roles/forgejo/handlers/main.yml +++ b/ansible/roles/forgejo/handlers/main.yml @@ -1,4 +1,6 @@ --- - name: Restart forgejo - ansible.builtin.command: brew services restart forgejo + ansible.builtin.shell: | + launchctl unload ~/Library/LaunchAgents/mcquack.eblume.forgejo.plist 2>/dev/null || true + launchctl load ~/Library/LaunchAgents/mcquack.eblume.forgejo.plist changed_when: true diff --git a/ansible/roles/forgejo/tasks/main.yml b/ansible/roles/forgejo/tasks/main.yml index a6d27b9..1877731 100644 --- a/ansible/roles/forgejo/tasks/main.yml +++ b/ansible/roles/forgejo/tasks/main.yml @@ -1,16 +1,37 @@ --- -# Forgejo role +# Forgejo role — source-built binary with LaunchAgent # -# Currently uses brew-managed forgejo. Phase 3 of ci-cd-bootstrap will -# transition to mcquack LaunchAgent with CI-built binary. +# ONE-TIME SETUP (before running ansible): +# +# 1. Clone forgejo from codeberg (avoid circular dependency): +# ssh indri 'git clone https://codeberg.org/forgejo/forgejo.git ~/code/3rd/forgejo' +# +# 2. Add forge mirror as secondary remote: +# ssh indri 'cd ~/code/3rd/forgejo && git remote add forge https://forge.eblu.me/mirrors/forgejo.git' +# +# 3. Set up Go and Node via mise: +# ssh indri 'cd ~/code/3rd/forgejo && mise use go@1.25 node@24' +# +# 4. Build: +# ssh indri 'cd ~/code/3rd/forgejo && TAGS="bindata timetzdata sqlite sqlite_unlock_notify" mise x -- make build && mise x -- make forgejo' +# +# 5. Run ansible to deploy config and LaunchAgent # # Secrets (lfs_jwt_secret, internal_token, oauth2_jwt_secret) are fetched # from 1Password in the playbook pre_tasks. -- name: Install forgejo via homebrew - community.general.homebrew: - name: forgejo - state: present +- name: Verify forgejo binary exists + ansible.builtin.stat: + path: "{{ forgejo_binary }}" + register: forgejo_binary_stat + +- name: Fail if forgejo binary not found + ansible.builtin.fail: + msg: | + Forgejo binary not found at {{ forgejo_binary }}. + Please build from source first: + ssh indri 'cd ~/code/3rd/forgejo && TAGS="bindata timetzdata sqlite sqlite_unlock_notify" mise x -- make build && mise x -- make forgejo' + when: not forgejo_binary_stat.stat.exists - name: Ensure forgejo config directory exists ansible.builtin.file: @@ -25,8 +46,21 @@ mode: '0600' notify: Restart forgejo -- name: Ensure forgejo service is started - ansible.builtin.command: brew services start forgejo - register: forgejo_brew_start - changed_when: "'Successfully started' in forgejo_brew_start.stdout" +- name: Deploy forgejo LaunchAgent plist + ansible.builtin.template: + src: forgejo.plist.j2 + dest: ~/Library/LaunchAgents/mcquack.eblume.forgejo.plist + mode: '0644' + notify: Restart forgejo + +- name: Check if forgejo LaunchAgent is loaded + ansible.builtin.command: launchctl list mcquack.eblume.forgejo + register: forgejo_launchctl_check + changed_when: false + failed_when: false + +- name: Load forgejo LaunchAgent if not loaded + ansible.builtin.command: launchctl load ~/Library/LaunchAgents/mcquack.eblume.forgejo.plist + when: forgejo_launchctl_check.rc != 0 + changed_when: true failed_when: false diff --git a/ansible/roles/forgejo/templates/forgejo.plist.j2 b/ansible/roles/forgejo/templates/forgejo.plist.j2 new file mode 100644 index 0000000..85d63f3 --- /dev/null +++ b/ansible/roles/forgejo/templates/forgejo.plist.j2 @@ -0,0 +1,26 @@ + + + + + + Label + mcquack.eblume.forgejo + ProgramArguments + + {{ forgejo_binary }} + -w + {{ forgejo_work_path }} + -c + {{ forgejo_config_path }} + web + + RunAtLoad + + KeepAlive + + StandardOutPath + {{ forgejo_log_dir }}/mcquack.forgejo.out.log + StandardErrorPath + {{ forgejo_log_dir }}/mcquack.forgejo.err.log + + diff --git a/docs/changelog.d/build-forgejo-from-source.infra.md b/docs/changelog.d/build-forgejo-from-source.infra.md new file mode 100644 index 0000000..bffd5c7 --- /dev/null +++ b/docs/changelog.d/build-forgejo-from-source.infra.md @@ -0,0 +1 @@ +Migrate Forgejo from Homebrew to source build with mcquack LaunchAgent, matching the pattern used by zot, caddy, and alloy. Upgrades to v14.0.3 (7 security fixes including PKCE bypass and OAuth scope bypass). diff --git a/service-versions.yaml b/service-versions.yaml index 6821488..bca2528 100644 --- a/service-versions.yaml +++ b/service-versions.yaml @@ -59,7 +59,7 @@ services: - name: frigate-notify type: argocd - last-reviewed: 2026-02-22 + last-reviewed: 2026-03-28 current-version: "v0.5.4" upstream-source: https://github.com/0x2142/frigate-notify/releases @@ -112,7 +112,7 @@ services: - name: cloudnative-pg type: argocd - last-reviewed: 2026-02-24 + last-reviewed: 2026-03-28 current-version: "v1.28.1" upstream-source: https://github.com/cloudnative-pg/cloudnative-pg/releases notes: Deployed via Helm chart (chart v0.27.1 from forge mirror) @@ -147,7 +147,7 @@ services: - name: blumeops-pg type: argocd - last-reviewed: 2026-02-27 + last-reviewed: 2026-03-28 current-version: "18.3" upstream-source: https://github.com/cloudnative-pg/cloudnative-pg/releases notes: CloudNativePG Cluster resource; pinned to PG minor version @@ -287,10 +287,10 @@ services: - name: forgejo type: ansible - last-reviewed: 2026-02-22 - current-version: "14.0.2" + last-reviewed: 2026-03-28 + current-version: "14.0.3" upstream-source: https://codeberg.org/forgejo/forgejo/releases - notes: Installed via Homebrew on indri; plan to migrate to source build + notes: Built from source on indri (~/code/3rd/forgejo) - name: alloy type: ansible From de8a08ca2d6598e25e1cd91e48360aa1a4621b9a Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sat, 28 Mar 2026 08:16:19 -0700 Subject: [PATCH 3/3] Fix build instructions: never use make forgejo directly make forgejo rebuilds with empty TAGS, stripping SQLite support. Updated docs and role to use mise run build instead, which wraps make build with the correct tags and creates the hardlink. Co-Authored-By: Claude Opus 4.6 (1M context) --- ansible/roles/forgejo/tasks/main.yml | 11 ++++------- docs/reference/services/forgejo.md | 9 ++++++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ansible/roles/forgejo/tasks/main.yml b/ansible/roles/forgejo/tasks/main.yml index 1877731..7cafb12 100644 --- a/ansible/roles/forgejo/tasks/main.yml +++ b/ansible/roles/forgejo/tasks/main.yml @@ -9,13 +9,10 @@ # 2. Add forge mirror as secondary remote: # ssh indri 'cd ~/code/3rd/forgejo && git remote add forge https://forge.eblu.me/mirrors/forgejo.git' # -# 3. Set up Go and Node via mise: -# ssh indri 'cd ~/code/3rd/forgejo && mise use go@1.25 node@24' +# 3. Build (mise.toml handles Go/Node versions and build tags): +# ssh indri 'cd ~/code/3rd/forgejo && mise run build' # -# 4. Build: -# ssh indri 'cd ~/code/3rd/forgejo && TAGS="bindata timetzdata sqlite sqlite_unlock_notify" mise x -- make build && mise x -- make forgejo' -# -# 5. Run ansible to deploy config and LaunchAgent +# 4. Run ansible to deploy config and LaunchAgent # # Secrets (lfs_jwt_secret, internal_token, oauth2_jwt_secret) are fetched # from 1Password in the playbook pre_tasks. @@ -30,7 +27,7 @@ msg: | Forgejo binary not found at {{ forgejo_binary }}. Please build from source first: - ssh indri 'cd ~/code/3rd/forgejo && TAGS="bindata timetzdata sqlite sqlite_unlock_notify" mise x -- make build && mise x -- make forgejo' + ssh indri 'cd ~/code/3rd/forgejo && mise run build' when: not forgejo_binary_stat.stat.exists - name: Ensure forgejo config directory exists diff --git a/docs/reference/services/forgejo.md b/docs/reference/services/forgejo.md index dcd0878..fbbc506 100644 --- a/docs/reference/services/forgejo.md +++ b/docs/reference/services/forgejo.md @@ -45,11 +45,14 @@ ssh indri 'cd ~/code/3rd/forgejo && git remote add forge https://forge.eblu.me/m ```fish ssh indri 'cd ~/code/3rd/forgejo && git fetch --tags && git checkout v14.0.3' -ssh indri 'cd ~/code/3rd/forgejo && mise use go@1.25 node@24' -ssh indri 'cd ~/code/3rd/forgejo && TAGS="bindata timetzdata sqlite sqlite_unlock_notify" mise x -- make build && mise x -- make forgejo' +ssh indri 'cd ~/code/3rd/forgejo && mise run build' ``` -Build tags: `bindata` (embed assets), `timetzdata` (embed timezone data), `sqlite sqlite_unlock_notify` (SQLite support). The `make build` target produces `./gitea`; `make forgejo` creates a `./forgejo` hardlink. +The `build` mise task (defined in the repo's `mise.toml`) runs `make build` with the correct tags and creates the `./forgejo` hardlink. It uses `go@1.25.8` and `node@24` as configured by `mise use`. + +**WARNING:** Do NOT use `make forgejo` directly — it rebuilds with empty TAGS, stripping SQLite support. Always use `mise run build` or pass TAGS explicitly to `make build` and `ln -f gitea forgejo` afterwards. + +Build tags: `bindata` (embed assets), `timetzdata` (embed timezone data), `sqlite sqlite_unlock_notify` (SQLite support). After building, run `mise run provision-indri -- --tags forgejo` to deploy the config and restart the service.