Webhook-to-SSE gateway with hierarchical topic routing and signature verification
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Return version banner on root URL, embed version at build time

The root path was returning 404 which made it impossible to use with
site-pinger for uptime monitoring. Now returns "wicket <version>" as
text/plain. Version is set via ldflags in ko and defaults to "dev"
locally. Also removes the unused Dockerfile since we build with ko.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+42 -11
+8
.ko.yaml
··· 1 1 defaultBaseImage: cgr.dev/chainguard/static:latest 2 + builds: 3 + - id: wicket 4 + dir: . 5 + main: . 6 + flags: 7 + - -trimpath 8 + ldflags: 9 + - -X main.version={{.Env.VERSION}}
+2 -1
.tangled/workflows/ci.yml
··· 28 28 command: | 29 29 go install github.com/google/ko@latest 30 30 export PATH="$(go env GOPATH)/bin:$PATH" 31 + export VERSION="$(date -u +%Y%m%d%H%M%S)-$(git rev-parse --short HEAD)" 31 32 echo "${ATCR_APP_PASSWORD}" | docker login atcr.io -u guid.foo --password-stdin 32 - ko build --bare --tags "latest,$(date -u +%Y%m%d%H%M%S)-$(git rev-parse --short HEAD)" . 33 + ko build --bare --tags "latest,${VERSION}" .
-10
Dockerfile
··· 1 - FROM golang:1.25 AS build 2 - WORKDIR /src 3 - COPY go.mod go.sum* ./ 4 - RUN go mod download 5 - COPY . . 6 - RUN CGO_ENABLED=0 go build -o /wicket . 7 - 8 - FROM scratch 9 - COPY --from=build /wicket /wicket 10 - ENTRYPOINT ["/wicket"]
+8
server.go
··· 15 15 "github.com/google/uuid" 16 16 ) 17 17 18 + var version = "dev" 19 + 18 20 type Server struct { 19 21 broker *Broker 20 22 config *atomic.Pointer[Configuration] ··· 27 29 28 30 func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 29 31 setCORSHeaders(w) 32 + 33 + if r.Method == "GET" && r.URL.Path == "/" { 34 + w.Header().Set("Content-Type", "text/plain") 35 + fmt.Fprintf(w, "wicket %s\n", version) 36 + return 37 + } 30 38 31 39 if r.Method == "GET" && r.URL.Path == "/_health" { 32 40 w.WriteHeader(http.StatusNoContent)
+24
server_test.go
··· 27 27 return httptest.NewServer(handler), broker, cancel 28 28 } 29 29 30 + func TestServer_rootBanner(t *testing.T) { 31 + ts, _, cancel := newTestServer(nil) 32 + defer cancel() 33 + defer ts.Close() 34 + 35 + resp, err := http.Get(ts.URL + "/") 36 + if err != nil { 37 + t.Fatalf("GET / failed: %v", err) 38 + } 39 + defer resp.Body.Close() 40 + if resp.StatusCode != http.StatusOK { 41 + t.Errorf("expected 200, got %d", resp.StatusCode) 42 + } 43 + if ct := resp.Header.Get("Content-Type"); ct != "text/plain" { 44 + t.Errorf("expected text/plain, got %s", ct) 45 + } 46 + buf := make([]byte, 1024) 47 + n, _ := resp.Body.Read(buf) 48 + body := string(buf[:n]) 49 + if !strings.Contains(body, "wicket") { 50 + t.Errorf("expected body to contain 'wicket', got %q", body) 51 + } 52 + } 53 + 30 54 func TestServer_healthEndpoint(t *testing.T) { 31 55 ts, _, cancel := newTestServer(nil) 32 56 defer cancel()