/bz-airgap

Primary LanguageStarlark

Example of an air-gapped (no internet) self-hosting go build.

  1. Assume that bazel (or bazelisk) is available on the path as bazel.

    See .bazelrc for command line flags. Particularly --distdir and --repository_cache.

    Also .bazelignore.

  2. Optional: clean up so we can be reasonably sure we're reproducing a clean build scenario:

    rm -rf bazel.repo.cache resolved/distdir derived/distdir
    bazel clean --expunge
  3. Get bazel's distdir content:

    bazel_release=$(<.bazelversion)
    bazel_url="https://github.com/bazelbuild/bazel/releases/download/${bazel_release}/bazel-${bazel_release}-dist.zip"
    bazel_dist=$(curl -sSfJLO --write-out '%{filename_effective}' "$bazel_url")
    unzip $bazel_dist "derived/distdir/*"
  4. Sync everything from WORKSPACE.bazel (and MODULE.bazel if BZLMOD is enabled) and bazel's secret internal externals:

    bazel sync
    du -sh bazel.repo.cache

    OMG. It downloaded about 3.8GB of crap‽

  5. Resolve then prejudicially filter out crap we are not interested in on linux-x86_64 with a local jdk:

    bazel run resolve < resolved.bzl |
           egrep --invert-match '(remotejdk|android|windows|darwin|arm64|s390x)' |
           tee resolved.lst 
  6. Copy from bazel's repository cache (or download):

    bazel build fetch                 # Yeah, it's just a bash script, but let's pretend it's more complicated.
    bazel-bin/fetch < resolved.lst    # Copies about ~100MB out of bazel's repository cache. What's the other 3.7Gb for‽
  7. While figuring stuff out, put things in manual/distdir if necessary.

  8. What about the go.starlark.net go_repository? Where did that go? It's not present anywhere in bazel.repo.cache.

    bazel run go env GOPATH GOMODCACHE GOCACHE
    /home/nick/go
    /home/nick/go/pkg/mod
    /home/nick/.cache/go-build
    

    Grr it's $HOME/go (actually $HOME/go/pkg/mod), we do not want crap in our home directory, outside the source tree, to be part of our build.

    GOPATH is likely fiddled with deep in the bowels of go_binary etc. so let's ignore that.

    We probably don't care about GOCACHE in $XDG_CACHE_HOME/go-build as that's the cache of build artifacts; it's right next to bazel's output anyway in $XDG_CACHE_HOME/bazel.

  9. Jail! Rather, network namespace!

    sudo ip netns add jail
    sudo ip netns exec jail ip link set lo up

    Clean up what has already been downloaded, hopefully not cached anywhere else, stop the bazel daemon:

    rm -rf $HOME/go $HOME/.cache/go-build
    bazel clean --expunge
    bazel shutdown

    Run a build in the jail:

    sudo ip netns exec jail sudo -u $USER $(which bazel) build resolve 

    Haaaa! DNS apparently worked but the actual TCP connections failed!

    Error in fail: failed to fetch go.starlark.net: fetch_repo: go.starlark.net@v0.0.0-20230224151120-c52844e64a10: Get "https://proxy.golang.org/go.starlark.net/@v/v0.0.0-20230224151120-c52844e64a10.info": dial tcp: lookup proxy.golang.org on 127.0.0.53:53: read udp 127.0.0.1:56368->127.0.0.53:53: read: connection refused

  10. OK, did bazel sync pull those down? Yes, it did. We can change where though; but this time we'll use fetch:

bazel fetch resolve --repo_env=GOMODCACHE=$PWD/go/pkg/mod

Note: we can't do this in .bazelrc as go requires an absolute path, and we need $PWD because %workspace% is not evaluated for --repo_env. We also set --repo_env=GO_REPOSITORY_USE_HOST_CACHE=1 in .bazelrc.

Kia kaha! We have ./go/pkg/mod/cache/download with the downloaded artifacts!

We do have all the expanded go mods too; let's delete them to ensure that they will be unpacked from the cache.

Also, see ./go/pkg/mod/cache/.gitignore.

  1. Try another jail build.
sudo ip netns exec jail sudo -u $USER $(which bazel) build resolve --repo_env=GOMODCACHE=$PWD/go/pkg/mod

Hooray!

It just so happens that the GOMODCACHE directory complies with the GOPROXY protocol, so we can use it that way too, either as a file:// URL or served with a simple HTTP server.