From fc0daf6a14bf123631ff149d924a15a237c74efa Mon Sep 17 00:00:00 2001 From: Ruidy Date: Sat, 30 Aug 2025 22:36:11 -0400 Subject: [PATCH] chore(build,ci,docs): switch to Makefile, consolidate CI, add caching + AGENTS (#47) --- .github/workflows/ci.yml | 73 ++++++++++++++++++++++++++-------------- AGENTS.md | 52 ++++++++++++++++++++++++++++ Dockerfile.dev | 21 +++++++----- Makefile | 43 +++++++++++++++++++++++ README.md | 11 ++++++ go.mod | 22 ++++++------ go.sum | 22 ++++++++++++ justfile | 46 ------------------------- 8 files changed, 199 insertions(+), 91 deletions(-) create mode 100644 AGENTS.md create mode 100644 Makefile delete mode 100644 justfile diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e82c7b9..306f26d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,41 +6,62 @@ on: - main pull_request: branches: - - main + - "**" jobs: - build: + checks: runs-on: ubuntu-latest env: - GO111MODULE: on - + NAME: rentease + PORT: 8000 + DB_USER: ci + DB_NAME: villafleurie steps: - - name: Checkout code - uses: actions/checkout@v3 + - name: Checkout + uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v5 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build dev image (cached) + uses: docker/build-push-action@v6 with: - go-version: 1.23 + context: . + file: Dockerfile.dev + tags: ${{ env.NAME }}:dev + load: true + cache-from: type=gha,scope=dev + cache-to: type=gha,scope=dev,mode=max - - name: Install dependencies + - name: Start dev container (background) run: | - go mod download + docker run -d \ + --name ${NAME} \ + -v "$GITHUB_WORKSPACE":/app \ + -v /app/tmp \ + ${NAME}:dev sleep infinity - - name: Lint code + - name: Make format + run: make format + + - name: Make lint + run: make lint + + - name: Make test + run: make test + + - name: Stop container + if: always() run: | - go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest - golangci-lint run + docker logs ${NAME} || true + docker stop ${NAME} || true - - name: Run tests - run: | - go test ./... - - # - name: Run Gosec Security Scanner - # uses: securego/gosec@master - # with: - # args: ./... - - - name: Build Docker image - run: | - docker build -t rentease . + - name: Build production image (cached, push only) + if: github.event_name == 'push' + uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile + tags: ${{ env.NAME }}:latest + cache-from: type=gha,scope=prod + cache-to: type=gha,scope=prod,mode=max diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..3e721f5 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,52 @@ +# Repository Guidelines + +## Project Structure & Module Organization + +- `main.go`: Application entrypoint (HTTP server on `:8000`). +- `internal/`: Private app code (e.g., `server/`, `service/`, `repository/`, `driver/`, + `config/`, `view/`). Templ views live in `internal/view` and generate `*_templ.go`. +- `pkg/`: Reusable packages shared across app layers. +- `cmd/`: Optional binaries/entrypoints. +- `assets/`: Static assets and images. +- `docs/`: Project documentation. +- `scripts/`, `tmp/`: Dev tooling and build artifacts (Air uses `tmp/`). + +## Build, Test, and Development Commands + +- `make dev`: Start dev container with live reload (Air) on `http://localhost:8000`. +- `make run`: Build and run the Docker image with `PORT` and `DATABASE_URL`. +- `make test`: Run `go test ./...` inside the running dev container. +- `make format`: Run `templ generate`, `templ fmt`, and `go fmt`. +- `make lint`: Run `golangci-lint`. +- Local alternative: `air -c .air.toml`, `go test ./...`, `go run .`. + +## Coding Style & Naming Conventions + +- Go formatting: Use `go fmt` (tabs, standard import ordering). CI helpers: + `make format`. +- Linting: `golangci-lint run ./...` via `make lint` (fix issues before PR). +- Packages: lowercase, no underscores; files: lowercase with underscores; exported + identifiers: `PascalCase`; unexported: `camelCase`. +- Templ: keep `.templ` in `internal/view`; commit sources, not generated `*_templ.go`. + +## Testing Guidelines + +- Framework: standard `testing` with table-driven tests. +- Files: `*_test.go`; functions: `TestXxx`, benchmarks: `BenchmarkXxx`. +- Run: `make test` (in container) or `go test ./...` locally. +- Aim for meaningful coverage on new/changed code; include error paths. + +## Commit & Pull Request Guidelines + +- Messages: Prefer Conventional Commits (e.g., `feat(parser): ...`, + `fix(config): ...`). Short, imperative first line; scope optional. +- PRs: Provide clear description, link issues (e.g., `#45`), include screenshots + for UI, and note breaking changes/migrations. +- Keep diffs focused; run `make format` and `make lint` before opening. + +## Security & Configuration Tips + +- Environment: Use `.env`/`prod.env`; never commit secrets. + Example: `DATABASE_URL="host=... user=... database=..."`. +- Ports: default `8000`; configure via `PORT`. +- Dependencies: use `go mod tidy` and `make up-deps` when updating. diff --git a/Dockerfile.dev b/Dockerfile.dev index 7653a23..37f3cf2 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -2,19 +2,24 @@ FROM golang:1.24-alpine AS builder WORKDIR /app -RUN apk update && apk add --no-cache build-base +RUN apk add --no-cache build-base +# Install tooling early so it stays cached across source changes +RUN go install github.com/air-verse/air@latest \ + && go install github.com/a-h/templ/cmd/templ@latest + +# Leverage module cache COPY go.mod go.sum ./ -RUN go mod download +RUN --mount=type=cache,target=/go/pkg/mod \ + go mod download +# Copy the rest of the sources COPY . . -RUN go build -ldflags="-s -w" -o rentease main.go - -# Install air and templ for live reload and templating in dev -RUN go install github.com/air-verse/air@latest \ - && go install github.com/a-h/templ/cmd/templ@latest \ - && rm -rf /go/pkg/mod /root/.cache/go-build +# Build once (helps verify builds and speeds CI with cache) +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg/mod \ + go build -ldflags="-s -w" -o rentease main.go # ----------- Dev Stage ----------- FROM golang:1.24-alpine AS dev diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6abaa0e --- /dev/null +++ b/Makefile @@ -0,0 +1,43 @@ +# Defaults (override via `make VAR=value`) +NAME ?= rentease +PORT ?= 8000 +DB_USER ?= ruidy +DB_NAME ?= villafleurie + +DOCKER_RUN_ENV = -e DATABASE_URL="host=docker.for.mac.host.internal user=$(DB_USER) database=$(DB_NAME)" -e PORT=$(PORT) + +.PHONY: help build run dev test up-deps format lint stop + +help: ## List available commands + @grep -E '^[a-zA-Z_-]+:.*?## ' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " %-12s %s\n", $$1, $$2}' + +build: format lint ## Build the production Docker image + docker build -t $(NAME):latest . + +run: build ## Run the production container (port $(PORT)) + docker run -p $(PORT):$(PORT) $(DOCKER_RUN_ENV) $(NAME) + +dev: ## Build and run the dev container with live reload (Air) + docker build -t $(NAME):dev -f Dockerfile.dev . + docker run -p $(PORT):$(PORT) --rm \ + -v `pwd`:/app -v /app/tmp \ + --name $(NAME) \ + $(DOCKER_RUN_ENV) $(NAME):dev + +test: ## Run Go tests inside the running dev container + docker exec $(NAME) go test ./... + +up-deps: ## Update Go dependencies on host + go get -u ./... + +format: ## Generate templ files and format code (dev container must be running) + docker exec $(NAME) templ generate internal/view + docker exec $(NAME) templ fmt . + docker exec $(NAME) go fmt ./... + +lint: ## Lint the code using golangci-lint (dev container must be running) + docker exec $(NAME) golangci-lint run ./... + +stop: ## Stop the dev container + -@docker stop $(NAME) >/dev/null 2>&1 || true + diff --git a/README.md b/README.md index 260a439..1bf54d8 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,17 @@ use a cloud alternative such as Railway, fly.io, _etc._ make dev ``` +### Development Commands + +Use the included Makefile for common tasks: + +- `make dev`: Run the dev container with live reload (Air) on `http://localhost:8000`. +- `make run`: Build and run the production image locally. +- `make test`: Run `go test ./...` inside the dev container. +- `make format`: Generate templ files and format code. +- `make lint`: Lint with `golangci-lint`. +- `make stop`: Stop the dev container. + 6. **Access the application**: Open your browser and go to `http://localhost:8000` to start using Rentease. diff --git a/go.mod b/go.mod index f625a8d..3ef896e 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/rjNemo/rentease go 1.24.2 require ( - github.com/a-h/templ v0.3.906 - github.com/getsentry/sentry-go v0.34.0 - github.com/getsentry/sentry-go/echo v0.34.0 + github.com/a-h/templ v0.3.943 + github.com/getsentry/sentry-go v0.35.1 + github.com/getsentry/sentry-go/echo v0.35.1 github.com/gorilla/sessions v1.4.0 github.com/joho/godotenv v1.5.1 github.com/labstack/echo-contrib v0.17.4 @@ -13,7 +13,7 @@ require ( github.com/labstack/gommon v0.4.2 github.com/rjNemo/underscore v0.7.0 gorm.io/driver/postgres v1.6.0 - gorm.io/gorm v1.30.0 + gorm.io/gorm v1.30.2 ) require ( @@ -35,15 +35,15 @@ require ( github.com/jinzhu/now v1.1.5 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/openai/openai-go v1.8.1 + github.com/openai/openai-go v1.12.0 github.com/sethvargo/go-envconfig v1.3.0 github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect - golang.org/x/crypto v0.39.0 // indirect - golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect - golang.org/x/net v0.41.0 // indirect - golang.org/x/sync v0.15.0 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.26.0 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect golang.org/x/time v0.12.0 // indirect ) diff --git a/go.sum b/go.sum index 516efe6..d80bdf7 100644 --- a/go.sum +++ b/go.sum @@ -2,13 +2,19 @@ github.com/a-h/templ v0.3.898 h1:g9oxL/dmM6tvwRe2egJS8hBDQTncokbMoOFk1oJMX7s= github.com/a-h/templ v0.3.898/go.mod h1:oLBbZVQ6//Q6zpvSMPTuBK0F3qOtBdFBcGRspcT+VNQ= github.com/a-h/templ v0.3.906 h1:ZUThc8Q9n04UATaCwaG60pB1AqbulLmYEAMnWV63svg= github.com/a-h/templ v0.3.906/go.mod h1:FFAu4dI//ESmEN7PQkJ7E7QfnSEMdcnu7QrAY8Dn334= +github.com/a-h/templ v0.3.943 h1:o+mT/4yqhZ33F3ootBiHwaY4HM5EVaOJfIshvd5UNTY= +github.com/a-h/templ v0.3.943/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/getsentry/sentry-go v0.34.0 h1:1FCHBVp8TfSc8L10zqSwXUZNiOSF+10qw4czjarTiY4= github.com/getsentry/sentry-go v0.34.0/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE= +github.com/getsentry/sentry-go v0.35.1 h1:iopow6UVLE2aXu46xKVIs8Z9D/YZkJrHkgozrxa+tOQ= +github.com/getsentry/sentry-go v0.35.1/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE= github.com/getsentry/sentry-go/echo v0.34.0 h1:qjEOup0MJ4qyNKEc/H1XUooIQndka0HOGDMFbLR3x8E= github.com/getsentry/sentry-go/echo v0.34.0/go.mod h1:kCjZ3/HnI340yMESlyRRYoL/7stfCkuxakhFkrR2nxk= +github.com/getsentry/sentry-go/echo v0.35.1 h1:MIhSUyo7cpCdcw0/lIeAw5fukrDt3x9G7qbiyjbVllI= +github.com/getsentry/sentry-go/echo v0.35.1/go.mod h1:IjdEzgvwlP2/7A32tWk75UmSUsBqvKFdpkN6WhB1e6M= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= @@ -49,6 +55,8 @@ github.com/openai/openai-go v1.6.0 h1:KGjDS5sDrO27vykzO50BYknuabzVxuFuwAB8Djrmex github.com/openai/openai-go v1.6.0/go.mod h1:g461MYGXEXBVdV5SaR/5tNzNbSfwTBBefwc+LlDCK0Y= github.com/openai/openai-go v1.8.1 h1:mGS5Y9dEeHvLnE3k9LF4vUV3pvYG2K/6MHI/fCr4Ou8= github.com/openai/openai-go v1.8.1/go.mod h1:g461MYGXEXBVdV5SaR/5tNzNbSfwTBBefwc+LlDCK0Y= +github.com/openai/openai-go v1.12.0 h1:NBQCnXzqOTv5wsgNC36PrFEiskGfO5wccfCWDo9S1U0= +github.com/openai/openai-go v1.12.0/go.mod h1:g461MYGXEXBVdV5SaR/5tNzNbSfwTBBefwc+LlDCK0Y= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -82,17 +90,29 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= +golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0= +golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4= golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -103,3 +123,5 @@ gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4= gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo= gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs= gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= +gorm.io/gorm v1.30.2 h1:f7bevlVoVe4Byu3pmbWPVHnPsLoWaMjEb7/clyr9Ivs= +gorm.io/gorm v1.30.2/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= diff --git a/justfile b/justfile deleted file mode 100644 index d5ab7ef..0000000 --- a/justfile +++ /dev/null @@ -1,46 +0,0 @@ -# Set default values -name := "rentease" -port := "8000" -db_user := "ruidy" -db_name := "villafleurie" - -# List available recipes -default: - @just --list - -# Build the binary -build: format lint - @docker build -t {{name}}:latest . - -# Run the binary -run: build - @docker run -p {{port}}:{{port}} \ - -e DATABASE_URL="host=docker.for.mac.host.internal user={{db_user}} database={{db_name}}" \ - -e PORT={{port}} {{name}} - -# Run the binary in dev mode -dev: - @docker build -t {{name}}:dev -f Dockerfile.dev . - @docker run -p {{port}}:{{port}} --rm \ - -v `pwd`:/app -v /app/tmp \ - --name {{name}} \ - -e DATABASE_URL="host=docker.for.mac.host.internal user={{db_user}} database={{db_name}}" \ - -e PORT={{port}} {{name}}:dev - -# Run the tests -test: - @docker exec {{name}} go test ./... - -# Update dependencies -up-deps: - @go get -u ./... - -# Format the code -format: - @docker exec {{name}} templ generate internal/view - @docker exec {{name}} templ fmt . - @docker exec {{name}} go fmt ./... - -# Lint the code -lint: - @docker exec {{name}} golangci-lint run ./...