gitsite: verify packaged lifecycle
ober
0d0ae9496a6867a4dca3d3af294db3243124f076
--- a/Makefile +++ b/Makefile @@ -10,7 +10,13 @@ JERBOA_GIT ?= $(CURDIR)/vendor/jerboa-git/native SINATRA_SRC ?= $(CURDIR)/vendor/jerboa-sinatra # Native FFI library path for jerboa-git -JERBOA_GIT_LIB_PATH ?= $(JERBOA_GIT)/target/release/libjerboa_git_shim.dylib +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Darwin) +SHLIB_EXT := dylib +else +SHLIB_EXT := so +endif +JERBOA_GIT_LIB_PATH ?= $(JERBOA_GIT)/target/release/libjerboa_git_shim.$(SHLIB_EXT) BUILD_DIR ?= build SRC_STAGE := $(BUILD_DIR)/src @@ -20,7 +26,7 @@ PORT ?= 8080 BINARY_OUTPUT ?= dist/gitsite -.PHONY: build run run-builds check verify clean binary jerboa-git vendor vendor-jsqlite vendor-sinatra +.PHONY: build run run-builds check verify test-integration clean binary jerboa-git vendor vendor-jsqlite vendor-sinatra vendor: vendor-jsqlite vendor-sinatra vendor-git @@ -53,6 +59,9 @@ run-builds: build binary: build mkdir -p dist JERBOA_GIT_LIB_PATH="$(JERBOA_GIT_LIB_PATH)" $(JERBUILD) binary --libdirs "$(LIBDIRS)" src/gitsite.ss $(BINARY_OUTPUT) + mkdir -p dist/lib + cp $(JERBOA_HOME)/lib/libjerboa_native.* dist/lib/ + cp $(JERBOA_GIT_LIB_PATH) dist/lib/ check: build JERBOA_GIT_LIB_PATH="$(JERBOA_GIT_LIB_PATH)" $(JERBUILD) exec --libdirs "$(LIBDIRS)" tests/smoke.ss @@ -60,11 +69,11 @@ check: build JERBOA_GIT_LIB_PATH="$(JERBOA_GIT_LIB_PATH)" $(JERBUILD) exec --libdirs "$(LIBDIRS)" tests/test-manifest.ss JERBOA_GIT_LIB_PATH="$(JERBOA_GIT_LIB_PATH)" $(JERBUILD) exec --libdirs "$(LIBDIRS)" tests/test-auth.ss -verify: binary check - @echo "verify: binary built and checks pass" +verify: check test-integration + @echo "verify: binary, checks, and integration lifecycle pass" test-integration: binary - JERBOA_GIT_LIB_PATH="$(JERBOA_GIT_LIB_PATH)" tests/test-integration.sh + JERBOA_HOME="$(CURDIR)/dist" JERBOA_GIT_LIB_PATH="$(CURDIR)/dist/lib/$(notdir $(JERBOA_GIT_LIB_PATH))" tests/test-integration.sh clean: rm -rf $(BUILD_DIR) var dist --- a/README.md +++ b/README.md @@ -16,17 +16,19 @@ A minimal SourceHut-style git forge built in Jerboa. ## Quick Start -### Build Static Binary (Production) +### Build Release Bundle (Production) ```bash make binary ``` -Creates `dist/gitsite` — a single static binary with subcommands: +Creates `dist/gitsite` with native runtime libraries in `dist/lib/`. +The binary has these subcommands: `serve`, `ssh-auth`, `buildd`, `add-user`, `migrate`. -No Jerboa runtime, no SQLite3 library, no Scheme interpreter required -on the target machine. +No Jerboa install, SQLite3 library, or Scheme interpreter is required on the +target machine. Run the binary with `JERBOA_HOME` pointing at the bundled +`dist` directory so the packaged native runtime can be loaded. ### Create First User @@ -95,9 +97,12 @@ Edit `etc/gitsite.sexp`: sudo useradd -r -m -d /srv/gitsite-build -s /bin/bash gitsite-build ``` -2. **Install binary**: +2. **Install release bundle**: ```bash - sudo install -m 755 dist/gitsite /usr/local/bin/ + sudo install -d -m 755 /usr/local/libexec/gitsite + sudo install -m 755 dist/gitsite /usr/local/libexec/gitsite/ + sudo cp -R dist/lib /usr/local/libexec/gitsite/ + sudo ln -sf /usr/local/libexec/gitsite/gitsite /usr/local/bin/gitsite ``` 3. **Create directories**: @@ -145,6 +150,8 @@ Edit `etc/gitsite.sexp`: Type=simple User=gitsite WorkingDirectory=/srv/gitsite + Environment=JERBOA_HOME=/usr/local/libexec/gitsite + Environment=JERBOA_GIT_LIB_PATH=/usr/local/libexec/gitsite/lib/libjerboa_git_shim.so ExecStart=/usr/local/bin/gitsite serve Restart=on-failure @@ -182,7 +189,10 @@ Edit `etc/gitsite.sexp`: For asynchronous build execution: ```bash -sudo -u gitsite /usr/local/bin/gitsite buildd & +sudo -u gitsite \ + JERBOA_HOME=/usr/local/libexec/gitsite \ + JERBOA_GIT_LIB_PATH=/usr/local/libexec/gitsite/lib/libjerboa_git_shim.so \ + /usr/local/bin/gitsite buildd & ``` Or create a separate systemd service: --- a/tests/test-integration.sh +++ b/tests/test-integration.sh @@ -1,40 +1,83 @@ #!/bin/sh set -eux -# Integration test: clone, push, build lifecycle via gitsite server -# Requires: gitsite binary built and running on PORT (default 8080) +# Integration test: create repo, push, clone, submit build lifecycle. PORT=${PORT:-8080} BINARY=${BINARY:-dist/gitsite} +case "$BINARY" in + /*) BINARY_ABS=$BINARY ;; + *) BINARY_ABS=$(pwd)/$BINARY ;; +esac TMP=$(mktemp -d /tmp/gitsite-integ.XXXXXX) -trap 'kill %1 2>/dev/null; rm -rf "$TMP"' EXIT +COOKIE="$TMP/cookies" +SERVER_PID= +trap 'if [ -n "$SERVER_PID" ]; then kill "$SERVER_PID" 2>/dev/null || true; fi; rm -rf "$TMP"' EXIT -# Start gitsite in background -$BINARY serve & -sleep 1 +mkdir -p "$TMP/etc" +cat > "$TMP/etc/gitsite.sexp" <<EOF +((listen-port $PORT) + (listen-address "127.0.0.1") + (var-root "$TMP/var") + (public-url "http://127.0.0.1:$PORT") + (registration "open") + (session-secret "integration-test-session-secret-32") + (tls-cert #f) + (tls-key #f) + (builds-mode "sync") + (buildd-workers 1) + (mode development)) +EOF -# Create a repo via HTTP -curl -sf -X POST -u admin:token "http://127.0.0.1:$PORT/api/v1/repos" \ - -d '{"name":"integration-test","owner":"admin"}' > /dev/null +cd "$TMP" +"$BINARY_ABS" add-user admin admin@example.invalid password123 -# Clone via HTTP -git clone "http://127.0.0.1:$PORT/git/~admin/integration-test.git" "$TMP/clone" -echo "ok" > "$TMP/clone/hello.txt" -git -C "$TMP/clone" add hello.txt -git -C "$TMP/clone" commit -m "initial commit" +# Start gitsite in background +"$BINARY_ABS" serve > "$TMP/server.log" 2>&1 & +SERVER_PID=$! +sleep 2 -# Push via HTTP -git -C "$TMP/clone" push origin main +# Login and create a repo through the implemented web routes. +curl -sf -c "$COOKIE" "http://127.0.0.1:$PORT/login" > /dev/null +curl -sf -b "$COOKIE" -c "$COOKIE" -X POST \ + -d "name=admin&password=password123" \ + "http://127.0.0.1:$PORT/login" > /dev/null -# Submit a build manifest -curl -sf -X POST -u admin:token \ - "http://127.0.0.1:$PORT/api/v1/builds" \ - -d '{"repo":"~admin/integration-test","sha":"HEAD","manifest":"tasks:\n - build: echo hello"}' +curl -sf -b "$COOKIE" "http://127.0.0.1:$PORT/new" > "$TMP/new.html" +CSRF=$(sed -n 's/.*name="csrf" value="\([^"]*\)".*/\1/p' "$TMP/new.html" | head -1) +curl -sf -b "$COOKIE" -c "$COOKIE" -X POST \ + -d "name=integration-test&description=integration&visibility=public&csrf=$CSRF" \ + "http://127.0.0.1:$PORT/new" > /dev/null -# Wait for build -sleep 2 +# Push test content into the bare repo. HTTPS push is intentionally disabled in +# gitsite v1; SSH push requires a system sshd fixture, so this local integration +# test verifies the server observes a real git push into repository storage. +git init -q "$TMP/src" +git -C "$TMP/src" config user.email admin@example.invalid +git -C "$TMP/src" config user.name admin +printf 'ok\n' > "$TMP/src/README.md" +cat > "$TMP/src/.build.yml" <<'EOF' +tasks: + - build: echo "build succeeded" +EOF +git -C "$TMP/src" add README.md .build.yml +git -C "$TMP/src" commit -qm "initial commit" +git -C "$TMP/src" push -q "$TMP/var/repos/admin/integration-test.git" HEAD:refs/heads/main + +# Clone via HTTP +git clone "http://127.0.0.1:$PORT/git/~admin/integration-test.git" "$TMP/clone" +grep -q "ok" "$TMP/clone/README.md" -# Verify build log exists and shows success -curl -sf "http://127.0.0.1:$PORT/~admin/integration-test/log" | grep -q "build succeeded" +# Submit and verify a synchronous build. +curl -sf -b "$COOKIE" "http://127.0.0.1:$PORT/~admin/integration-test/builds" > "$TMP/builds.html" +CSRF=$(sed -n 's/.*name="csrf" value="\([^"]*\)".*/\1/p' "$TMP/builds.html" | head -1) +curl -s -D "$TMP/submit.headers" -o "$TMP/submit.body" -b "$COOKIE" -c "$COOKIE" -X POST \ + -d "csrf=$CSRF" \ + "http://127.0.0.1:$PORT/~admin/integration-test/submit" +JOB_PATH=$(sed -n 's/^[Ll]ocation: \(.*\)$/\1/p' "$TMP/submit.headers" | tr -d '\r' | head -1) +test -n "$JOB_PATH" +curl -sf -b "$COOKIE" "http://127.0.0.1:$PORT$JOB_PATH" > "$TMP/job.html" +grep -q "success" "$TMP/job.html" +grep -q "build succeeded" "$TMP/var/builds/1.log" echo "PASS: integration test"