From bd943f42ab4e49a0c681424d13452a4811ccec0b Mon Sep 17 00:00:00 2001 From: Reto Gantenbein Date: Sat, 9 May 2026 23:55:51 +0200 Subject: [PATCH 1/4] Makefile: Use local copy of CI tools --- Makefile | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index c8d4cda..56ae7b6 100644 --- a/Makefile +++ b/Makefile @@ -5,8 +5,6 @@ REVISION ?= $(shell git rev-parse HEAD) BUILD_DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ) ARCHS ?= $(shell go env GOARCH) -BIN_DIR := bin/ - LDFLAGS_PKG := github.com/ganto/pkgproxy/cmd LDFLAGS := -X $(LDFLAGS_PKG).Version=$(VERSION) -X $(LDFLAGS_PKG).GitCommit=$(REVISION) -X $(LDFLAGS_PKG).BuildDate=$(BUILD_DATE) @@ -16,6 +14,11 @@ GO_INSTALL_ARGS_EXTRA := GO_BUILD_ARGS := -a -v -trimpath GO_BUILD_ARGS_EXTRA := +## Location to install dependencies to +LOCALBIN ?= $(shell pwd)/bin +$(LOCALBIN): + mkdir -p $(LOCALBIN) + # Vet: https://pkg.go.dev/cmd/vet GO_VET_ARGS := @@ -48,12 +51,13 @@ help: ## Display this help ## Format sources files with goimports .PHONY: format +GOIMPORTS_VERSION ?= latest format: ## Format source files with `goimports` $(info ****************************************************) $(info ********** EXECUTING 'format' MAKE TARGET **********) $(info ****************************************************) - @command -v goimports 2>&1 >/dev/null || go install $(GO_INSTALL_ARGS) $(GO_INSTALL_ARGS_EXTRA) golang.org/x/tools/cmd/goimports@latest - goimports -w $(SOURCE) + test -s $(LOCALBIN)/goimports || GOBIN=$(LOCALBIN) go install $(GO_INSTALL_ARGS) $(GO_INSTALL_ARGS_EXTRA) golang.org/x/tools/cmd/goimports@$(GOIMPORTS_VERSION) + $(LOCALBIN)/goimports -w $(SOURCE) .PHONY: vet vet: ## Run go vet against code @@ -70,12 +74,13 @@ generate: ## Run code generation (if required) go generate -v ./... .PHONY: ci-lint +GOLANGCILINT_VERSION ?= latest ci-lint: ## Run all lint related tests against the codebase (will use the .golangci.yml config) $(info *****************************************************) $(info ********** EXECUTING 'ci-lint' MAKE TARGET **********) $(info *****************************************************) - @command -v golangci-lint 2>&1 >/dev/null || go install $(GO_INSTALL_ARGS) $(GO_INSTALL_ARGS_EXTRA) github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest - golangci-lint -v run + test -s $(LOCALBIN)/golangci-lint || GOBIN=$(LOCALBIN) go install $(GO_INSTALL_ARGS) $(GO_INSTALL_ARGS_EXTRA) github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCILINT_VERSION) + $(LOCALBIN)/golangci-lint -v run .PHONY: ci-check ci-check: ci-lint govulncheck test ## Run various check commands intended for CI use (lint, govulncheck, test, ...) @@ -88,7 +93,7 @@ lint-fix: ci-lint ## Run golangci-lint linter and perform fixes $(info ******************************************************) $(info ********** EXECUTING 'lint-fix' MAKE TARGET **********) $(info ******************************************************) - golangci-lint -v run --fix + $(LOCALBIN)/golangci-lint -v run --fix .PHONY: test test: ## Run the tests against the codebase @@ -127,12 +132,13 @@ coverage: ## Generates test coverage report go test ./... -coverpkg=./... -coverprofile=coverage.out .PHONY: govulncheck +GOVULNCHECK_VERSION ?= latest govulncheck: ## Run Go vulnerability check $(info *********************************************************) $(info ********** EXECUTING 'govulncheck' MAKE TARGET **********) $(info *********************************************************) - @command -v govulncheck 2>&1 >/dev/null || go install $(GO_INSTALL_ARGS) $(GO_INSTALL_ARGS_EXTRA) golang.org/x/vuln/cmd/govulncheck@latest - govulncheck -show verbose ./... || true + test -s $(LOCALBIN)/govulncheck || GOBIN=$(LOCALBIN) go install $(GO_INSTALL_ARGS) $(GO_INSTALL_ARGS_EXTRA) golang.org/x/vuln/cmd/govulncheck@$(GOVULNCHECK_VERSION) + $(LOCALBIN)/govulncheck -show verbose ./... || true ##@ Build @@ -144,7 +150,7 @@ ci-build: ## To be called to build the application binary in a CI pipeline $(info ******************************************************) $(info ********** EXECUTING 'ci-build' MAKE TARGET **********) $(info ******************************************************) - CGO_ENABLED=$(CGO_ENABLED) go build $(GO_BUILD_ARGS) $(GO_BUILD_ARGS_EXTRA) -ldflags '$(LDFLAGS)' -o $(BIN_DIR)$(NAME) . + CGO_ENABLED=$(CGO_ENABLED) go build $(GO_BUILD_ARGS) $(GO_BUILD_ARGS_EXTRA) -ldflags '$(LDFLAGS)' -o $(LOCALBIN)$(NAME) . .PHONY: run run: format vet generate ## Run the application from your host @@ -195,7 +201,7 @@ clean: ## Cleanup binary $(info ***************************************************) $(info ********** EXECUTING 'clean' MAKE TARGET **********) $(info ***************************************************) - rm -rvf $(BIN_DIR) image-ref.out + rm -rvf $(LOCALBIN) image-ref.out .PHONY: all all: format vet lint govulncheck test build From 20cddc6588139623bf51e049a446f7707b3b0404 Mon Sep 17 00:00:00 2001 From: Reto Gantenbein Date: Sat, 9 May 2026 23:58:20 +0200 Subject: [PATCH 2/4] proxy: Fix "string `DELETE` has 3 occurrences, make it a constant (goconst)" --- pkg/pkgproxy/proxy.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/pkgproxy/proxy.go b/pkg/pkgproxy/proxy.go index 83814fa..b6849e6 100644 --- a/pkg/pkgproxy/proxy.go +++ b/pkg/pkgproxy/proxy.go @@ -49,12 +49,14 @@ type ( } ) +const httpMethodDelete = "DELETE" + var ( // HTTP methods that are allowed for the cache allowedCacheMethods = []string{ "GET", "HEAD", - "DELETE", + httpMethodDelete, } // HTTP methods that are allowed for the proxy @@ -167,7 +169,7 @@ func (pp *pkgProxy) Cache(next echo.HandlerFunc) echo.HandlerFunc { if repoCache.IsCacheCandidate(uri) { if repoCache.IsCached(uri) { // serve or delete from cache - if c.Request().Method == "DELETE" { + if c.Request().Method == httpMethodDelete { slog.Info("cache delete", "request_id", requestID(c), "uri", uri) if err := repoCache.DeleteFile(uri); err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{"message": err.Error()}) @@ -184,7 +186,7 @@ func (pp *pkgProxy) Cache(next echo.HandlerFunc) echo.HandlerFunc { } return c.FileFS(filepath.Base(absPath), os.DirFS(filepath.Dir(absPath))) } else { - if c.Request().Method == "DELETE" { + if c.Request().Method == httpMethodDelete { return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) } // Stream response to both client and cache temp file From df318758baf19d5793682caad7cf0e129848fbb8 Mon Sep 17 00:00:00 2001 From: Reto Gantenbein Date: Sun, 10 May 2026 00:01:08 +0200 Subject: [PATCH 3/4] proxy: Fix "string `message` has 7 occurrences, make it a constant (goconst)" --- pkg/pkgproxy/proxy.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pkg/pkgproxy/proxy.go b/pkg/pkgproxy/proxy.go index b6849e6..04145c2 100644 --- a/pkg/pkgproxy/proxy.go +++ b/pkg/pkgproxy/proxy.go @@ -50,6 +50,7 @@ type ( ) const httpMethodDelete = "DELETE" +const jsonKeyMessage = "message" var ( // HTTP methods that are allowed for the cache @@ -162,7 +163,7 @@ func (pp *pkgProxy) Cache(next echo.HandlerFunc) echo.HandlerFunc { if pp.isRepositoryRequest(uri) { if !utils.Contains(allowedCacheMethods, c.Request().Method) { - return c.JSON(http.StatusMethodNotAllowed, map[string]string{"message": fmt.Sprintf("Cache does not allow method %s\n", c.Request().Method)}) + return c.JSON(http.StatusMethodNotAllowed, map[string]string{jsonKeyMessage: fmt.Sprintf("Cache does not allow method %s\n", c.Request().Method)}) } repoCache = pp.upstreams[getRepoFromURI(uri)].cache @@ -172,22 +173,22 @@ func (pp *pkgProxy) Cache(next echo.HandlerFunc) echo.HandlerFunc { if c.Request().Method == httpMethodDelete { slog.Info("cache delete", "request_id", requestID(c), "uri", uri) if err := repoCache.DeleteFile(uri); err != nil { - return c.JSON(http.StatusInternalServerError, map[string]string{"message": err.Error()}) + return c.JSON(http.StatusInternalServerError, map[string]string{jsonKeyMessage: err.Error()}) } - return c.JSON(http.StatusOK, map[string]string{"message": "Success"}) + return c.JSON(http.StatusOK, map[string]string{jsonKeyMessage: "Success"}) } filePath, err := repoCache.GetFilePath(uri) if err != nil { - return c.JSON(http.StatusForbidden, map[string]string{"message": "Forbidden"}) + return c.JSON(http.StatusForbidden, map[string]string{jsonKeyMessage: "Forbidden"}) } absPath, err := filepath.Abs(filePath) if err != nil { - return c.JSON(http.StatusInternalServerError, map[string]string{"message": err.Error()}) + return c.JSON(http.StatusInternalServerError, map[string]string{jsonKeyMessage: err.Error()}) } return c.FileFS(filepath.Base(absPath), os.DirFS(filepath.Dir(absPath))) } else { if c.Request().Method == httpMethodDelete { - return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) + return c.JSON(http.StatusNotFound, map[string]string{jsonKeyMessage: "Not Found"}) } // Stream response to both client and cache temp file rw = newResilientWriter(repoCache, uri) @@ -279,7 +280,7 @@ func (pp *pkgProxy) ForwardProxy(next echo.HandlerFunc) echo.HandlerFunc { } if !utils.Contains(allowedProxyMethods, c.Request().Method) { - return c.JSON(http.StatusMethodNotAllowed, map[string]string{"message": fmt.Sprintf("Forward proxy does not allow method %s\n", c.Request().Method)}) + return c.JSON(http.StatusMethodNotAllowed, map[string]string{jsonKeyMessage: fmt.Sprintf("Forward proxy does not allow method %s\n", c.Request().Method)}) } // Buffer the request body once so it can be replayed across mirror retries and redirects. From 39bdee43a7babe0d8f54818bdd5472e2a9586ad0 Mon Sep 17 00:00:00 2001 From: Reto Gantenbein Date: Sun, 10 May 2026 00:02:55 +0200 Subject: [PATCH 4/4] utils: Fix "inline: Type alias constraints.Ordered should be inlined (govet)" --- pkg/utils/utils.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 34d6b1b..cc1d673 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -3,10 +3,9 @@ package utils import ( + "cmp" "sort" "strings" - - "golang.org/x/exp/constraints" ) // Contains checks if slice contains element @@ -20,7 +19,7 @@ func Contains[T comparable](s []T, e T) bool { } // KeysFromMap returns a list of the keys of a map -func KeysFromMap[T constraints.Ordered, S any](a map[T]S) []T { +func KeysFromMap[T cmp.Ordered, S any](a map[T]S) []T { keys := make([]T, len(a)) i := 0