# 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 `-unstable-`). 3. **Tags the gitea fork** at the new rev with `v` 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 ` — for each package (including `gitea-local-fork` on `x86_64-linux` and `aarch64-linux`): ```bash nix build .#packages..gitea-local-fork --print-build-logs ... attic push attic-infra-cache-k3s-1 ``` 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: ```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 ``` ## 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 ─► 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 │ │ ─ nix build .#packages..gitea-local-fork │ │ ─ attic push attic-infra-cache-k3s-1 │ └────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────────────────────────────────┐ │ 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` 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 ≈10–15 min/arch, later ≈1–2 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..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`