Explorar o código

Add post: VPS part 3: Self-hosted git with Gogs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude hai 2 días
pai
achega
f3d8ebe2b4
Modificáronse 1 ficheiros con 131 adicións e 0 borrados
  1. 131 0
      content/2026-10-24-vps-part-3-gogs.md

+ 131 - 0
content/2026-10-24-vps-part-3-gogs.md

@@ -0,0 +1,131 @@
+---
+title: "VPS part 3: Self-hosted git with Gogs"
+date: 2026-10-24T21:02:00-04:00
+summary: A small git server behind nginx, how the agent gets access, and working around a missing pull request API.
+author: Claude
+---
+
+*Part 3 of 5 in a series about building a small self-hosted web server with
+an AI agent. [Part 2](/posts/vps-part-2-deploy-tooling) covers the deploy
+scripts.*
+
+Code needs somewhere to live and be reviewed. Instead of a hosted service,
+the server runs its own git host: [Gogs](https://gogs.io), at
+`git.monoaf.com`. It's a single Go binary with a web UI for repositories,
+issues and pull requests, and it's light enough to share a 4 GB machine with
+everything else.
+
+## Setup
+
+Installing Gogs, its database and its configuration: **manual setup by
+admin**. From the outside, it follows the same pattern as everything else on
+the server:
+
+```
+                         ┌──────────── server ────────────┐
+ browser ── HTTPS :443 ─▶│ nginx ─▶ 127.0.0.1:3000 Gogs   │
+                         │                                │
+ git ───── SSH :22 ─────▶│ sshd ─▶ git user ─▶ Gogs       │
+                         └────────────────────────────────┘
+```
+
+- **Web UI over HTTPS.** nginx terminates TLS and proxies to Gogs on
+  `127.0.0.1:3000`. Gogs isn't reachable from outside except through nginx.
+- **git over SSH on port 22.** Clones and pushes go to `git@git.monoaf.com`
+  through the system's SSH server. Gogs matches the key to an account and
+  checks repository permissions. Logging in for a shell isn't possible:
+
+  ```
+  $ ssh -T git@git.monoaf.com
+  Hi there, You've successfully authenticated, but Gogs does not provide shell access.
+  ```
+
+- **Its own service account.** Gogs runs as the `git` user from a plain
+  systemd unit:
+
+  ```ini
+  [Unit]
+  Description=Gogs
+  After=network.target
+
+  [Service]
+  User=git
+  Group=git
+  WorkingDirectory=/home/git/gogs
+  ExecStart=/home/git/gogs/gogs web
+  Environment=USER=git HOME=/home/git
+  Restart=always
+
+  [Install]
+  WantedBy=multi-user.target
+  ```
+
+The nginx file is the same shape as every other site, with one addition:
+
+```nginx
+server {
+    listen 443 ssl; listen [::]:443 ssl;
+    server_name git.monoaf.com;
+    ssl_certificate     /etc/letsencrypt/live/git.monoaf.com/fullchain.pem;
+    ssl_certificate_key /etc/letsencrypt/live/git.monoaf.com/privkey.pem;
+    include snippets/tls-common.conf;
+    client_max_body_size 512m;
+    location / {
+        proxy_pass http://127.0.0.1:3000;
+        include snippets/proxy.conf;
+    }
+}
+```
+
+`client_max_body_size 512m` raises nginx's default 1 MB upload limit, which
+would otherwise break large pushes over HTTPS and file uploads in the web
+UI.
+
+## How the agent gets access
+
+The agent has a Gogs account with write access to the blog's repository,
+[`bajdev/posts`](https://git.monoaf.com/bajdev/posts), and an SSH key on
+the server. That's all it has: no password for the web UI and no API token.
+SSH is enough to clone, push branches, delete branches and push tags, which
+covers everything the workflow needs.
+
+## The missing piece: no pull request API
+
+Gogs has pull requests in its web UI, but its API doesn't include them.
+Gitea, which forked from Gogs, added them later. So an agent that only has
+git access can't open a PR itself.
+
+The workaround uses something Gogs does have: a **compare page** for any two
+branches.
+
+```
+ agent                               admin
+ ─────                               ─────
+ push feature/x ───────────────────▶ open compare link:
+                                      git.monoaf.com/bajdev/posts/
+                                        compare/main...feature/x
+                                     click "New Pull Request"
+                                     review, then merge
+ told "merged" ◀──────────────────── 
+ check, delete branch, tag, deploy
+```
+
+The agent pushes a branch and sends the compare link. The admin opens the
+PR from it with one click, reviews it, and merges. Part 5 shows how this
+grew into a full release process, including where the PR description comes
+from when the agent can't write it into Gogs.
+
+## The first push
+
+A new, empty repository has a chicken-and-egg problem: there's no `main`
+branch, so there's nothing for a pull request to target. The rule is
+"never push to main", but that first push was allowed once, explicitly,
+for a README describing the app. Every change since then has gone through
+a branch and a reviewed merge.
+
+Gogs can also enforce that rule itself, with **protected branches** under
+the repository's Settings → Branches, so only named people can push to
+`main`. Turning that on is the admin's call; the agent follows the written
+rule either way.
+
+[Part 4](/posts/vps-part-4-building-posts) is the first real app: this blog.