docs: gitea-local-fork end-to-end publishing flow
ci/woodpecker/push/arm64 Pipeline failed
ci/woodpecker/push/amd64 Pipeline failed

Rescued from the second flake-hub checkout (~/projects/flake-hub) before
that working copy was removed. Documents the path from local Gitea fork
to the attic binary cache.
This commit is contained in:
Oleks
2026-05-27 14:33:39 +03:00
parent 26bdad6d1b
commit ef13a18b4c
+174
View File
@@ -0,0 +1,174 @@
# Publishing `gitea-local-fork`
End-to-end flow for taking a change in Oleks's local gitea fork
(`/home/oleks/projects/gitea`, branch `oleks/main`) all the way to a
binary in the `attic-infra-cache-k3s-1` cache, ready for any consumer
to fetch instead of recompiling Go 1.26.3 locally.
## Step-by-step
### 1. Develop in the gitea fork
```bash
cd ~/projects/gitea
# work on oleks/main (the fork integration tip)
git commit -m "..."
git push oleks oleks/main:main
```
Lands on `git.oleks.space/oleks/gitea` at `refs/heads/main`.
This step alone does **not** trigger any CI on flake-hub — the binding
contract is the flake-hub pin commit in step 2.
### 2. Bump the pin in flake-hub
```bash
cd ~/projects/nix-customs/flake-hub
just gitea-update
```
Under the hood, `scripts/update-gitea-local-fork.sh`:
1. **Wipes stale unstable tags**`git push oleks :refs/tags/v*-unstable-*`
so `nix-update` sees only clean semver tags (`v1.26.0` etc.) when
constructing the new version.
2. **Bumps the pin**
```bash
nix run nixpkgs#nix-update -- \
--flake gitea-local-fork \
--version=branch=main \
--build
```
Updates `rev`, `vendorHash`, `pnpmDeps` hash, and `version` (formatted
as `<latest-semver-tag>-unstable-<commit-date>`).
3. **Tags the gitea fork** at the new rev with `v<version>` and pushes
the tag. **This is the load-bearing step:** the tag makes the rev
reachable forever, surviving any future rebase / force-push of
`oleks/main`.
4. **Commits + pushes flake-hub** — the bumped `packages/gitea-local-fork.nix`
lands on `git.oleks.space/oleks/flake-hub` `main`.
### 3. Woodpecker picks up the flake-hub push
Pipeline auto-triggers from `.woodpecker/amd64.yaml` and `.woodpecker/arm64.yaml`
running in parallel. Each workflow:
1. Clones `oleks/flake-hub` using the `gitea_clone_token` secret.
2. Runs `ci/setup.sh` — configures `/etc/hosts` to pin `armer`
directly (bypassing Cloudflare upload-size limits via hairpin NAT)
and writes the trusted substituters / public keys to `/etc/nix/nix.conf`.
3. Runs `ci/build.py <arch>` — for each package (including
`gitea-local-fork` on `x86_64-linux` and `aarch64-linux`):
```bash
nix build .#packages.<arch>.gitea-local-fork --print-build-logs ...
attic push attic-infra-cache-k3s-1 <closure>
```
Authenticates the push with `ATTIC_TOKEN` against
`https://nix-cache-upload.oleks.space`.
### 4. Closure lives in attic
After both arch workflows succeed, the closure is queryable from:
- **Cache URL:** `https://nix-cache-custom.oleks.space/attic-infra-cache-k3s-1`
- **Public key:** `attic-infra-cache-k3s-1:qYSNK3DmttQXCFqn1t50qoWGtQNPRFWq9mgQjD05DeU=`
### 5. Consume from cache
Anyone (or any host) can pull the binary instead of building:
<!-- markdownlint-disable MD013 -->
```bash
just gitea-run
# or, equivalently:
nix run \
--extra-substituters "https://nix-cache-custom.oleks.space/attic-infra-cache-k3s-1" \
--extra-trusted-public-keys "attic-infra-cache-k3s-1:qYSNK3DmttQXCFqn1t50qoWGtQNPRFWq9mgQjD05DeU=" \
git+https://git.oleks.space/oleks/flake-hub#gitea-local-fork
```
<!-- markdownlint-enable MD013 -->
## Diagram
```text
┌────────────────────────────────────────────────────────────────────────┐
│ 1. ~/projects/gitea (branch oleks/main) │
│ ─ git push oleks oleks/main:main ─► oleks/gitea refs/heads/main │
└────────────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────────────┐
│ 2. ~/projects/nix-customs/flake-hub │
│ ─ just gitea-update │
│ a. wipe v*-unstable-* tags on fork │
│ b. nix-update → bump rev/hashes/version │
│ c. tag fork at new rev: v<version> ─► push │
│ d. commit + push flake-hub origin/main │
└────────────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────────────┐
│ 3. ci.oleks.space pipeline auto-fires │
│ amd64 + arm64 (parallel): │
│ clone → ci/setup.sh → ci/build.py <arch> │
│ ─ nix build .#packages.<arch>.gitea-local-fork │
│ ─ attic push attic-infra-cache-k3s-1 <closure> │
└────────────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────────────┐
│ 4. attic-infra-cache-k3s-1 │
│ URL: https://nix-cache-custom.oleks.space/attic-infra-cache-k3s-1 │
└────────────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────────────┐
│ 5. Consumer: just gitea-run / nix run …#gitea-local-fork │
│ ─ closure pulled from cache, no recompile │
└────────────────────────────────────────────────────────────────────────┘
```
## Secrets
Both are Woodpecker repo secrets on `oleks/flake-hub` (never in code):
- `gitea_clone_token` — clone flake-hub source; netrc for `fetchgit` of
the fork.
- `attic_token` — authenticate `attic push` against
`nix-cache-upload.oleks.space`.
## Why the dance with tags in step 2c
The fork's `oleks/main` branch is rebaseable. `fetchgit` in the
derivation resolves by `rev`, so the rev must remain reachable from
*some* ref on the remote. Tags are immutable. The
`v<version>` tag created at update time becomes the load-bearing
reference: even if `oleks/main` later gets force-pushed and the rev
falls off the branch, the tag keeps it alive (and Gitea won't GC it).
## Failure points to watch
- **Step 2c — tag push rejected.** Check `oleks` remote auth in your
shell; the pre-push hook also runs lint.
- **Step 3 build — first cold build ≈1015 min/arch, later ≈12 min.**
Normal; Go 1.26.3 compiles from source.
- **Step 3 attic — push hangs or 413.** `ci/setup.sh` pins armer in
`/etc/hosts` to bypass the Cloudflare 100 MB upload cap; confirm the
pin survived.
- **Step 5 consume — cache miss, recompile locally.** Check the closure
landed (`attic info` or browse the cache URL) and that the public key
matches.
## Reference
- Derivation: `packages/gitea-local-fork.nix`
- Flake attr: `flake.nix` → `packages.<system>.gitea-local-fork`
- Update script: `scripts/update-gitea-local-fork.sh` *(untracked, local-only)*
- Pipelines: `.woodpecker/amd64.yaml`, `.woodpecker/arm64.yaml`
- Build driver: `ci/build.py`
- Justfile recipes: `just gitea-build`, `just gitea-update`, `just gitea-run`