203 lines
5.7 KiB
Markdown
203 lines
5.7 KiB
Markdown
|
|
---
|
||
|
|
phase: 01-foundation
|
||
|
|
plan: 01
|
||
|
|
type: execute
|
||
|
|
wave: 1
|
||
|
|
depends_on: []
|
||
|
|
files_modified:
|
||
|
|
- go.mod
|
||
|
|
- go.sum
|
||
|
|
- cmd/server/main.go
|
||
|
|
- internal/health/handler.go
|
||
|
|
- docker/Dockerfile
|
||
|
|
- .dockerignore
|
||
|
|
autonomous: true
|
||
|
|
|
||
|
|
must_haves:
|
||
|
|
truths:
|
||
|
|
- "Go module initializes without errors"
|
||
|
|
- "Go server compiles to a single binary"
|
||
|
|
- "Docker image builds successfully for linux/amd64"
|
||
|
|
- "Built image is less than 150MB (debian slim + static binary)"
|
||
|
|
artifacts:
|
||
|
|
- path: "go.mod"
|
||
|
|
provides: "Go module definition"
|
||
|
|
contains: "module"
|
||
|
|
- path: "cmd/server/main.go"
|
||
|
|
provides: "Application entry point"
|
||
|
|
contains: "func main()"
|
||
|
|
- path: "internal/health/handler.go"
|
||
|
|
provides: "Health check endpoint handler"
|
||
|
|
contains: "func Handler"
|
||
|
|
- path: "docker/Dockerfile"
|
||
|
|
provides: "Multi-stage build definition"
|
||
|
|
contains: "FROM golang"
|
||
|
|
- path: ".dockerignore"
|
||
|
|
provides: "Build context exclusions"
|
||
|
|
contains: ".git"
|
||
|
|
key_links:
|
||
|
|
- from: "cmd/server/main.go"
|
||
|
|
to: "internal/health"
|
||
|
|
via: "import statement"
|
||
|
|
pattern: "internal/health"
|
||
|
|
- from: "docker/Dockerfile"
|
||
|
|
to: "cmd/server"
|
||
|
|
via: "go build command"
|
||
|
|
pattern: "go build.*cmd/server"
|
||
|
|
---
|
||
|
|
|
||
|
|
<objective>
|
||
|
|
Create Go project structure and multi-stage Dockerfile for isolated container builds.
|
||
|
|
|
||
|
|
Purpose: Establishes the foundational artifacts needed to build and run the Pirate Station backend. This plan creates the Go module with a minimal HTTP server and the multi-stage Dockerfile that produces a portable ARM64/x86_64 binary.
|
||
|
|
|
||
|
|
Output: Buildable Go project with Dockerfile that produces a slim container image.
|
||
|
|
</objective>
|
||
|
|
|
||
|
|
<execution_context>
|
||
|
|
@/home/acty/.claude/get-shit-done/workflows/execute-plan.md
|
||
|
|
@/home/acty/.claude/get-shit-done/templates/summary.md
|
||
|
|
</execution_context>
|
||
|
|
|
||
|
|
<context>
|
||
|
|
@.planning/PROJECT.md
|
||
|
|
@.planning/ROADMAP.md
|
||
|
|
@.planning/phases/01-foundation/01-CONTEXT.md
|
||
|
|
@.planning/phases/01-foundation/01-RESEARCH.md
|
||
|
|
</context>
|
||
|
|
|
||
|
|
<tasks>
|
||
|
|
|
||
|
|
<task type="auto">
|
||
|
|
<name>Task 1: Initialize Go project with HTTP server</name>
|
||
|
|
<files>
|
||
|
|
go.mod
|
||
|
|
go.sum
|
||
|
|
cmd/server/main.go
|
||
|
|
internal/health/handler.go
|
||
|
|
</files>
|
||
|
|
<action>
|
||
|
|
1. Initialize Go module:
|
||
|
|
```bash
|
||
|
|
go mod init github.com/acty/pirate-station
|
||
|
|
```
|
||
|
|
(Use github.com path even though pushing to Gitea - conventional Go module naming)
|
||
|
|
|
||
|
|
2. Create cmd/server/main.go with:
|
||
|
|
- Import net/http and internal/health
|
||
|
|
- http.HandleFunc for "/" returning "Pirate Station API"
|
||
|
|
- http.HandleFunc for "/health" using health.Handler
|
||
|
|
- ListenAndServe on :32768
|
||
|
|
- log.Fatal on server error
|
||
|
|
- Log startup message with port number
|
||
|
|
|
||
|
|
3. Create internal/health/handler.go with:
|
||
|
|
- package health
|
||
|
|
- Handler(w http.ResponseWriter, r *http.Request) function
|
||
|
|
- Check os.Stat("/data") - return 503 if not exists
|
||
|
|
- Return 200 with {"status":"healthy"} JSON if /data exists
|
||
|
|
- Return 503 with {"status":"unhealthy","reason":"data volume not mounted"} if not
|
||
|
|
|
||
|
|
4. Run `go mod tidy` to create go.sum
|
||
|
|
|
||
|
|
Keep it minimal - no frameworks, no external dependencies, pure stdlib.
|
||
|
|
</action>
|
||
|
|
<verify>
|
||
|
|
```bash
|
||
|
|
go build -o /tmp/server ./cmd/server && echo "Build successful"
|
||
|
|
```
|
||
|
|
</verify>
|
||
|
|
<done>
|
||
|
|
- go.mod exists with module path
|
||
|
|
- cmd/server/main.go compiles
|
||
|
|
- internal/health/handler.go provides health check
|
||
|
|
- No external dependencies (only stdlib)
|
||
|
|
</done>
|
||
|
|
</task>
|
||
|
|
|
||
|
|
<task type="auto">
|
||
|
|
<name>Task 2: Create multi-stage Dockerfile</name>
|
||
|
|
<files>
|
||
|
|
docker/Dockerfile
|
||
|
|
.dockerignore
|
||
|
|
</files>
|
||
|
|
<action>
|
||
|
|
1. Create docker/Dockerfile with multi-stage build:
|
||
|
|
|
||
|
|
Build stage:
|
||
|
|
- FROM --platform=$BUILDPLATFORM golang:1.25-bookworm AS builder
|
||
|
|
- WORKDIR /build
|
||
|
|
- COPY go.mod go.sum ./ (cache dependencies separately)
|
||
|
|
- RUN go mod download
|
||
|
|
- COPY . .
|
||
|
|
- ARG TARGETOS TARGETARCH
|
||
|
|
- RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -ldflags="-w -s" -o /server ./cmd/server
|
||
|
|
|
||
|
|
Runtime stage:
|
||
|
|
- FROM debian:bookworm-slim
|
||
|
|
- RUN useradd -u 10001 -m appuser
|
||
|
|
- USER appuser
|
||
|
|
- COPY --from=builder /server /usr/local/bin/server
|
||
|
|
- VOLUME /data
|
||
|
|
- EXPOSE 32768
|
||
|
|
- CMD ["server"]
|
||
|
|
|
||
|
|
2. Create .dockerignore at project root:
|
||
|
|
```
|
||
|
|
.git
|
||
|
|
.gitignore
|
||
|
|
README.md
|
||
|
|
*.md
|
||
|
|
.env
|
||
|
|
.env.local
|
||
|
|
.DS_Store
|
||
|
|
.air.toml
|
||
|
|
docker-compose.yml
|
||
|
|
.planning/
|
||
|
|
tmp/
|
||
|
|
```
|
||
|
|
|
||
|
|
Key points:
|
||
|
|
- Use --platform=$BUILDPLATFORM for native build speed
|
||
|
|
- CGO_ENABLED=0 for static binary (no libc dependency)
|
||
|
|
- -ldflags="-w -s" strips debug symbols (smaller binary)
|
||
|
|
- Non-root user (appuser) for security
|
||
|
|
- VOLUME /data declares mount point
|
||
|
|
- Port 32768 as per user decision
|
||
|
|
</action>
|
||
|
|
<verify>
|
||
|
|
```bash
|
||
|
|
docker build -f docker/Dockerfile -t pirate-station:test . && docker images pirate-station:test --format "{{.Size}}"
|
||
|
|
```
|
||
|
|
Image should build and be under 150MB.
|
||
|
|
</verify>
|
||
|
|
<done>
|
||
|
|
- docker/Dockerfile exists with multi-stage build
|
||
|
|
- .dockerignore excludes non-essential files
|
||
|
|
- Image builds successfully
|
||
|
|
- Image size under 150MB (debian slim + Go binary)
|
||
|
|
</done>
|
||
|
|
</task>
|
||
|
|
|
||
|
|
</tasks>
|
||
|
|
|
||
|
|
<verification>
|
||
|
|
1. Go project compiles: `go build ./...`
|
||
|
|
2. Docker image builds: `docker build -f docker/Dockerfile -t pirate-station:test .`
|
||
|
|
3. Container starts: `docker run --rm -v /tmp/test-data:/data pirate-station:test &` then `curl localhost:32768/health`
|
||
|
|
4. Container cannot access host filesystem outside /data mount
|
||
|
|
</verification>
|
||
|
|
|
||
|
|
<success_criteria>
|
||
|
|
- Go module initialized with github.com/acty/pirate-station
|
||
|
|
- HTTP server listens on :32768
|
||
|
|
- /health endpoint returns JSON status
|
||
|
|
- Dockerfile produces working image under 150MB
|
||
|
|
- Image runs as non-root user
|
||
|
|
- No external Go dependencies (stdlib only)
|
||
|
|
</success_criteria>
|
||
|
|
|
||
|
|
<output>
|
||
|
|
After completion, create `.planning/phases/01-foundation/01-01-SUMMARY.md`
|
||
|
|
</output>
|