Add migration plan for Forgejo brew-to-source transition #140
5 changed files with 327 additions and 0 deletions
1
docs/changelog.d/doc-forgejo-brew-migration-plan.doc.md
Normal file
1
docs/changelog.d/doc-forgejo-brew-migration-plan.doc.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Add migration plan for Forgejo brew-to-source transition
|
||||
|
|
@ -43,3 +43,12 @@ Task-oriented instructions for common BlumeOps operations. These guides assume y
|
|||
| [[restart-indri]] | Safely shut down and restart indri |
|
||||
| [[manage-flyio-proxy]] | Deploy, shutoff, and troubleshoot the public proxy |
|
||||
| [[troubleshooting]] | Diagnose and fix common issues |
|
||||
|
||||
## Plans
|
||||
|
||||
Migration and transition plans for upcoming infrastructure changes.
|
||||
|
||||
| Plan | Description |
|
||||
|------|-------------|
|
||||
| [[plans]] | Index of all plans |
|
||||
| [[migrate-forgejo-from-brew]] | Transition Forgejo from Homebrew to source-built binary |
|
||||
|
|
|
|||
298
docs/how-to/plans/migrate-forgejo-from-brew.md
Normal file
298
docs/how-to/plans/migrate-forgejo-from-brew.md
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
---
|
||||
title: "Plan: Migrate Forgejo from Brew to Source Build"
|
||||
tags:
|
||||
- how-to
|
||||
- plans
|
||||
- forgejo
|
||||
---
|
||||
|
||||
# Plan: Migrate Forgejo from Brew to Source Build
|
||||
|
||||
> **Status:** Planned (not yet executed)
|
||||
|
||||
|
eblume marked this conversation as resolved
|
||||
## Background
|
||||
|
||||
Forgejo was force-upgraded from v13 to v14 by `brew upgrade`, breaking version control. To prevent uncontrolled upgrades and align with the established pattern for other native services (zot, caddy, alloy), we are transitioning Forgejo from Homebrew to a source-built binary managed by a LaunchAgent.
|
||||
|
||||
### Why Source Build?
|
||||
|
||||
- **Version pinning** — upgrade on our schedule by checking out specific tags
|
||||
- **Consistency** — matches [[zot]], [[caddy]], and [[alloy]] deployment patterns
|
||||
- **Control** — build flags, patches, and dependencies are explicit
|
||||
|
eblume marked this conversation as resolved
eblume
commented
I think caddy and alloy have service reference cards to link to here as well. if not, just skip this comment I think caddy and alloy have service reference cards to link to here as well. if not, just skip this comment
|
||||
|
||||
## Source Remote
|
||||
|
||||
Use **Codeberg upstream** as the primary clone source to avoid a circular dependency (Forgejo hosting its own source):
|
||||
|
||||
```
|
||||
https://codeberg.org/forgejo/forgejo.git
|
||||
```
|
||||
|
||||
Add the forge mirror as a secondary remote for convenience and backup:
|
||||
|
||||
```
|
||||
https://forge.ops.eblu.me/eblume/forgejo.git
|
||||
```
|
||||
|
||||
## One-Time Migration Steps
|
||||
|
||||
|
eblume marked this conversation as resolved
eblume
commented
is that true? i thought caddy fixed that actually, can we test this? is that true? i thought caddy fixed that actually, can we test this?
|
||||
These steps are performed manually on indri **before** running Ansible.
|
||||
|
||||
### 1. Clone Forgejo from Codeberg
|
||||
|
||||
```fish
|
||||
ssh indri 'git clone https://codeberg.org/forgejo/forgejo.git ~/code/3rd/forgejo'
|
||||
```
|
||||
|
||||
### 2. Add Forge Mirror as Secondary Remote
|
||||
|
||||
```fish
|
||||
ssh indri 'cd ~/code/3rd/forgejo && git remote add forge https://forge.ops.eblu.me/eblume/forgejo.git'
|
||||
```
|
||||
|
||||
### 3. Check Out the Desired Version Tag
|
||||
|
eblume marked this conversation as resolved
eblume
commented
see above question on whether hairpinning is a valid concern see above question on whether hairpinning is a valid concern
|
||||
|
||||
```fish
|
||||
ssh indri 'cd ~/code/3rd/forgejo && git checkout v14.0.1'
|
||||
```
|
||||
|
||||
### 4. Create a Local Deployment Branch
|
||||
|
||||
Create a local-only `indri-deployment` branch to track the deployed version. Rebase this branch when upgrading to new tags:
|
||||
|
||||
```fish
|
||||
ssh indri 'cd ~/code/3rd/forgejo && git checkout -b indri-deployment'
|
||||
```
|
||||
|
||||
### 5. Set Up Build Dependencies via Mise
|
||||
|
||||
Forgejo requires Go 1.24+ and Node 20+:
|
||||
|
||||
```fish
|
||||
ssh indri 'cd ~/code/3rd/forgejo && mise use go@1.24 node@20'
|
||||
```
|
||||
|
||||
### 6. Build the Binary
|
||||
|
||||
```fish
|
||||
ssh indri 'cd ~/code/3rd/forgejo && TAGS="bindata timedzdata sqlite sqlite_unlock_notify" mise x -- make build'
|
||||
```
|
||||
|
||||
This produces `./forgejo` in the repo root.
|
||||
|
||||
### 7. Stop Brew Forgejo
|
||||
|
||||
```fish
|
||||
ssh indri 'brew services stop forgejo'
|
||||
```
|
||||
|
||||
### 8. Copy Data to New Location
|
||||
|
||||
```fish
|
||||
ssh indri 'sudo cp -a /opt/homebrew/var/forgejo ~/forgejo'
|
||||
```
|
||||
|
||||
### 9. Fix Ownership
|
||||
|
||||
```fish
|
||||
ssh indri 'sudo chown -R erichblume:staff ~/forgejo'
|
||||
```
|
||||
|
||||
### 10. Run Ansible to Deploy New Config + LaunchAgent
|
||||
|
||||
```fish
|
||||
mise run provision-indri -- --tags forgejo
|
||||
```
|
||||
|
||||
### 11. Verify Service Health
|
||||
|
||||
See the verification checklist below.
|
||||
|
||||
### 12. Uninstall Brew Forgejo
|
||||
|
||||
Only after verifying everything works:
|
||||
|
||||
```fish
|
||||
ssh indri 'brew uninstall forgejo'
|
||||
```
|
||||
|
||||
## Ansible Role Changes
|
||||
|
||||
The following changes to `ansible/roles/forgejo/` should be made in the execution session.
|
||||
|
||||
### `defaults/main.yml`
|
||||
|
||||
Update paths and add new variables to match the zot pattern (`ansible/roles/zot/defaults/main.yml`):
|
||||
|
||||
```yaml
|
||||
# Source build paths
|
||||
forgejo_repo_dir: /Users/erichblume/code/3rd/forgejo
|
||||
forgejo_binary: "{{ forgejo_repo_dir }}/forgejo"
|
||||
|
||||
# Data paths (migrated from brew)
|
||||
forgejo_work_path: /Users/erichblume/forgejo
|
||||
forgejo_config_path: "{{ forgejo_work_path }}/custom/conf/app.ini"
|
||||
forgejo_data_path: "{{ forgejo_work_path }}/data"
|
||||
forgejo_log_path: "{{ forgejo_work_path }}/log"
|
||||
forgejo_log_dir: /Users/erichblume/Library/Logs
|
||||
|
||||
# RUN_USER changes from 'forgejo' to 'erichblume' (LaunchAgent user)
|
||||
forgejo_run_user: erichblume
|
||||
```
|
||||
|
||||
### `tasks/main.yml`
|
||||
|
||||
Replace brew install/start with binary-check + LaunchAgent pattern (matching `ansible/roles/zot/tasks/main.yml`):
|
||||
|
||||
```yaml
|
||||
---
|
||||
# Forgejo role — source-built binary with LaunchAgent
|
||||
#
|
||||
# ONE-TIME SETUP (before running ansible):
|
||||
#
|
||||
|
eblume marked this conversation as resolved
eblume
commented
see above notes about hairpinning see above notes about hairpinning
|
||||
# 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.ops.eblu.me/eblume/forgejo.git'
|
||||
#
|
||||
# 3. Set up Go and Node via mise:
|
||||
# ssh indri 'cd ~/code/3rd/forgejo && mise use go@1.24 node@20'
|
||||
#
|
||||
# 4. Build:
|
||||
# ssh indri 'cd ~/code/3rd/forgejo && TAGS="bindata timedzdata sqlite sqlite_unlock_notify" mise x -- make build'
|
||||
#
|
||||
# 5. Run ansible to deploy config and LaunchAgent
|
||||
|
||||
- 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 timedzdata sqlite sqlite_unlock_notify" mise x -- make build'
|
||||
when: not forgejo_binary_stat.stat.exists
|
||||
|
||||
- name: Ensure forgejo config directory exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ forgejo_work_path }}/custom/conf"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Deploy forgejo config
|
||||
ansible.builtin.template:
|
||||
src: app.ini.j2
|
||||
dest: "{{ forgejo_config_path }}"
|
||||
mode: '0600'
|
||||
notify: Restart forgejo
|
||||
|
||||
- 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
|
||||
```
|
||||
|
||||
### `handlers/main.yml`
|
||||
|
||||
Replace `brew services restart` with `launchctl unload/load` (matching `ansible/roles/zot/handlers/main.yml`):
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: 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
|
||||
```
|
||||
|
||||
### New Template: `forgejo.plist.j2`
|
||||
|
||||
LaunchAgent plist (matching `ansible/roles/zot/templates/zot.plist.j2`):
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- {{ ansible_managed }} -->
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>mcquack.eblume.forgejo</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>{{ forgejo_binary }}</string>
|
||||
<string>-w</string>
|
||||
<string>{{ forgejo_work_path }}</string>
|
||||
<string>-c</string>
|
||||
<string>{{ forgejo_config_path }}</string>
|
||||
<string>web</string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>StandardOutPath</key>
|
||||
<string>{{ forgejo_log_dir }}/mcquack.forgejo.out.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>{{ forgejo_log_dir }}/mcquack.forgejo.err.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
### `app.ini.j2`
|
||||
|
||||
No changes needed — paths already flow through variables in `defaults/main.yml`. The only change is that `RUN_USER` will pick up `erichblume` from the updated default.
|
||||
|
||||
## What Stays the Same
|
||||
|
||||
- **1Password secret fetching** — playbook `pre_tasks` are unchanged
|
||||
- **`forgejo_actions_secrets` role** — API-based secret sync is unaffected
|
||||
- **SSH clone URLs** — `BUILTIN_SSH_SERVER_USER` stays `forgejo` (this is the git SSH user, not the OS user)
|
||||
- **Caddy routing** — still proxies to `localhost:3001`
|
||||
- **SQLite database** — copied as-is to new location
|
||||
- **All `app.ini` settings** — template is unchanged, just re-rendered with new paths
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
After running the migration and Ansible:
|
||||
|
||||
- [ ] `ssh indri 'launchctl list mcquack.eblume.forgejo'` — shows running
|
||||
- [ ] `curl https://forge.ops.eblu.me/api/v1/version` — returns JSON with version
|
||||
- [ ] Git clone over SSH: `git clone ssh://forgejo@forge.ops.eblu.me:2222/eblume/blumeops.git /tmp/test-clone`
|
||||
- [ ] Git push works on an existing clone
|
||||
- [ ] Ansible dry-run is clean: `mise run provision-indri -- --tags forgejo --check --diff`
|
||||
- [ ] `mise run services-check` — all green
|
||||
- [ ] Forgejo Actions runners reconnect and jobs succeed
|
||||
|
||||
|
eblume marked this conversation as resolved
eblume
commented
Also add indri user management to this list. We really aught to run all of these services as their own users. I tried doing that once and I think a forgejo user still exists but it went really wonky. macos user session management is hard with launch agents, even when using the root launch agent thing. Also add indri user management to this list. We really aught to run all of these services as their own users. I tried doing that once and I think a forgejo user still exists but it went really wonky. macos user session management is hard with launch agents, even when using the root launch agent thing.
|
||||
## Future Considerations
|
||||
|
||||
- **CI-built binaries** — build on gilbert or in Forgejo Actions, deploy as artifact
|
||||
- **Artifact release system** — tag-triggered binary builds, similar to container releases (`mise run container-release`)
|
||||
- **Automated upgrades** — Renovate or similar watching Codeberg tags, opening PRs with version bumps
|
||||
- **Indri user management** — run each service as its own macOS user for isolation (a `forgejo` user exists but LaunchAgent session management under non-login users is tricky on macOS)
|
||||
|
||||
## Reference Pattern Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `ansible/roles/zot/tasks/main.yml` | Primary pattern for source-built binary tasks |
|
||||
| `ansible/roles/zot/defaults/main.yml` | Variable naming conventions |
|
||||
| `ansible/roles/zot/templates/zot.plist.j2` | LaunchAgent plist template |
|
||||
| `ansible/roles/zot/handlers/main.yml` | Handler pattern (launchctl unload/load) |
|
||||
16
docs/how-to/plans/plans.md
Normal file
16
docs/how-to/plans/plans.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
title: Plans
|
||||
tags:
|
||||
- how-to
|
||||
- plans
|
||||
---
|
||||
|
||||
# Plans
|
||||
|
||||
Migration and transition plans for upcoming infrastructure changes. Each plan is a how-to document that captures the full context, steps, and verification criteria for a future execution session.
|
||||
|
||||
Plans differ from regular how-to guides in that they describe work that has been designed but not yet executed. Once a plan is completed, it may be archived or converted into a standard how-to guide.
|
||||
|
||||
| Plan | Status | Description |
|
||||
|------|--------|-------------|
|
||||
| [[migrate-forgejo-from-brew]] | Planned | Transition Forgejo from Homebrew to source-built binary with LaunchAgent |
|
||||
|
|
@ -20,6 +20,7 @@ The docs follow the [Diataxis](https://diataxis.fr/) framework:
|
|||
| **[[tutorials|Tutorials]]** | Learning-oriented | "I'm new and want to understand" |
|
||||
| **[[reference|Reference]]** | Information-oriented | "I need specific technical details" |
|
||||
| **[[how-to|How-to]]** | Task-oriented | "I need to do X" |
|
||||
| **[[plans|Plans]]** | Future work | "What's planned next?" |
|
||||
| **[[explanation|Explanation]]** | Understanding-oriented | "I want to understand why" |
|
||||
|
||||
## Quick Paths by Audience
|
||||
|
|
@ -28,6 +29,7 @@ The docs follow the [Diataxis](https://diataxis.fr/) framework:
|
|||
|
||||
You probably want quick access to operational details:
|
||||
- [[how-to]] guides for common operations (deploy, troubleshoot, update ACLs)
|
||||
- [[plans]] captures migration and transition plans for future execution
|
||||
- [[reference]] has service URLs, commands, and config locations
|
||||
- [[ai-assistance-guide]] explains how to work effectively with Claude
|
||||
- Run `mise run zk-docs` to prime AI context with key documentation
|
||||
|
|
@ -36,6 +38,7 @@ You probably want quick access to operational details:
|
|||
|
||||
Context for effective assistance:
|
||||
- Read [[ai-assistance-guide]] for operational conventions
|
||||
- [[plans]] has migration plans designed for AI-executed sessions
|
||||
- [[reference]] has the technical specifics you'll need
|
||||
- The repo's `CLAUDE.md` has critical rules (especially the kubectl context requirement)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue
we dont need audience labels on non-tutorials