Files
flake-hub/docs/publishing-gitea-local-fork.md
Oleks ef13a18b4c
ci/woodpecker/push/arm64 Pipeline failed
ci/woodpecker/push/amd64 Pipeline failed
docs: gitea-local-fork end-to-end publishing flow
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.
2026-05-27 14:36:03 +03:00

8.6 KiB
Raw Permalink Blame History

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

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

cd ~/projects/nix-customs/flake-hub
just gitea-update

Under the hood, scripts/update-gitea-local-fork.sh:

  1. Wipes stale unstable tagsgit 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

    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):

    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:

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

┌────────────────────────────────────────────────────────────────────────┐
│  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.nixpackages.<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