feat: bootstrap Go project layout (forgejo-mcp-broker-n84)
- Initialize go.mod with module path kode.naiv.no/olemd/forgejo-mcp-broker
- Create directory layout: cmd/broker + internal/{buildinfo,config,log,store,httpserver}
- Add Makefile with build/test/lint/tidy/clean targets and ldflags-injected build info
- Stub cmd/broker/main.go with --version support; real wiring follows in -t37
- Stub doc.go for each internal/* package, pointing to the issue that fills it in
Closes forgejo-mcp-broker-n84.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
021bf0502d
commit
de5ce2de94
10 changed files with 134 additions and 0 deletions
5
.beads/issues.jsonl
Normal file
5
.beads/issues.jsonl
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{"id":"forgejo-mcp-broker-8ei","title":"Phase 1: internal/httpserver with /healthz and graceful shutdown","description":"Implement internal/httpserver: constructs a *http.Server bound to cfg.Listen, mounts GET /healthz (returns 200 with JSON build-info from the build-info package, including version, git revision, build date, and current store status), handles SIGTERM/SIGINT by initiating graceful shutdown with a configurable deadline (default 10s). Uses log/slog for structured JSON logs. Exposes a Run(ctx) error method that blocks until shutdown completes.","acceptance_criteria":"go test ./internal/httpserver passes; GET /healthz returns expected JSON; sending SIGTERM causes Run to return nil within 2 seconds after in-flight requests complete; slow in-flight request is allowed to finish within the deadline, then forcibly closed.","status":"open","priority":1,"issue_type":"task","owner":"olemd@glemt.net","created_at":"2026-04-24T14:46:20Z","created_by":"Ole-Morten Duesund","updated_at":"2026-04-24T14:46:20Z","dependencies":[{"issue_id":"forgejo-mcp-broker-8ei","depends_on_id":"forgejo-mcp-broker-n84","type":"blocks","created_at":"2026-04-24T16:46:19Z","created_by":"Ole-Morten Duesund","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0}
|
||||
{"id":"forgejo-mcp-broker-t37","title":"Phase 1: wire cmd/broker/main.go and integration test","description":"Final phase 1 task: wire config → log → store → httpserver in cmd/broker/main.go. Parse config, init slog, open store, start httpserver, wait for shutdown signal, close store, exit. Add an integration test under cmd/broker/ that builds the binary, runs it with a valid env + temp store path, curls /healthz, sends SIGTERM, verifies clean exit within 2s. This is the acceptance gate for phase 1.","acceptance_criteria":"make build; make test (incl. integration) pass; running the binary with missing config fails with a clear error; running with valid config serves /healthz; SIGTERM shuts down cleanly within 2s; /healthz JSON includes version, git revision, build date, and store OK status.","status":"open","priority":1,"issue_type":"task","owner":"olemd@glemt.net","created_at":"2026-04-24T14:46:20Z","created_by":"Ole-Morten Duesund","updated_at":"2026-04-24T14:46:20Z","dependencies":[{"issue_id":"forgejo-mcp-broker-t37","depends_on_id":"forgejo-mcp-broker-8ei","type":"blocks","created_at":"2026-04-24T16:48:29Z","created_by":"Ole-Morten Duesund","metadata":"{}"},{"issue_id":"forgejo-mcp-broker-t37","depends_on_id":"forgejo-mcp-broker-9jh","type":"blocks","created_at":"2026-04-24T16:48:29Z","created_by":"Ole-Morten Duesund","metadata":"{}"},{"issue_id":"forgejo-mcp-broker-t37","depends_on_id":"forgejo-mcp-broker-9nq","type":"blocks","created_at":"2026-04-24T16:48:28Z","created_by":"Ole-Morten Duesund","metadata":"{}"},{"issue_id":"forgejo-mcp-broker-t37","depends_on_id":"forgejo-mcp-broker-n84","type":"blocks","created_at":"2026-04-24T16:48:28Z","created_by":"Ole-Morten Duesund","metadata":"{}"}],"dependency_count":4,"dependent_count":0,"comment_count":0}
|
||||
{"id":"forgejo-mcp-broker-9jh","title":"Phase 1: internal/store with SQLite open and embedded schema migrations","description":"Implement internal/store: wraps a modernc.org/sqlite connection, applies embedded schema migrations in order via a schema_migrations table, exposes a *sql.DB and a Close method. Phase 1 schema is just the migrations table itself plus a health_check row — real tables (clients, auth_codes, access_tokens, refresh_tokens) ship in phase 2. Store_path from config; creates parent dirs if missing; fails fast on unwritable path. Migrations embedded via embed.FS under internal/store/migrations/.","acceptance_criteria":"go test ./internal/store passes; opening a fresh db file applies migrations; re-opening is idempotent (no re-application, no errors); corrupt/locked files yield a clear error; Close() leaves no file handles open.","status":"open","priority":1,"issue_type":"task","owner":"olemd@glemt.net","created_at":"2026-04-24T14:46:19Z","created_by":"Ole-Morten Duesund","updated_at":"2026-04-24T14:46:19Z","dependencies":[{"issue_id":"forgejo-mcp-broker-9jh","depends_on_id":"forgejo-mcp-broker-n84","type":"blocks","created_at":"2026-04-24T16:46:19Z","created_by":"Ole-Morten Duesund","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0}
|
||||
{"id":"forgejo-mcp-broker-9nq","title":"Phase 1: internal/config package with flag + env parsing and validation","description":"Implement internal/config: a Config struct populated from CLI flags and environment variables (flags win), with validation at startup. Parse public-url, listen addr, forgejo-url, forgejo-oauth-client-id/secret/scopes, forgejo-mcp-binary, store-path, max-sessions, session-idle-timeout, debug. Validation: required fields present and non-empty; public-url parses as an https URL; store-path writable; idle-timeout positive; max-sessions positive. Unit tests cover happy path + every validation error branch.","acceptance_criteria":"go test ./internal/config passes with \u003e=90% coverage; missing required env produces a clear error message listing all missing fields; flag values override env; --help prints all config options.","status":"open","priority":1,"issue_type":"task","owner":"olemd@glemt.net","created_at":"2026-04-24T14:46:19Z","created_by":"Ole-Morten Duesund","updated_at":"2026-04-24T14:46:19Z","dependencies":[{"issue_id":"forgejo-mcp-broker-9nq","depends_on_id":"forgejo-mcp-broker-n84","type":"blocks","created_at":"2026-04-24T16:46:18Z","created_by":"Ole-Morten Duesund","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0}
|
||||
{"id":"forgejo-mcp-broker-n84","title":"Phase 1: bootstrap Go project layout","description":"Set up the Go project skeleton so all subsequent phase 1 packages have somewhere to live. Initialize go.mod with module path kode.naiv.no/olemd/forgejo-mcp-broker, create the directory layout (cmd/broker, internal/config, internal/log, internal/store, internal/httpserver), add a Makefile with build/test/lint targets, and wire build-info injection (version, git revision, build date) via -ldflags.","acceptance_criteria":"go.mod present with correct module path; make build produces ./fjmcp-broker binary; make test and make lint targets exist and pass against an empty codebase; binary prints --version with injected build info.","status":"in_progress","priority":1,"issue_type":"task","assignee":"Ole-Morten Duesund","owner":"olemd@glemt.net","created_at":"2026-04-24T14:45:44Z","created_by":"Ole-Morten Duesund","updated_at":"2026-04-24T14:50:57Z","started_at":"2026-04-24T14:50:57Z","dependency_count":0,"dependent_count":4,"comment_count":0}
|
||||
39
Makefile
Normal file
39
Makefile
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# forgejo-mcp-broker Makefile
|
||||
|
||||
BINARY := fjmcp-broker
|
||||
CMD_PKG := ./cmd/broker
|
||||
MODULE := kode.naiv.no/olemd/forgejo-mcp-broker
|
||||
VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
|
||||
GIT_REV := $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
|
||||
BUILD_DATE := $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
LDFLAGS := -s -w \
|
||||
-X $(MODULE)/internal/buildinfo.Version=$(VERSION) \
|
||||
-X $(MODULE)/internal/buildinfo.GitRevision=$(GIT_REV) \
|
||||
-X $(MODULE)/internal/buildinfo.BuildDate=$(BUILD_DATE)
|
||||
|
||||
.PHONY: all build test lint tidy clean help
|
||||
|
||||
all: build
|
||||
|
||||
build: ## Build the broker binary
|
||||
go build -trimpath -ldflags '$(LDFLAGS)' -o $(BINARY) $(CMD_PKG)
|
||||
|
||||
test: ## Run tests with the race detector
|
||||
go test -race ./...
|
||||
|
||||
lint: ## Static analysis (go vet; golangci-lint if installed)
|
||||
go vet ./...
|
||||
@if command -v golangci-lint >/dev/null 2>&1; then \
|
||||
golangci-lint run; \
|
||||
else \
|
||||
echo "golangci-lint not installed; skipping (go vet already ran)"; \
|
||||
fi
|
||||
|
||||
tidy: ## Tidy go.mod / go.sum
|
||||
go mod tidy
|
||||
|
||||
clean: ## Remove build artefacts
|
||||
rm -f $(BINARY)
|
||||
|
||||
help: ## Show available targets
|
||||
@awk 'BEGIN {FS = ":.*##"; print "Targets:"} /^[a-zA-Z_-]+:.*?##/ { printf " %-8s %s\n", $$1, $$2 }' $(MAKEFILE_LIST)
|
||||
31
cmd/broker/main.go
Normal file
31
cmd/broker/main.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// Command fjmcp-broker is an OAuth 2.1 authorization server and MCP session
|
||||
// broker that fronts forgejo-mcp. See ../../README.md and ../../docs/ for the
|
||||
// design.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"kode.naiv.no/olemd/forgejo-mcp-broker/internal/buildinfo"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var showVersion bool
|
||||
flag.BoolVar(&showVersion, "version", false, "print build info and exit")
|
||||
flag.Parse()
|
||||
|
||||
if showVersion {
|
||||
fmt.Printf("fjmcp-broker %s (rev %s, built %s)\n",
|
||||
buildinfo.Version, buildinfo.GitRevision, buildinfo.BuildDate)
|
||||
return
|
||||
}
|
||||
|
||||
// Full startup wiring (config → log → store → httpserver) lands in
|
||||
// forgejo-mcp-broker-t37. Until then this binary only serves --version
|
||||
// so the bootstrap acceptance criteria can be exercised.
|
||||
fmt.Fprintln(os.Stderr, "fjmcp-broker: runtime wiring not yet implemented (phase 1 in progress)")
|
||||
fmt.Fprintln(os.Stderr, "Use --version to print build info.")
|
||||
os.Exit(2)
|
||||
}
|
||||
3
go.mod
Normal file
3
go.mod
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
module kode.naiv.no/olemd/forgejo-mcp-broker
|
||||
|
||||
go 1.26
|
||||
12
internal/buildinfo/buildinfo.go
Normal file
12
internal/buildinfo/buildinfo.go
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
// Package buildinfo exposes compile-time build metadata.
|
||||
//
|
||||
// Values are injected at link time via -ldflags -X (see the Makefile). When
|
||||
// the binary is built without those flags (e.g. go run, go test), the
|
||||
// placeholder defaults below are used.
|
||||
package buildinfo
|
||||
|
||||
var (
|
||||
Version = "dev"
|
||||
GitRevision = "unknown"
|
||||
BuildDate = "unknown"
|
||||
)
|
||||
21
internal/buildinfo/buildinfo_test.go
Normal file
21
internal/buildinfo/buildinfo_test.go
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package buildinfo_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"kode.naiv.no/olemd/forgejo-mcp-broker/internal/buildinfo"
|
||||
)
|
||||
|
||||
func TestDefaultsArePopulated(t *testing.T) {
|
||||
// Build-info placeholders must never be empty: they are exposed on /healthz
|
||||
// and an empty value would be observable to operators as a broken build.
|
||||
if buildinfo.Version == "" {
|
||||
t.Error("Version is empty")
|
||||
}
|
||||
if buildinfo.GitRevision == "" {
|
||||
t.Error("GitRevision is empty")
|
||||
}
|
||||
if buildinfo.BuildDate == "" {
|
||||
t.Error("BuildDate is empty")
|
||||
}
|
||||
}
|
||||
5
internal/config/doc.go
Normal file
5
internal/config/doc.go
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// Package config loads broker configuration from flags and environment
|
||||
// variables, applies defaults, and validates the result.
|
||||
//
|
||||
// Implementation lands in forgejo-mcp-broker-9nq.
|
||||
package config
|
||||
6
internal/httpserver/doc.go
Normal file
6
internal/httpserver/doc.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// Package httpserver hosts the broker's HTTP surface: OAuth endpoints, the
|
||||
// gated MCP endpoint, and /healthz. Owns an *http.Server with graceful
|
||||
// shutdown on SIGTERM / SIGINT.
|
||||
//
|
||||
// Implementation lands in forgejo-mcp-broker-8ei.
|
||||
package httpserver
|
||||
6
internal/log/doc.go
Normal file
6
internal/log/doc.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// Package log constructs the process-wide structured logger (log/slog, JSON
|
||||
// handler to stderr) and provides small helpers for attaching build-info and
|
||||
// request-scoped fields.
|
||||
//
|
||||
// Implementation lands alongside forgejo-mcp-broker-8ei / t37.
|
||||
package log
|
||||
6
internal/store/doc.go
Normal file
6
internal/store/doc.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// Package store opens the SQLite-backed persistence layer used by the broker
|
||||
// for OAuth clients, authorization codes, access tokens, and refresh tokens.
|
||||
// Migrations are embedded via embed.FS and applied on open.
|
||||
//
|
||||
// Implementation lands in forgejo-mcp-broker-9jh.
|
||||
package store
|
||||
Loading…
Add table
Add a link
Reference in a new issue