git-ents.gitmain
⌘K
foforge
commit 2fb135d
release: deploy the single-node hosted root to Fly

git-ents itself (roots.single-node-hosted), served behind stock git’s smart-HTTP transport (nginx+fcgiwrap in front of git-http-backend), with setup --hosted wiring the mandatory gate into pre-receive/post-receive on first boot.

No Rust toolchain in the image: docker/bin/git-ents is a musl static binary cross-compiled on the host (cargo zigbuild --target x86_64-unknown-linux-musl) and materialized from the blob recorded at refs/meta/releases/<source-commit-sha> — gitignored, never a normal tracked file.

release: add Dockerfile, docker/entrypoint.sh, docker/nginx.conf release: repoint .config/fly.toml at the new Dockerfile and app release: ignore docker/bin/ (materialized release binary)

Joseph D. Carpinelli · 1 month ago

Reviews

No reviews of this commit yet — record a verdict below.

Start a review

verdict

.dockerignore @@ -1,2 +1,6 @@ -fly.toml -.git/ +# The Dockerfile only ever COPYs docker/*; ignore everything else so the +# build context stays a few KB instead of the whole workspace (including +# target/, which alone can run into the tens of GB). +* +!docker/ +!docker/**
.gitignore @@ -1,2 +1,5 @@ .DS_Store target/ +# Materialized from refs/meta/releases/<sha> just before `docker build`; +# never a normal tracked file. +docker/bin/
.config/fly.toml @@ -1,20 +1,25 @@ -# fly.toml app configuration file generated for git-ents-server on 2026-06-20T12:17:33-04:00 +# fly.toml app configuration file for git-ents-hosted. # # See https://fly.io/docs/reference/configuration/ for information about how to use this file. # +# `git-ents-server` (no `-c` needed, its own separate app) is the legacy +# pre-redo deployment, left alone. This app is the single-node hosted root +# (`roots.single-node-hosted`, phase 6), a dedicated app so its shared +# anycast proxy never round-robins traffic onto legacy's stopped machine. -app = 'git-ents-server' +app = 'git-ents-hosted' primary_region = 'iad' [build] - # crates/git-ents-server doesn't exist yet post-redo; path is stale until it's rebuilt - dockerfile = "../crates/git-ents-server/Dockerfile" + # The single-node hosted root (`roots.single-node-hosted`, phase 6): + # `git-ents` itself, served behind stock git's smart-HTTP transport. + dockerfile = "../Dockerfile" [env] PORT = '8080' [mounts] - source = 'odb' + source = 'git_ents_hosted' destination = '/data' [http_service]
Dockerfile @@ -1,0 +1,34 @@ +# The single-node hosted root (`roots.single-node-hosted`, +# docs/development-plan.adoc phase 6): the `git-ents` binary itself, wired +# so stock git's own smart-HTTP transport (`git http-backend`, via +# nginx+fcgiwrap) invokes its `pre-receive`/`post-receive` hooks. No +# Postgres/Tigris/gix-receive here — that is `git-ents-server`, phase 8. +# +# No Rust toolchain, no cargo build, in this image: `docker/bin/git-ents` is +# a musl static binary cross-compiled on the host (`cargo zigbuild --target +# x86_64-unknown-linux-musl`) and materialized here from the on-disk blob +# recorded at `refs/meta/releases/<source-commit-sha>` — never a normal +# tracked file on `refs/heads` (see `.gitignore`). +FROM debian:bookworm-slim AS runtime +WORKDIR /app +# git: the bare repo + git-http-backend CGI itself. +# nginx+fcgiwrap+spawn-fcgi: the smart-HTTP transport (Phase 0's bootstrap, +# still the transport Phase 6 rides per docs/development-plan.adoc). +# curl: installs the sprite CLI the post-receive hook shells out to. +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + git ca-certificates curl nginx fcgiwrap spawn-fcgi \ + && rm -rf /var/lib/apt/lists/* +# The sprite CLI runs post-receive's checks in a Sprite; it reads +# SPRITES_TOKEN from the env. The installer drops the binary in +# $HOME/.local/bin and never touches PATH, so point it at /usr/local/bin +# (already on PATH) where the hosted root can spawn it. +RUN curl -fsSL https://sprites.dev/install.sh \ + | env SPRITE_INSTALL_PREFERRED_DIRS=/usr/local/bin \ + SPRITE_INSTALL_DEFAULT_BIN_DIR=/usr/local/bin bash +COPY docker/bin/git-ents /usr/local/bin/git-ents +RUN chmod +x /usr/local/bin/git-ents +COPY docker/nginx.conf /etc/git-ents/nginx.conf +COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh +RUN chmod +x /usr/local/bin/entrypoint.sh +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
docker/entrypoint.sh @@ -1,0 +1,22 @@ +#!/bin/sh +# Bootstraps the single-node hosted root (`roots.single-node-hosted`) on +# first boot, then serves it over stock git's smart-HTTP transport. +set -eu + +repo=/data/repo.git +key=/data/hosted_signing_key + +if [ ! -d "$repo" ]; then + git init --quiet --bare "$repo" +fi +git -C "$repo" config http.receivepack true +git -C "$repo" config http.uploadpack true + +# Idempotent: reuses $key if it already exists (persisted on the /data +# volume across deploys), and always reinstalls hooks pointing at this +# binary's own current path. +git-ents setup --hosted --key "$key" "$repo" + +mkdir -p /run +spawn-fcgi -s /run/fcgiwrap.sock -M 766 -- /usr/sbin/fcgiwrap +exec nginx -c /etc/git-ents/nginx.conf -g "daemon off;"
docker/nginx.conf @@ -1,0 +1,33 @@ +worker_processes 1; +pid /run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + access_log /dev/stdout; + error_log /dev/stderr; + + server { + listen 8080; + + location / { + # git push can be large; never buffer it into a temp file. + client_max_body_size 0; + gzip off; + + include /etc/nginx/fastcgi_params; + fastcgi_pass unix:/run/fcgiwrap.sock; + fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend; + fastcgi_param GIT_HTTP_EXPORT_ALL ""; + fastcgi_param GIT_PROJECT_ROOT /data; + fastcgi_param PATH_INFO $uri; + # git-http-backend spawns receive-pack, which runs our hooks, + # which shell to the sprite CLI (`ents-effect`'s + # `SpriteExecutor`) — fcgiwrap otherwise hands CGI processes a + # minimal environment with no PATH at all. + fastcgi_param PATH "/usr/local/bin:/usr/bin:/bin"; + } + } +}