refactor: adopt portigo infrastructure patterns

Add config package with build-tag-switched dev/prod environments,
structured logging via zerolog, Taskfile for dev workflow, golangci-lint
config, testutil package, and improved DB setup with proper SQLite
pragmas and cleanup. Rename sqlc output package from gen to repository.

Switch to allowlist .gitignore, Alpine+UPX+scratch Dockerfile, and
CI pipeline with test/lint gates before deploy.
This commit is contained in:
Ryan Hamamura
2026-03-02 11:48:47 -10:00
parent 6d4f3eb821
commit 2df20c2840
27 changed files with 694 additions and 143 deletions

View File

@@ -5,17 +5,17 @@ import (
"database/sql"
"slices"
"github.com/ryanhamamura/c4/db/gen"
"github.com/ryanhamamura/c4/db/repository"
"github.com/ryanhamamura/c4/game"
"github.com/ryanhamamura/c4/snake"
"github.com/ryanhamamura/c4/ui"
)
type GamePersister struct {
queries *gen.Queries
queries *repository.Queries
}
func NewGamePersister(q *gen.Queries) *GamePersister {
func NewGamePersister(q *repository.Queries) *GamePersister {
return &GamePersister{queries: q}
}
@@ -24,7 +24,7 @@ func (p *GamePersister) SaveGame(g *game.Game) error {
_, err := p.queries.GetGame(ctx, g.ID)
if err == sql.ErrNoRows {
_, err = p.queries.CreateGame(ctx, gen.CreateGameParams{
_, err = p.queries.CreateGame(ctx, repository.CreateGameParams{
ID: g.ID,
Board: g.BoardToJSON(),
CurrentTurn: int64(g.CurrentTurn),
@@ -51,7 +51,7 @@ func (p *GamePersister) SaveGame(g *game.Game) error {
rematchGameID = sql.NullString{String: *g.RematchGameID, Valid: true}
}
return p.queries.UpdateGame(ctx, gen.UpdateGameParams{
return p.queries.UpdateGame(ctx, repository.UpdateGameParams{
Board: g.BoardToJSON(),
CurrentTurn: int64(g.CurrentTurn),
Status: int64(g.Status),
@@ -100,7 +100,7 @@ func (p *GamePersister) SaveGamePlayer(gameID string, player *game.Player, slot
guestPlayerID = sql.NullString{String: string(player.ID), Valid: true}
}
return p.queries.CreateGamePlayer(ctx, gen.CreateGamePlayerParams{
return p.queries.CreateGamePlayer(ctx, repository.CreateGamePlayerParams{
GameID: gameID,
UserID: userID,
GuestPlayerID: guestPlayerID,
@@ -144,10 +144,10 @@ func (p *GamePersister) DeleteGame(id string) error {
// SnakePersister implements snake.Persister
type SnakePersister struct {
queries *gen.Queries
queries *repository.Queries
}
func NewSnakePersister(q *gen.Queries) *SnakePersister {
func NewSnakePersister(q *repository.Queries) *SnakePersister {
return &SnakePersister{queries: q}
}
@@ -167,7 +167,7 @@ func (p *SnakePersister) SaveSnakeGame(sg *snake.SnakeGame) error {
_, err := p.queries.GetSnakeGame(ctx, sg.ID)
if err == sql.ErrNoRows {
_, err = p.queries.CreateSnakeGame(ctx, gen.CreateSnakeGameParams{
_, err = p.queries.CreateSnakeGame(ctx, repository.CreateSnakeGameParams{
ID: sg.ID,
Board: boardJSON,
Status: int64(sg.Status),
@@ -192,7 +192,7 @@ func (p *SnakePersister) SaveSnakeGame(sg *snake.SnakeGame) error {
rematchGameID = sql.NullString{String: *sg.RematchGameID, Valid: true}
}
return p.queries.UpdateSnakeGame(ctx, gen.UpdateSnakeGameParams{
return p.queries.UpdateSnakeGame(ctx, repository.UpdateSnakeGameParams{
Board: boardJSON,
Status: int64(sg.Status),
WinnerUserID: winnerUserID,
@@ -247,7 +247,7 @@ func (p *SnakePersister) SaveSnakePlayer(gameID string, player *snake.Player) er
guestPlayerID = sql.NullString{String: string(player.ID), Valid: true}
}
return p.queries.CreateSnakePlayer(ctx, gen.CreateSnakePlayerParams{
return p.queries.CreateSnakePlayer(ctx, repository.CreateSnakePlayerParams{
GameID: gameID,
UserID: userID,
GuestPlayerID: guestPlayerID,
@@ -290,15 +290,15 @@ func (p *SnakePersister) DeleteSnakeGame(id string) error {
}
type ChatPersister struct {
queries *gen.Queries
queries *repository.Queries
}
func NewChatPersister(q *gen.Queries) *ChatPersister {
func NewChatPersister(q *repository.Queries) *ChatPersister {
return &ChatPersister{queries: q}
}
func (p *ChatPersister) SaveChatMessage(gameID string, msg ui.C4ChatMessage) error {
return p.queries.CreateChatMessage(context.Background(), gen.CreateChatMessageParams{
return p.queries.CreateChatMessage(context.Background(), repository.CreateChatMessageParams{
GameID: gameID,
Nickname: msg.Nickname,
Color: int64(msg.Color),