## Summary - Remove aliases from all zk cards to prevent them from capturing wiki-links - Convert all wiki-links from `[[filename|Title]]` to `[[Title]]` format - Replace `doc-filenames` task with `doc-titles` for duplicate title detection - Update pre-commit hook to use `doc-titles` Wiki-links now resolve to reference docs by their frontmatter title, which is more readable and maintainable than filename-based links. ## Deployment and Testing - [x] Pre-commit hooks pass (including new `doc-titles` check) - [x] Manually verified zk cards have aliases removed - [ ] Deploy docs v1.0.7 and verify wiki-links resolve correctly - [ ] Test links to reference docs (e.g., [[Grafana Alloy]], [[ArgoCD]]) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/91
5.1 KiB
| id | tags | |||||
|---|---|---|---|---|---|---|
| 1768246525-RVRY |
|
Mon Jan 12 11:35
❯ brew install forgejo
❯ brew --prefix forgejo
/opt/homebrew/opt/forgejo
❯ brew services start forgejo
==> Successfully started `forgejo` (label: homebrew.mxcl.forgejo)
From the service definition I can see that this runs as:
/opt/homebrew/opt/forgejo/bin/forgejo web --work-path /opt/homebrew/var/forgejo > /opt/homebrew/var/log/forgejo.log 2> /opt/homebrew/var/log/forgejo.log
It sounds from the docs like this means the config file should live at:
/opt/homebrew/var/forgejo/custom/conf/app.ini
Ah, based on the logs, it looks like forgejo has picked port 3000 which is used by grafana:
❯ lsof -nP -iTCP:3000 -sTCP:LISTEN
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
grafana 1530 erichblume 15u IPv6 0x4acfad8b21dcb063 0t0 TCP *:3000 (LISTEN)
Ok I've set a basic config for port 3001, and then gone through the basic app setup. Looks like it's working! Not sure how SSH works yet though. Let's get this service registered.
Ok so the next issue is that I want to use ssh as my primary git interface, and I want that to look to users like I'm using port 22 but I want to host it on indri which has its own separate ssh setup. Hmm. Let's tell forgejo to use port 2200. Ah perfect, we can set SSH_PORT to 22 and SSH_LISTEN_PORT to 2200.
Hmm, let's stop running this as me and run as a new user, 'forgejo'.
sudo sysadminctl -addUser forgejo -system -shell /usr/bin/false
sudo chown -R forgejo:staff /opt/homebrew/var/forgejo
Ok, I think I need to switch all my services on this host over to a services file.
Wow, missing from the above is like 4 hours of deep diving in to the particulars of tailscale service definition hosting. In the end, I never got a services file to work - and yes, I did remember to advertise! Adding to the complexity is that I didn't discover until the end that you can't do "hairpinning", ie you CANNOT use the tailnet service name from the host doing that hosting. I probably had it fixed at some point hours ago and ruled it out because I didn't know about the hairpinning issue. So anyway... what ended up working was to just use the cli:
tailscale serve --service="svc:forge" --tcp=22 tcp://localhost:2200
tailscale serve --service="svc:forge" --https=443 http://localhost:3001
That's it. Nothing else needed, worked right away. Sheesh. (Ok there was also a
solid hour spent on permission issues... I honestly don't know how it's working
now, as there is now a forgejo user and the config says to use it but the
files are all owned by erichblume:staff but with group permissions set... in
any case, it friggin' works. So I'm happy.
Configuration (Ansible-Managed)
As of 2026-01-23, the app.ini is managed by ansible:
- Template:
ansible/roles/forgejo/templates/app.ini.j2 - Secrets fetched from 1Password in playbook pre_tasks
- Secrets item: "Forgejo Secrets" in blumeops vault (fields:
lfs-jwt-secret,internal-token,oauth2-jwt-secret,runner_reg)
Deploy config changes:
mise run provision-indri -- --tags forgejo
Forgejo Actions (CI/CD)
Runner (k8s)
The Forgejo runner runs in Kubernetes with Docker-in-Docker (DinD) for container builds.
Architecture:
- Runner daemon + DinD sidecar in a single pod
- Jobs execute in containers using the
k8slabel - DinD exposes Docker API on
tcp://127.0.0.1:2375 - Pods reach
*.ops.eblu.meservices via Caddy reverse proxy
Components:
- ArgoCD app:
argocd/apps/forgejo-runner.yaml - Manifests:
argocd/manifests/forgejo-runner/ - Job image:
registry.ops.eblu.me/blumeops/forgejo-runner(Node.js + Docker CLI) - Job image source:
containers/forgejo-runner/
Deployment:
# Apply secret (contains runner token from 1Password)
op inject -i argocd/manifests/forgejo-runner/secret.yaml.tpl | kubectl --context=minikube-indri apply -f -
# Sync via ArgoCD
argocd app sync forgejo-runner
View logs:
kubectl --context=minikube-indri logs -n forgejo-runner -l app=forgejo-runner -c runner
Container Build Workflow
Container images are built via .forgejo/workflows/build-container.yaml, triggered by tags matching <container>-v<version>.
Release a container:
mise run container-list # See available containers
mise run container-tag-and-release nettest v1.0.0 # Tag and trigger build
Test container (containers/nettest/): Network connectivity test for debugging CI/CD.
Workflows
Workflows live in .forgejo/workflows/ (not .github/workflows/).
Important: Use github.* context variables, not gitea.*. Forgejo supports both at runtime, but:
- The Forgejo web UI schema validator only recognizes
github.* actionlintpre-commit hook validates workflows locally (catches errors before push)- Pass untrusted inputs (like
github.head_ref) through env vars for security
Runner Token
Stored in 1Password "Forgejo Secrets" item, field runner_reg.
To create a new token:
- Go to https://forge.ops.eblu.me/admin/actions/runners
- Click "Create new Runner"
- Copy the token and update 1Password