diff --git a/.gitignore b/.gitignore index 4c49bd7..51a41ee 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .env +bin/ diff --git a/Dockerfile b/Dockerfile index b5d75a9..e84b8cb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,15 +7,16 @@ RUN go mod download COPY "./internal" "./internal" COPY "./cmd" "./cmd" +COPY "Makefile" "./Makefile" -RUN go build -o ./server github.com/robindittmar/dttmr-api/cmd/api-server +RUN make build FROM debian:latest WORKDIR /app COPY .env.docker .env -COPY --from=build /app/server /app/server +COPY --from=build /app/bin/dttmr-api /app/dttmr-api EXPOSE 8080 -CMD ["/app/server"] +CMD ["/app/dttmr-api"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f6787b2 --- /dev/null +++ b/Makefile @@ -0,0 +1,84 @@ +# Variables +APP_NAME := dttmr-api +MAIN_DIR := ./cmd/api +BIN_DIR := ./bin + +# Dynamically pull version and commit hash from Git +VERSION := $(shell git describe --tags --always --dirty) +COMMIT := $(shell git rev-parse --short HEAD) +BUILD_TIME := $(shell date -u +"%Y-%m-%dT%H:%M:%SZ") + +# Linker flags to strip symbols (-s -w) and inject build data into the binary +LDFLAGS := -w -s \ + -X 'main.Version=$(VERSION)' \ + -X 'main.Commit=$(COMMIT)' \ + -X 'main.BuildTime=$(BUILD_TIME)' + +# Targets + +.PHONY: all help build run test test-cover lint fmt mod clean docker + +all: help + +## help: Show this help message +help: + @echo "Usage: make [target]" + @echo "" + @echo "Targets:" + @sed -n 's/^##//p' $(MAKEFILE_LIST) | column -t -s ':' | sed -e 's/^/ /' + +## build: Compile the binary +build: + @echo "Building $(APP_NAME) version $(VERSION)..." + @CGO_ENABLED=0 go build -ldflags="$(LDFLAGS)" -o $(BIN_DIR)/$(APP_NAME) $(MAIN_DIR) + +## run: Run the application directly +run: build + @echo "Starting $(APP_NAME)..." + @$(BIN_DIR)/$(APP_NAME) + +## test: Run tests with race detector +test: + @echo "Running tests..." + @go test -v -race -timeout 30s ./... + +## test-cover: Run tests and generate coverage report +test-cover: + @echo "Running tests with coverage..." + @go test -coverprofile=coverage.out ./... + @go tool cover -html=coverage.out -o coverage.html + @echo "Coverage report generated at coverage.html" + +## lint: Run golangci-lint +lint: + @echo "Running linter..." + @golangci-lint run ./... + +## fmt: Format code and organize imports +fmt: + @echo "Formatting code..." + @go fmt ./... + @go run golang.org/x/tools/cmd/goimports@latest -w . + +## mod: Tidy and verify dependencies +mod: + @echo "Tidying and verifying module dependencies..." + @go mod tidy + @go mod verify + +## clean: Remove build artifacts +clean: + @echo "Cleaning up..." + @rm -rf $(BIN_DIR) + @rm -f coverage.out coverage.html + @go clean + +## docker: Build a Docker image +docker: + @echo "Building Docker image for $(APP_NAME):$(VERSION)..." + @docker build -t $(APP_NAME):$(VERSION) -t $(APP_NAME):latest . + +## swag: Create swagger documentation +swag: + @echo "Creating swagger documentation for $(APP_NAME)" + @swag init --outputTypes json,yaml -g $(MAIN_DIR)/main.go diff --git a/cmd/api-server/main.go b/cmd/api/main.go similarity index 87% rename from cmd/api-server/main.go rename to cmd/api/main.go index ef53b8c..5771715 100644 --- a/cmd/api-server/main.go +++ b/cmd/api/main.go @@ -19,6 +19,12 @@ import ( "github.com/robindittmar/dttmr-api/internal/telemetry" ) +var ( + Version = "dev" + Commit = "none" // In the Makefile, we called this 'Commit' instead of 'Hash' + BuildTime = "unknown" +) + // @title dttmr-api // @version 0.1.0 // @description API documentation for dttmr-api service. @@ -37,29 +43,32 @@ import ( // @in Header // @name Authorization func main() { - serviceName := "dttmr-api" - serviceVersion := "0.1.0" - - if err := run(serviceName, serviceVersion); err != nil { + if err := run(); err != nil { slog.Error("service crashed") os.Exit(1) } } -func run(serviceName string, serviceVersion string) error { +func run() error { + serivceName := "dttmr-api" time.Local, _ = time.LoadLocation("UTC") _ = godotenv.Load(".env") setupLogging() - slog.Info("starting service", slog.String("service", serviceName), slog.String("version", serviceVersion)) + slog.Info("starting service", + slog.String("service", serivceName), + slog.String("version", Version), + slog.String("commit", Commit), + slog.String("build_time", BuildTime), + ) defer slog.Info("service shutdown!") cfg := config.Load() telCfg := telemetry.Config{ - ServiceName: serviceName, - ServiceVersion: serviceVersion, + ServiceName: serivceName, + ServiceVersion: Version, Endpoint: cfg.OTLPEndpoint, Environment: cfg.Environment, } diff --git a/internal/api/handler/list.go b/internal/api/handler/list.go index 7354a07..1295724 100644 --- a/internal/api/handler/list.go +++ b/internal/api/handler/list.go @@ -104,7 +104,7 @@ func (h *ListHandler) AddUserToList(w http.ResponseWriter, r *http.Request) { // @Tags List // @Accept json // @Produce json -// @Param payload body request.RemoveUserFromList true "Remove user from list payload" +// @Param payload body request.RemoveUserFromListPayload true "Remove user from list payload" // @Success 204 {object} nil // @Error 400 {object} response.ErrorResponse "failed to decode request body" // @Error 500 {object} response.ErrorResponse "failed to remove user from list"