$ make migration name=create_example_table
run: go run ./cmd/api psql: psql ${GREENLIGHT_DB_DSN} migration: @echo 'Creating migration files for ${name}...' migrate create -seq -ext=.sql -dir=./migrations ${name} up: @echo 'Running up migrations...' migrate -path ./migrations -database ${GREENLIGHT_DB_DSN} up
run/api: go run ./cmd/api db/psql: psql ${GREENLIGHT_DB_DSN} db/migrations/new: @echo 'Creating migration files for ${name}...' migrate create -seq -ext=.sql -dir=./migrations ${name} db/migrations/up: @echo 'Running up migrations...' migrate -path ./migrations -database ${GREENLIGHT_DB_DSN} up
# Create the new confirm target. confirm: @echo -n 'Are you sure? [y/N] ' && read ans && [ $${ans:-N} = y ] # Include it as prerequisite. db/migrations/up: confirm @echo 'Running up migrations...' migrate -path ./migrations -database ${GREENLIGHT_DB_DSN} up
## help: print this help message help: @echo 'Usage:' @sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /' confirm: @echo -n 'Are you sure? [y/N] ' && read ans && [ $${ans:-N} = y ] ## run/api: run the cmd/api application run/api: go run ./cmd/api ## db/psql: connect to the database using psql db/psql: psql ${GREENLIGHT_DB_DSN} ## db/migrations/new name=$1: create a new database migration db/migrations/new: @echo 'Creating migration files for ${name}...' migrate create -seq -ext=.sql -dir=./migrations ${name} ## db/migrations/up: apply all up database migrations db/migrations/up: confirm @echo 'Running up migrations...' migrate -path ./migrations -database ${GREENLIGHT_DB_DSN} up
## help: print this help message .PHONY: help: @echo 'Usage:' @sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /' .PHONY: confirm: @echo -n 'Are you sure? [y/N] ' && read ans && [ $${ans:-N} = y ] ## run/api: run the cmd/api application .PHONY: run/api: go run ./cmd/api ## db/psql: connect to the database using psql .PHONY: db/psql: psql ${GREENLIGHT_DB_DSN} ## db/migrations/new name=$1: create a new database migration .PHONY: db/migrations/new: @echo 'Creating migration files for ${name}...' migrate create -seq -ext=.sql -dir=./migrations ${name} ## db/migrations/up: apply all up database migrations .PHONY: db/migrations/up: confirm @echo 'Running up migrations...' migrate -path ./migrations -database ${GREENLIGHT_DB_DSN} up
zzh@ZZHPC:/zdata/Github/greenlight$ go install honnef.co/go/tools/cmd/staticcheck@latest zzh@ZZHPC:/zdata/Github/greenlight$ which staticcheck /home/zzh/.goenv/shims/staticcheck
The /home/zzh/.goenv/shims/staticcheck was installed by goenv.
The staticcheck installed by the above 'go install' command is here:
zzh@ZZHPC:~/.goenv/shims$ go env GOPATH /home/zzh/go/1.23.0 zzh@ZZHPC:~/.goenv/shims$ cd /home/zzh/go/1.23.0/bin zzh@ZZHPC:~/go/1.23.0/bin$ lh staticcheck -rwxrwxr-x 1 zzh zzh 14M Nov 29 18:41 staticcheck
# ==================================================================================== # # QUALITY CONTROL # ==================================================================================== # ## tidy: format all .go files and tidy module dependencies # @echo 'Formatting .go files ...' # go fmt ./... tidy: @echo 'Tidying module dependencies ...' go mod tidy ## audit: run quality control checks audit: @echo 'Checking module dependencies' go mod tidy -diff go mod verify @echo 'Vetting code ...' go vet ./... staticcheck ./... @echo 'Running tests ...' go test -race -vet=off ./... .PHONY: tidy audit
zzh@ZZHPC:~/zd/Github/greenlight$ make tidy Tidying module dependencies ... go mod tidy zzh@ZZHPC:~/zd/Github/greenlight$ make audit Checking module dependencies go mod tidy -diff go mod verify all modules verified Vetting code ... go vet ./... staticcheck ./... Running tests ... go test -race -vet=off ./... ? greenlight.zzh.net/cmd/api [no test files] ? greenlight.zzh.net/cmd/examples/cors/preflight [no test files] ? greenlight.zzh.net/cmd/examples/cors/simple [no test files] ? greenlight.zzh.net/internal/config [no test files] ? greenlight.zzh.net/internal/data [no test files] ? greenlight.zzh.net/internal/mail [no test files] ? greenlight.zzh.net/internal/validator [no test files]
zzh@ZZHPC:~/zd/Github/greenlight$ go env GO111MODULE='on' GOARCH='amd64' GOBIN='' GOCACHE='/home/zzh/.cache/go-build' GOENV='/home/zzh/.config/go/env' GOEXE='' GOEXPERIMENT='' GOFLAGS='' GOHOSTARCH='amd64' GOHOSTOS='linux' GOINSECURE='' GOMODCACHE='/home/zzh/go/1.23.0/pkg/mod' GONOPROXY='' GONOSUMDB='' GOOS='linux' GOPATH='/home/zzh/go/1.23.0' GOPRIVATE='' GOPROXY='https://goproxy.io,direct' GOROOT='/home/zzh/.goenv/versions/1.23.0' GOSUMDB='sum.golang.org' GOTMPDIR='' GOTOOLCHAIN='auto' GOTOOLDIR='/home/zzh/.goenv/versions/1.23.0/pkg/tool/linux_amd64' GOVCS='' GOVERSION='go1.23.0' GODEBUG='' GOTELEMETRY='local' GOTELEMETRYDIR='/home/zzh/.config/go/telemetry' GCCGO='gccgo' GOAMD64='v1' AR='ar' CC='gcc' CXX='g++' CGO_ENABLED='1' GOMOD='/home/zzh/zd/Github/greenlight/go.mod' GOWORK='/home/zzh/zd/Github/go.work' CGO_CFLAGS='-O2 -g' CGO_CPPFLAGS='' CGO_CXXFLAGS='-O2 -g' CGO_FFLAGS='-O2 -g' CGO_LDFLAGS='-O2 -g' PKG_CONFIG='pkg-config' GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build506883647=/tmp/go-build -gno-record-gcc-switches'
## tidy: format all .go files and tidy module dependencies # @echo 'Formatting .go files ...' # go fmt ./... tidy: @echo 'Tidying module dependencies ...' go mod tidy @echo 'Verifying and vendoring module dependencies ...' go mod verify go mod vendor
func (app *application) rateLimit(next http.Handler) http.Handler { type client struct { limiter *rate.Limiter lastSeen time.Time } var ( mu sync.Mutex clients = make(map[string]*client) ) // Launch a background goroutine which removes old entries from the clients map // once every minute. go func() { for { time.Sleep(time.Minute) mu.Lock() for ip, client := range clients { if time.Since(client.lastSeen) > 3*time.Minute { delete(clients, ip) } } mu.Unlock() } }() return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if app.config.limiter.Enabled { // Use the realip.FromRequest() function to ge the client's real IP address. ip := realip.FromRequest(r) mu.Lock() if _, found := clients[ip]; !found { clients[ip] = &client{ limiter: rate.NewLimiter(rate.Limit(app.config.limiter.Rps), app.config.limiter.Burst), } } clients[ip].lastSeen = time.Now() if !clients[ip].limiter.Allow() { mu.Unlock() app.rateLimitExceededResponse(w, r) return } mu.Unlock() } next.ServeHTTP(w, r) }) }
due to any problems that might exist within those vendored packages.
# ==================================================================================== # # BUILD # ==================================================================================== # ## build/api: build the cmd/api application build/api: @echo 'Building cmd/api ...' go build -o=./bin/api ./cmd/api
zzh@ZZHPC:~/zd/Github/greenlight$ make build/api Building cmd/api ... go build -o=./bin/api ./cmd/api zzh@ZZHPC:~/zd/Github/greenlight$ ./bin/api time=2024-11-30T12:08:43.077+08:00 level=INFO msg="database connection pool established" time=2024-11-30T12:08:43.077+08:00 level=INFO msg="starting server" addr=:4000 env=development
zzh@ZZHPC:~/zd/Github/greenlight$ lh bin/api -rwxrwxr-x 1 zzh zzh 19M Nov 30 12:08 bin/api
build/api: @echo 'Building cmd/api ...' go build -ldflags='-s' -o=./bin/api ./cmd/api
zzh@ZZHPC:~/zd/Github/greenlight$ make build/api Building cmd/api ... go build -ldflags='-s' -o=./bin/api ./cmd/api zzh@ZZHPC:~/zd/Github/greenlight$ lh bin/api -rwxrwxr-x 1 zzh zzh 13M Nov 30 12:15 bin/api
zzh@ZZHPC:~/zd/Github/greenlight$ go tool dist list aix/ppc64 android/386 android/amd64 android/arm android/arm64 darwin/amd64 darwin/arm64 dragonfly/amd64 freebsd/386 freebsd/amd64 freebsd/arm freebsd/arm64 freebsd/riscv64 illumos/amd64 ios/amd64 ios/arm64 js/wasm linux/386 linux/amd64 linux/arm linux/arm64 linux/loong64 linux/mips linux/mips64 linux/mips64le linux/mipsle linux/ppc64 linux/ppc64le linux/riscv64 linux/s390x netbsd/386 netbsd/amd64 netbsd/arm netbsd/arm64 openbsd/386 openbsd/amd64 openbsd/arm openbsd/arm64 openbsd/ppc64 openbsd/riscv64 plan9/386 plan9/amd64 plan9/arm solaris/amd64 wasip1/wasm windows/386 windows/amd64 windows/arm windows/arm64
zzh@ZZHPC:~/zd/Github/greenlight$ go env GOCACHE /home/zzh/.cache/go-build
zzh@ZZHPC:~/zd/Github/greenlight$ make build/api Building cmd/api ... go build -ldflags='-s' -o=./bin/api ./cmd/api zzh@ZZHPC:~/zd/Github/greenlight$ ./bin/api -version Version: 1.0.0
zzh@ZZHPC:~/zd/Github/greenlight$ go version -m ./bin/api ./bin/api: go1.23.0 path greenlight.zzh.net/cmd/api mod greenlight.zzh.net (devel) dep github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= dep github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= dep github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= dep github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= dep github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs= dep github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= dep github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA= dep github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= dep github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= dep github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= dep github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= dep github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= dep github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= dep github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= dep github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= dep github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= dep github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= dep github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc= dep golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= dep golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= dep golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= dep golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= dep golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= dep gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= dep gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= build -buildmode=exe build -compiler=gc build -ldflags=-s build CGO_ENABLED=1 build CGO_CFLAGS= build CGO_CPPFLAGS= build CGO_CXXFLAGS= build CGO_LDFLAGS= build GOARCH=amd64 build GOOS=linux build GOAMD64=v1 build vcs=git build vcs.revision=2b72058b2f98710bbd13cba86a60a30f95cc0ab5 build vcs.time=2024-11-29T06:43:10Z build vcs.modified=true
package vcs import ( "fmt" "runtime/debug" ) // Version returns the vcs.revision of the build, adding a '-dirty' suffix // if the vcs.modified is true. func Version() string { var revision string var modified bool bi, ok := debug.ReadBuildInfo() if ok { for _, s := range bi.Settings { switch s.Key { case "vcs.revision": revision = s.Value case "vcs.modified": modified = true } } } if modified { return fmt.Sprintf("%s-dirty", revision) } return revision }
... var version = vcs.Version() ... func main() { ...
zzh@ZZHPC:~/zd/Github/greenlight$ make build/api Building cmd/api ... go build -ldflags='-s' -o=./bin/api ./cmd/api zzh@ZZHPC:~/zd/Github/greenlight$ ./bin/api -version Version: 2b72058b2f98710bbd13cba86a60a30f95cc0ab5-dirty
package vcs import ( "fmt" "runtime/debug" ) // Version returns the vcs.revision of the build, adding a '-dirty' suffix // if the vcs.modified is true. func Version() string { var ( time string revision string modified bool ) bi, ok := debug.ReadBuildInfo() if ok { for _, s := range bi.Settings { switch s.Key { case "vcs.time": time = s.Value case "vcs.revision": revision = s.Value case "vcs.modified": if s.Value == "true" { modified = true } } } } if modified { return fmt.Sprintf("%s-%s-dirty", time, revision) } return fmt.Sprintf("%s-%s", time, revision) }
zzh@ZZHPC:~/zd/Github/greenlight$ make build/api Building cmd/api ... go build -ldflags='-s' -o=./bin/api ./cmd/api zzh@ZZHPC:~/zd/Github/greenlight$ ./bin/api -version Version: 2024-11-29T06:43:10Z-2b72058b2f98710bbd13cba86a60a30f95cc0ab5-dirty