#!/bin/sh # End-to-end smoke for mgit: builds the CLI in the Docker builder, boots # the FAKE Gitea in a container on 127.0.0.1:8602, drives the real # binary from the host through a 0600 env file, and asserts the full # command surface (repo create/list/show, branch list, commit status, # PR create/list/show/merge, -o json, exit codes) plus token redaction. # No real forge is ever contacted. Only the exact container ID spawned # here is removed. set -e cd "$(dirname "$0")/.." IMAGE="golang@sha256:e8c859f5632dcfde7b32d2012b4351728f6437930887c2f6a91ea242459e5514" PORT=8602 KEY="smoke-gitea-token-0123456789" CID="" cleanup() { if [ -n "$CID" ]; then docker rm -f "$CID" >/dev/null 2>&1 || true fi rm -rf .smoke } trap cleanup EXIT INT TERM mkdir -p .smoke umask 077 echo "--- build CLI + fake (docker builder)" docker run --rm -v "$PWD:/h" -w /h \ -u "$(id -u):$(id -g)" -e HOME=/tmp -e GOFLAGS=-buildvcs=false \ "$IMAGE" sh -c 'go build -o bin/mgit ./cmd/mgit && go build -o bin/fakegitea ./smoke/fakegitea' echo "--- boot fake Gitea (container, port $PORT)" CID=$(docker run -d --rm \ -v "$PWD:/h" -w /h \ -u "$(id -u):$(id -g)" -e HOME=/tmp \ -p 127.0.0.1:$PORT:8602 \ -e FAKE_KEY="$KEY" \ "$IMAGE" /h/bin/fakegitea -addr :8602) # wait for the fake to answer (any HTTP response, even 401, proves it is up) i=0 until [ -n "$CID" ] && [ "$(docker inspect -f '{{.State.Running}}' "$CID" 2>/dev/null)" = "true" ] && \ python3 -c " import urllib.request, urllib.error req = urllib.request.Request('http://127.0.0.1:$PORT/api/v1/user/repos', headers={'Authorization': 'token probe'}) try: urllib.request.urlopen(req, timeout=2) except urllib.error.HTTPError: raise SystemExit(0) except Exception: raise SystemExit(1) raise SystemExit(0) "; do i=$((i+1)) if [ "$i" -ge 90 ]; then echo "smoke: fake Gitea did not come up; logs:" >&2 docker logs "$CID" >&2 || true exit 1 fi sleep 1 done printf 'GITEA_URL=http://127.0.0.1:%s\nGITEA_KEY=%s\n' "$PORT" "$KEY" > .smoke/env chmod 600 .smoke/env mgit() { ./bin/mgit --config .smoke/env "$@"; } echo "--- repo create + list + show" mgit repo create -n vertical-x --desc "one vertical" --private --auto-init > .smoke/repo.out grep -q "created repo ukrrs/vertical-x (private)" .smoke/repo.out mgit repo list > .smoke/repos.out grep -q "ukrrs/MOPAC" .smoke/repos.out grep -q "ukrrs/vertical-x" .smoke/repos.out mgit repo list -o json | python3 -c 'import json,sys repos = json.load(sys.stdin) assert any(r["full_name"] == "ukrrs/vertical-x" for r in repos), repos' mgit repo show ukrrs/MOPAC > .smoke/reposhow.out grep -q "default branch: main" .smoke/reposhow.out echo "--- branch list" mgit branch list ukrrs/MOPAC > .smoke/branches.out grep -q "main" .smoke/branches.out grep -q "feat/quota" .smoke/branches.out echo "--- commit status (report + combined)" mgit status create ukrrs/MOPAC feat/quota --state pending --context ci/lint \ --description "lint queued" > .smoke/st1.out grep -q "pending" .smoke/st1.out mgit status create ukrrs/MOPAC feat/quota --state success --context ci/lint \ --description "lint clean" > .smoke/st2.out grep -q "success" .smoke/st2.out mgit status list ukrrs/MOPAC feat/quota > .smoke/statuses.out grep -q "combined success (1 checks)" .smoke/statuses.out grep -q "ci/lint" .smoke/statuses.out echo "--- pr create (body from file) + list + show + merge" cat > .smoke/body.md <<'EOF' ## Scope - smoke body EOF mgit pr create ukrrs/MOPAC --title "Smoke: quota accounting" \ --body .smoke/body.md --base main --head feat/quota -o json > .smoke/pr.out grep -q '"number":' .smoke/pr.out mgit pr list ukrrs/MOPAC > .smoke/prs.out grep -q "Smoke: quota accounting" .smoke/prs.out grep -q "main..feat/quota" .smoke/prs.out mgit pr show ukrrs/MOPAC 1 > .smoke/prshow.out grep -q "smoke body" .smoke/prshow.out mgit pr merge ukrrs/MOPAC 1 --do squash > .smoke/merge.out grep -q "merged pull request #1 (squash)" .smoke/merge.out mgit pr list ukrrs/MOPAC --state closed > .smoke/prsclosed.out grep -q "Smoke: quota accounting" .smoke/prsclosed.out mgit pr list ukrrs/MOPAC > .smoke/prsopen.out if grep -q "Smoke" .smoke/prsopen.out; then echo "smoke: merged PR still listed as open" >&2; exit 1 fi echo "--- failure path: missing repo (exit 2, one-line stderr with http code)" if mgit repo show ukrrs/absent > .smoke/missing.out 2> .smoke/missing.err; then echo "smoke: missing repo should fail" >&2; exit 1 fi grep -q "http 404" .smoke/missing.err [ "$(wc -l < .smoke/missing.err)" -eq 1 ] || { echo "smoke: stderr not one line" >&2; exit 1; } [ ! -s .smoke/missing.out ] || { echo "smoke: stdout not empty on failure" >&2; exit 1; } echo "--- failure path: wrong token (exit 2, no token material in stderr)" printf 'GITEA_URL=http://127.0.0.1:%s\nGITEA_KEY=wrong-token-abcdef\n' "$PORT" > .smoke/bad.env chmod 600 .smoke/bad.env if ./bin/mgit --config .smoke/bad.env repo list 2> .smoke/bad.err; then echo "smoke: wrong token should fail" >&2; exit 1 fi grep -q "http 401" .smoke/bad.err echo "--- failure path: loose env file (exit 1)" printf 'GITEA_URL=http://127.0.0.1:%s\nGITEA_KEY=%s\n' "$PORT" "$KEY" > .smoke/loose.env chmod 644 .smoke/loose.env if ./bin/mgit --config .smoke/loose.env repo list 2> .smoke/loose.err; then echo "smoke: loose env file should be refused" >&2; exit 1 fi grep -q "insecure mode" .smoke/loose.err echo "--- redaction: no token material in any captured output" for f in .smoke/*.out .smoke/*.err; do grep -qF "$KEY" "$f" && { echo "smoke: token leaked into $f" >&2; exit 1; } done grep -qF "wrong-token-abcdef" .smoke/bad.err && { echo "smoke: wrong token echoed" >&2; exit 1; } echo "smoke: OK"