From 2fb1b357135f3340833e0b8e75bfb56e810e79dd Mon Sep 17 00:00:00 2001 From: Martin Riedl Date: Thu, 27 Feb 2025 20:34:47 +0100 Subject: [PATCH 1/8] feat: renamed goHTTPRouter to httprouter BREAKING CHANGE: new package name git.martin-riedl.de/golang/httprouter --- README.md | 32 ++++++++++++++++++++------------ fs.go | 4 ++-- fs_test.go | 16 +++++++++++++++- go.mod | 2 +- route.go | 4 ++-- route_test.go | 16 +++++++++++++++- router.go | 4 ++-- router_test.go | 16 +++++++++++++++- 8 files changed, 72 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index c0f4218..289fcda 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,19 @@ -# goHTTPRouter +# httprouter + +[![release](https://git.martin-riedl.de/golang/httprouter/badges/release.svg)](https://git.martin-riedl.de/golang/httprouter/tags) +[![pipeline status](https://git.martin-riedl.de/golang/httprouter/badges/workflows/build.yml/badge.svg)](https://git.martin-riedl.de/golang/httprouter/actions) [![GoDoc](https://godoc.org/gitlab.com/martinr92/gohttprouter?status.svg)](https://godoc.org/gitlab.com/martinr92/gohttprouter) -[![pipeline status](https://gitlab.com/martinr92/gohttprouter/badges/master/pipeline.svg)](https://gitlab.com/martinr92/gohttprouter/commits/master) -[![coverage report](https://gitlab.com/martinr92/gohttprouter/badges/master/coverage.svg)](https://gitlab.com/martinr92/gohttprouter/commits/master) -[![codecov](https://codecov.io/gl/martinr92/gohttprouter/branch/master/graph/badge.svg)](https://codecov.io/gl/martinr92/gohttprouter) [![Go Report Card](https://goreportcard.com/badge/gitlab.com/martinr92/gohttprouter)](https://goreportcard.com/report/gitlab.com/martinr92/gohttprouter) -goHTTPRouter is a framework used for HTTP request routing. +httprouter is a framework used for HTTP request routing. # Examples + ## Simple Routing + Just replace the standard router of golang with this one: ```golang -import httprouter "gitlab.com/martinr92/gohttprouter/v3" +import "git.martin-riedl.de/golang/httprouter/v3" ``` ```golang router := httprouter.New() @@ -22,9 +24,10 @@ err := http.ListenAndServe("localhost:8080", router) ``` ## Routing with placeholder + A path can also contain placeholder (like :id). If then a request is sent to the url "/user/123" the method gets executed and the URL part (in this case "123") is passed as parameter into your handler function. ```golang -import httprouter "gitlab.com/martinr92/gohttprouter/v3" +import "git.martin-riedl.de/golang/httprouter/v3" ``` ```golang router := httprouter.New() @@ -38,11 +41,12 @@ err := http.ListenAndServe("localhost:8080", router) ``` ## Serve Static Content + Static files (like JavaScript or CSS) can be served automatically (including caching header and MIME type). It uses the `embed.FS` (since go 1.16) to serve static content. ```golang -import httprouter "gitlab.com/martinr92/gohttprouter/v3" +import "git.martin-riedl.de/golang/httprouter/v3" -//go:embed files/statc/* +//go:embed files/static/* var staticFiles embed.FS ``` ```golang @@ -51,9 +55,12 @@ router := httprouter.New() router.Handle(http.MethodGet, "/static/*", staticFS) ``` -For development purpose you can enable the local file serving additionally. The framework checks first, if the file exists locally and serves it directly. If not, the file is served from the `embed.FS`. This helps you during local development so you can modify a CSS file without recompiling everything. +For development purpose you can enable the local file serving additionally. +The framework checks first, if the file exists locally and serves it directly. +If not, the file is served from the `embed.FS`. +This helps you during local development so you can modify a CSS file without recompiling everything. ```golang -import httprouter "gitlab.com/martinr92/gohttprouter/v3" +import "git.martin-riedl.de/golang/httprouter/v3" ``` ```golang staticFS := httprouter.NewFS(&staticFiles) @@ -62,8 +69,9 @@ staticFS.LocalFolderPrefix = "some/folder" // optional ``` # License + ``` -Copyright 2018-2021 Martin Riedl +Copyright 2018-2025 Martin Riedl Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/fs.go b/fs.go index b987b4e..0696fc4 100644 --- a/fs.go +++ b/fs.go @@ -1,4 +1,4 @@ -// Copyright 2021 Martin Riedl +// Copyright 2021-2025 Martin Riedl // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package gohttprouter +package httprouter import ( "embed" diff --git a/fs_test.go b/fs_test.go index 470d9f8..b61cc61 100644 --- a/fs_test.go +++ b/fs_test.go @@ -1,4 +1,18 @@ -package gohttprouter +// Copyright 2025 Martin Riedl +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package httprouter import ( "embed" diff --git a/go.mod b/go.mod index 7e397f0..5309e59 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module gitlab.com/martinr92/gohttprouter/v3 +module git.martin-riedl.de/golang/httprouter/v3 go 1.19 \ No newline at end of file diff --git a/route.go b/route.go index b15a9d9..d4891d5 100644 --- a/route.go +++ b/route.go @@ -1,4 +1,4 @@ -// Copyright 2018-2021 Martin Riedl +// Copyright 2018-2025 Martin Riedl // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package gohttprouter +package httprouter import ( "strings" diff --git a/route_test.go b/route_test.go index 68e0d61..e2403ee 100644 --- a/route_test.go +++ b/route_test.go @@ -1,4 +1,18 @@ -package gohttprouter +// Copyright 2025 Martin Riedl +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package httprouter import ( "testing" diff --git a/router.go b/router.go index 251f657..59688d7 100644 --- a/router.go +++ b/router.go @@ -1,4 +1,4 @@ -// Copyright 2018-2021 Martin Riedl +// Copyright 2018-2025 Martin Riedl // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package gohttprouter +package httprouter import ( "fmt" diff --git a/router_test.go b/router_test.go index 842db7c..251f7c1 100755 --- a/router_test.go +++ b/router_test.go @@ -1,4 +1,18 @@ -package gohttprouter +// Copyright 2025 Martin Riedl +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package httprouter import ( "net" From ceb7d04eddca664451d4fa094fc7c3bc4949391a Mon Sep 17 00:00:00 2001 From: Martin Riedl Date: Thu, 27 Feb 2025 20:39:15 +0100 Subject: [PATCH 2/8] ci: new release workflow --- .forgejo/workflows/release.yml | 20 ++++++++++++++++++++ .releaserc | 8 +++++++- 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 .forgejo/workflows/release.yml diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml new file mode 100644 index 0000000..d393a3e --- /dev/null +++ b/.forgejo/workflows/release.yml @@ -0,0 +1,20 @@ +name: Release + +on: + push: + branches: + - main + - beta + +jobs: + release: + name: Semantic Release + runs-on: docker + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Semantic Release + shell: bash + run: | + npm install -g semantic-release@23 conventional-changelog-conventionalcommits@7 + semantic-release diff --git a/.releaserc b/.releaserc index 46eeb2a..c35a8d5 100644 --- a/.releaserc +++ b/.releaserc @@ -61,6 +61,12 @@ } } ], - "@semantic-release/gitlab" + [ + "@semantic-release/github", + { + "successCommentCondition": false, + "failTitle": false + } + ] ] } \ No newline at end of file From 203438dc8cc2d12ce6e421fd1e828806465158a6 Mon Sep 17 00:00:00 2001 From: Martin Riedl Date: Thu, 27 Feb 2025 20:41:08 +0100 Subject: [PATCH 3/8] ci: new build pipeline --- .forgejo/workflows/build.yml | 81 +++++++++++++++++++++ .gitlab-ci.yml | 135 ----------------------------------- sonar-project.properties | 19 ----- 3 files changed, 81 insertions(+), 154 deletions(-) create mode 100644 .forgejo/workflows/build.yml delete mode 100644 .gitlab-ci.yml delete mode 100644 sonar-project.properties diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml new file mode 100644 index 0000000..57ead14 --- /dev/null +++ b/.forgejo/workflows/build.yml @@ -0,0 +1,81 @@ +name: Build + +on: + push: + branches: + - main + - beta + - develop + pull_request: + +jobs: + checks: + name: Checks + runs-on: docker + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: '1.23.x' + check-latest: true + - name: Run go fmt and go vet + run: | + go fmt $(go list ./...) + go vet $(go list ./...) + + code-coverage: + name: Code Coverage + runs-on: docker + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: '1.23.x' + check-latest: true + - name: Run tests and generate coverage report + run: | + go test -covermode=count -coverprofile coverage.cov $(go list ./...) + go tool cover -func=coverage.cov + go tool cover -html=coverage.cov -o coverage.html + - name: Upload coverage artifacts + uses: https://code.forgejo.org/forgejo/upload-artifact@v4 + with: + name: coverage-reports + path: | + coverage.cov + coverage.html + + build: + name: Build + runs-on: docker + strategy: + matrix: + go: + - GOOS: darwin + GOARCH: amd64 + - GOOS: darwin + GOARCH: arm64 + - GOOS: linux + GOARCH: amd64 + - GOOS: linux + GOARCH: arm64 + - GOOS: windows + GOARCH: amd64 + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: '1.23.x' + check-latest: true + - name: Set environment variables + run: | + echo "GOOS=${{ matrix.go.GOOS }}" >> $GITHUB_ENV + echo "GOARCH=${{ matrix.go.GOARCH }}" >> $GITHUB_ENV + - name: Build + run: go build . diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 0f7dfea..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,135 +0,0 @@ -image: golang:1.19 - -stages: - - test - - build - - pre-release - -checks: - stage: test - script: - - go fmt $(go list ./...) - - go vet $(go list ./...) - tags: - - docker - -code coverage: - stage: test - script: - - go install honnef.co/go/tools/cmd/staticcheck@latest - - go fmt $(go list ./...) - - go vet $(go list ./...) - - staticcheck ./... - - go test -coverprofile coverage.cov -p 1 $(go list ./...) - - go tool cover -html=coverage.cov -o coverage.html - - go tool cover -func=coverage.cov - coverage: '/\(statements\)\W+\d+\.\d+%/' - artifacts: - paths: - - coverage.cov - - coverage.html - tags: - - docker - -codecov.io: - stage: test - script: - - curl https://keybase.io/codecovsecurity/pgp_keys.asc | gpg --import - - curl -Os https://uploader.codecov.io/latest/linux/codecov - - curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM - - curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM.sig - - gpg --verify codecov.SHA256SUM.sig codecov.SHA256SUM - - shasum -a 256 -c codecov.SHA256SUM - - chmod +x codecov - - go test -race -coverprofile=coverage.out -covermode=atomic - - ./codecov -t ${CODECOV_TOKEN} - rules: - - if: $CODECOV_TOKEN - when: on_success - tags: - - docker - -# stage "sonarcloud" is only needed because of this issue: -# https://gitlab.com/gitlab-org/gitlab/-/issues/30632 -sonarcloud-check: - stage: test - needs: - - checks - image: - name: sonarsource/sonar-scanner-cli:latest - entrypoint: [""] - variables: - SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" # Defines the location of the analysis task cache - GIT_DEPTH: "0" # Tells git to fetch all the branches of the project, required by the analysis task - cache: - key: "${CI_JOB_NAME}" - paths: - - .sonar/cache - script: - - sonar-scanner - tags: - - docker - allow_failure: true - -# build template -# execute the following command for all os/arch combinations: go tool dist list -.compile: - stage: build - # no dependencies -> no download of artifacts from previous jobs/stages - dependencies: [] - script: - - go build . - tags: - - docker - -darwin-amd64: - extends: .compile - variables: - GOOS: "darwin" - GOARCH: "amd64" - -linux-amd64: - extends: .compile - variables: - GOOS: "linux" - GOARCH: "amd64" - -linux-arm64: - extends: .compile - variables: - GOOS: "linux" - GOARCH: "arm64" - -windows-amd64: - extends: .compile - variables: - GOOS: "windows" - GOARCH: "amd64" - -.semantic-release: - stage: pre-release - image: node:20-buster-slim - dependencies: [] - before_script: - - apt-get update && apt-get install -y --no-install-recommends git-core ca-certificates - - npm install -g semantic-release@23 @semantic-release/gitlab@13 conventional-changelog-conventionalcommits@7 - script: - - semantic-release -d $DRY_RUN - variables: - GL_TOKEN: $SEMANTIC_RELEASE_TOKEN - DRY_RUN: "false" - tags: - - docker - -semantic-release-dry-run: - extends: .semantic-release - variables: - DRY_RUN: "true" - rules: - - if: '$CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "beta"' - -semantic-release: - extends: .semantic-release - rules: - - if: '$CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "beta"' - when: manual diff --git a/sonar-project.properties b/sonar-project.properties deleted file mode 100644 index 85b076d..0000000 --- a/sonar-project.properties +++ /dev/null @@ -1,19 +0,0 @@ -sonar.projectKey=martinr92_gohttprouter -sonar.organization=martinr92 - -# This is the name and version displayed in the SonarCloud UI. -#sonar.projectName=goHTTPRouter -#sonar.projectVersion=1.0 - -# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. -sonar.sources=. -sonar.exclusions=**/*_test.go,coverage.cov,coverage.html - -sonar.tests=. -sonar.test.inclusions=**/*_test.go - -# Encoding of the source code. Default is default system encoding -#sonar.sourceEncoding=UTF-8 - -# Golang -sonar.go.coverage.reportPaths=coverage.cov From 8d8f8b0c9991f4d9bf33ca52f727e3d672d26292 Mon Sep 17 00:00:00 2001 From: Martin Riedl Date: Thu, 27 Feb 2025 20:42:57 +0100 Subject: [PATCH 4/8] style: fixed typo --- route_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/route_test.go b/route_test.go index e2403ee..54fbc3a 100644 --- a/route_test.go +++ b/route_test.go @@ -73,7 +73,7 @@ func TestRoutePlaceholder(t *testing.T) { t.Error("error during placeholder name validation;", placeholderElement.placeholderName) } - // check path finder + // check pathfinder foundRoute, _, foundParameters := route.parsePath("/user/123/home/martin", false) if foundRoute != homePathElement.routes[":"] { t.Error("wrong path element returned form path finding") From 18a84b9b9291c7e6ef508ed18263abe70e07f8d7 Mon Sep 17 00:00:00 2001 From: Martin Riedl Date: Thu, 27 Feb 2025 20:43:46 +0100 Subject: [PATCH 5/8] style: removed unnecessary braces and else if statements --- route.go | 4 ++-- router.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/route.go b/route.go index d4891d5..b11a394 100644 --- a/route.go +++ b/route.go @@ -82,7 +82,7 @@ func (r *route) parsePath(path string, create bool) (*route, bool, map[string]st if !ok && !create { // no route found return nil, false, nil - } else if !ok && create { + } else if !ok { subRoute = newRoute() subRoute.parseName(subName) r.routes[subRoute.name] = subRoute @@ -91,7 +91,7 @@ func (r *route) parsePath(path string, create bool) (*route, bool, map[string]st // parse sub-route matchingRoute, created, subParameters := subRoute.parsePath(parts[1], create) - return matchingRoute, (subRouteCreated || created), mergeParameterMaps(parameters, subParameters) + return matchingRoute, subRouteCreated || created, mergeParameterMaps(parameters, subParameters) } // last element in path diff --git a/router.go b/router.go index 59688d7..b645b66 100644 --- a/router.go +++ b/router.go @@ -85,7 +85,7 @@ func (router *Router) findRoute(method string, path string, create bool) (route methodRoute, ok := router.registry[methodUpper] if !ok && !create { return nil, false, nil - } else if !ok && create { + } else if !ok { methodRoute = newRoute() router.registry[methodUpper] = methodRoute created = true From 7b5e1d025428574e55f7516dd2c2e524720c65d2 Mon Sep 17 00:00:00 2001 From: Martin Riedl Date: Fri, 28 Feb 2025 15:46:06 +0100 Subject: [PATCH 6/8] feat: new release of version 4 --- README.md | 8 ++++---- go.mod | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 289fcda..07be8f6 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ httprouter is a framework used for HTTP request routing. Just replace the standard router of golang with this one: ```golang -import "git.martin-riedl.de/golang/httprouter/v3" +import "git.martin-riedl.de/golang/httprouter/v4" ``` ```golang router := httprouter.New() @@ -27,7 +27,7 @@ err := http.ListenAndServe("localhost:8080", router) A path can also contain placeholder (like :id). If then a request is sent to the url "/user/123" the method gets executed and the URL part (in this case "123") is passed as parameter into your handler function. ```golang -import "git.martin-riedl.de/golang/httprouter/v3" +import "git.martin-riedl.de/golang/httprouter/v4" ``` ```golang router := httprouter.New() @@ -44,7 +44,7 @@ err := http.ListenAndServe("localhost:8080", router) Static files (like JavaScript or CSS) can be served automatically (including caching header and MIME type). It uses the `embed.FS` (since go 1.16) to serve static content. ```golang -import "git.martin-riedl.de/golang/httprouter/v3" +import "git.martin-riedl.de/golang/httprouter/v4" //go:embed files/static/* var staticFiles embed.FS @@ -60,7 +60,7 @@ The framework checks first, if the file exists locally and serves it directly. If not, the file is served from the `embed.FS`. This helps you during local development so you can modify a CSS file without recompiling everything. ```golang -import "git.martin-riedl.de/golang/httprouter/v3" +import "git.martin-riedl.de/golang/httprouter/v4" ``` ```golang staticFS := httprouter.NewFS(&staticFiles) diff --git a/go.mod b/go.mod index 5309e59..ee3c466 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module git.martin-riedl.de/golang/httprouter/v3 +module git.martin-riedl.de/golang/httprouter/v4 go 1.19 \ No newline at end of file From 851ecb345a9365ac0cfb102cff01256b531006ba Mon Sep 17 00:00:00 2001 From: Martin Riedl Date: Sat, 1 Mar 2025 12:22:47 +0000 Subject: [PATCH 7/8] chore: Configure Renovate (#1) Reviewed-on: https://git.martin-riedl.de/golang/httprouter/pulls/1 --- renovate.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 renovate.json diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..9e937cb --- /dev/null +++ b/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "local>ci/renovate//configs/base" + ] +} From 107c873b4b86921286f63ad04e22596a34d14ff7 Mon Sep 17 00:00:00 2001 From: Martin Riedl Date: Sat, 1 Mar 2025 13:26:00 +0100 Subject: [PATCH 8/8] ci: merge dependency updates into develop branch --- renovate.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/renovate.json b/renovate.json index 9e937cb..5a11f66 100644 --- a/renovate.json +++ b/renovate.json @@ -2,5 +2,8 @@ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "local>ci/renovate//configs/base" + ], + "baseBranches": [ + "develop" ] }