diff --git a/.concourse/branch-tracker.yml b/.concourse/branch-tracker.yml new file mode 100644 index 0000000..721f488 --- /dev/null +++ b/.concourse/branch-tracker.yml @@ -0,0 +1,42 @@ +--- +resource_types: + - name: git-branches + type: registry-image + source: + repository: aoldershaw/git-branches-resource + +resources: + - name: feature-branches + type: git-branches + source: + uri: https://code.icb4dc0.de/prskr/nurse.git + # The "(?Ppattern)" syntax defines a named capture group. + # aoldershaw/git-branches-resource emits the value of each named capture + # group under the `groups` key. + # + # e.g. feature/some-feature ==> {"groups": {"feature": "some-feature"}} + branch_regex: '(\d+-|\w+\/|\w+\(\w+\):\s*)(?P.*)' + + - name: nurse.git + type: git + icon: github + source: + uri: https://code.icb4dc0.de/prskr/nurse.git + fetch_tags: true + +jobs: + - name: set-feature-pipelines + plan: + - in_parallel: + - get: feature-branches + trigger: true + - get: nurse.git + - load_var: branches + file: feature-branches/branches.json + - across: + - var: branch + values: ((.:branches)) + set_pipeline: nurse-dev + file: nurse.git/.concourse/branch-validate.yml + instance_vars: {feature: ((.:branch.groups.feature))} + vars: {branch: ((.:branch.name))} \ No newline at end of file diff --git a/.concourse/branch-validate.yml b/.concourse/branch-validate.yml new file mode 100644 index 0000000..7893825 --- /dev/null +++ b/.concourse/branch-validate.yml @@ -0,0 +1,17 @@ +--- +resources: + - name: nurse.git + type: git + icon: github + source: + uri: https://code.icb4dc0.de/prskr/nurse.git + branch: ((branch)) + +jobs: + - name: lint + plan: + - get: nurse.git + trigger: true + - task: lint + file: nurse.git/.concourse/tasks/lint.yml + input_mapping: {repo: nurse.git} \ No newline at end of file diff --git a/.concourse/cleanup.yml b/.concourse/cleanup.yml new file mode 100644 index 0000000..3be29ce --- /dev/null +++ b/.concourse/cleanup.yml @@ -0,0 +1,31 @@ +--- +resources: + - name: daily + type: time + source: + interval: 24h + - name: nurse.git + type: git + icon: github + source: + uri: https://code.icb4dc0.de/prskr/nurse.git + - name: templates.git + type: git + icon: github + source: + uri: https://code.icb4dc0.de/prskr/pipeline-templates.git + +jobs: + - name: renovate + plan: + - get: nurse.git + trigger: true + - get: templates.git + trigger: true + - get: daily + trigger: true + - task: renovate + file: templates.git/tasks/renovate.yml + input_mapping: {repo: nurse.git} + vars: + project_path: prskr/nurse \ No newline at end of file diff --git a/.concourse/tasks/lint.yml b/.concourse/tasks/lint.yml new file mode 100644 index 0000000..06d5be0 --- /dev/null +++ b/.concourse/tasks/lint.yml @@ -0,0 +1,27 @@ +--- +platform: linux + +image_resource: + type: registry-image + source: + repository: docker.io/golang:1.19-bullseye + tag: latest + +inputs: + - name: repo + path: . + +params: + GO111MODULE: "on" + CGO_ENABLED: "0" + GITEA_TOKEN: ((gitea-credentials.token)) + DOCKER_HOST: tcp://127.0.0.1:2375 + +run: + path: sh + args: + - -ce + - | + apk add -U --no-cache curl + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b /usr/local/bin + go run github.com/magefile/mage lint diff --git a/.gitignore b/.gitignore index 18067a7..ab18ed4 100644 --- a/.gitignore +++ b/.gitignore @@ -16,5 +16,6 @@ dist/ .idea/ +.fleet/ cue.mod/ .go/ \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml index f98f2e6..0cc88f9 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -72,7 +72,7 @@ linters: - funlen - gocognit - goconst - - gocritic + # - gocritic - gocyclo - godox - gofumpt @@ -80,10 +80,11 @@ linters: - gomoddirectives - gomnd - gosec - # - gosimple + - gosimple - govet - importas - ineffassign + # - ireturn - enable later - lll - misspell - nakedret @@ -91,17 +92,15 @@ linters: - nilnil - noctx - nolintlint - - nosprintfhostport - paralleltest - prealloc - predeclared - promlinter - # - staticcheck - # - stylecheck - - tenv + - staticcheck + - stylecheck - testpackage - thelper - - typecheck + # - typecheck - unconvert - unparam - whitespace @@ -118,6 +117,11 @@ issues: - gocognit - gomnd - govet + - path: magefiles/.*\.go + linters: + - typecheck + - unused + - govet run: skip-files: diff --git a/api/check_handler.go b/api/check_handler.go index 1e0c6f4..4d023d7 100644 --- a/api/check_handler.go +++ b/api/check_handler.go @@ -26,5 +26,4 @@ func (c CheckHandler) ServeHTTP(writer http.ResponseWriter, request *http.Reques } writer.WriteHeader(http.StatusOK) - return } diff --git a/config/env.go b/config/env.go index f9227af..e3b505c 100644 --- a/config/env.go +++ b/config/env.go @@ -3,7 +3,7 @@ package config import ( "os" "path" - . "strings" + "strings" ) const ( @@ -14,16 +14,16 @@ const ( func ServersFromEnv() (map[string]Server, error) { servers := make(map[string]Server) for _, kv := range os.Environ() { - key, value, valid := Cut(kv, "=") + key, value, valid := strings.Cut(kv, "=") if !valid { continue } - if !HasPrefix(key, ServerKeyPrefix) { + if !strings.HasPrefix(key, ServerKeyPrefix) { continue } - serverName := ToLower(Trim(Replace(key, ServerKeyPrefix, "", -1), "_")) + serverName := strings.ToLower(strings.Trim(strings.Replace(key, ServerKeyPrefix, "", -1), "_")) srv := Server{} if err := srv.UnmarshalURL(value); err != nil { return nil, err @@ -39,16 +39,16 @@ func EndpointsFromEnv() (map[Route]EndpointSpec, error) { endpoints := make(map[Route]EndpointSpec) for _, kv := range os.Environ() { - key, value, valid := Cut(kv, "=") + key, value, valid := strings.Cut(kv, "=") if !valid { continue } - if !HasPrefix(key, EndpointKeyPrefix) { + if !strings.HasPrefix(key, EndpointKeyPrefix) { continue } - endpointRoute := path.Join(Split(ToLower(Trim(Replace(key, EndpointKeyPrefix, "", -1), "_")), "_")...) + endpointRoute := path.Join(strings.Split(strings.ToLower(strings.Trim(strings.Replace(key, EndpointKeyPrefix, "", -1), "_")), "_")...) spec := EndpointSpec{} if err := spec.Parse(value); err != nil { return nil, err diff --git a/config/server.go b/config/server.go index dad5753..8ee47e9 100644 --- a/config/server.go +++ b/config/server.go @@ -199,8 +199,8 @@ func (s *Server) UnmarshalJSON(bytes []byte) error { return s.mergedMarshaledServer(*tmp) } -func (s *Server) UnmarshalURL(rawUrl string) error { - rawPath, username, password, err := s.extractBaseProperties(rawUrl) +func (s *Server) UnmarshalURL(rawURL string) error { + rawPath, username, password, err := s.extractBaseProperties(rawURL) if err != nil { return err } @@ -215,11 +215,11 @@ func (s *Server) UnmarshalURL(rawUrl string) error { } if rawPath != "" { - parsedUrl, err := url.Parse(fmt.Sprintf("%s://%s%s", s.Type.Scheme(), s.Hosts[0], rawPath)) + parsedURL, err := url.Parse(fmt.Sprintf("%s://%s%s", s.Type.Scheme(), s.Hosts[0], rawPath)) if err != nil { return err } - if err := s.unmarshalPath(parsedUrl); err != nil { + if err := s.unmarshalPath(parsedURL); err != nil { return err } } else { @@ -229,8 +229,8 @@ func (s *Server) UnmarshalURL(rawUrl string) error { return nil } -func (s *Server) extractBaseProperties(rawUrl string) (rawPath, username, password string, err error) { - allMatches := hostsRegexp.FindAllStringSubmatch(rawUrl, -1) +func (s *Server) extractBaseProperties(rawURL string) (rawPath, username, password string, err error) { + allMatches := hostsRegexp.FindAllStringSubmatch(rawURL, -1) if matchLen := len(allMatches); matchLen != 1 { return "", "", "", fmt.Errorf("ambiguous server match: %d", matchLen) } diff --git a/go.mod b/go.mod index 4699d8d..d4090d6 100644 --- a/go.mod +++ b/go.mod @@ -3,19 +3,21 @@ module code.icb4dc0.de/prskr/nurse go 1.19 require ( + code.gitea.io/sdk/gitea v0.15.1 github.com/PaesslerAG/jsonpath v0.1.1 github.com/alecthomas/participle/v2 v2.0.0-beta.5 github.com/go-redis/redis/v8 v8.11.5 github.com/go-sql-driver/mysql v1.6.0 github.com/google/uuid v1.3.0 github.com/jackc/pgx/v5 v5.0.0 + github.com/magefile/mage v1.14.0 github.com/maxatome/go-testdeep v1.12.0 github.com/mitchellh/mapstructure v1.5.0 github.com/testcontainers/testcontainers-go v0.14.0 github.com/valyala/bytebufferpool v1.0.0 go.uber.org/multierr v1.8.0 go.uber.org/zap v1.23.0 - golang.org/x/exp v0.0.0-20220927162542-c76eaa363f9d + golang.org/x/exp v0.0.0-20221230185412-738e83a70c30 golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7 gopkg.in/yaml.v3 v3.0.1 ) @@ -38,6 +40,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect + github.com/hashicorp/go-version v1.2.1 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect github.com/magiconair/properties v1.8.6 // indirect @@ -55,7 +58,7 @@ require ( go.uber.org/atomic v1.10.0 // indirect golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect golang.org/x/net v0.0.0-20220617184016-355a448f1bc9 // indirect - golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect + golang.org/x/sys v0.1.0 // indirect golang.org/x/text v0.3.7 // indirect google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad // indirect google.golang.org/grpc v1.47.0 // indirect diff --git a/go.sum b/go.sum index 5b68159..8627ec1 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,9 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE= +code.gitea.io/sdk/gitea v0.15.1 h1:WJreC7YYuxbn0UDaPuWIe/mtiNKTvLN8MLkaw71yx/M= +code.gitea.io/sdk/gitea v0.15.1/go.mod h1:klY2LVI3s3NChzIk/MzMn7G1FHrfU7qd63iSMVoHRBA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= @@ -542,6 +545,8 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -606,6 +611,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= +github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo= +github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= @@ -988,12 +995,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20220907003533-145caa8ea1d0 h1:17k44ji3KFYG94XS5QEFC8pyuOlMh3IoR+vkmTZmJJs= -golang.org/x/exp v0.0.0-20220907003533-145caa8ea1d0/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20220921164117-439092de6870 h1:j8b6j9gzSigH28O5SjSpQSSh9lFd6f5D/q0aHjNTulc= -golang.org/x/exp v0.0.0-20220921164117-439092de6870/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20220927162542-c76eaa363f9d h1:3wgmvnqHUJ8SxiNWwea5NCzTwAVfhTtuV+0ClVFlClc= -golang.org/x/exp v0.0.0-20220927162542-c76eaa363f9d/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/exp v0.0.0-20221230185412-738e83a70c30 h1:m9O6OTJ627iFnN2JIWfdqlZCzneRO6EEBsHXI25P8ws= +golang.org/x/exp v0.0.0-20221230185412-738e83a70c30/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1104,8 +1107,6 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220907140024-f12130a52804 h1:0SH2R3f1b1VmIMG7BXbEZCBUu2dKmHschSmjqGUrW8A= -golang.org/x/sync v0.0.0-20220907140024-f12130a52804/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7 h1:ZrnxWX62AgTKOSagEqxvb3ffipvEDX2pl7E1TdqLqIc= golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1212,8 +1213,8 @@ golang.org/x/sys v0.0.0-20220405210540-1e041c57c461/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1280,6 +1281,7 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= diff --git a/magefiles/common.go b/magefiles/common.go new file mode 100644 index 0000000..64d32f9 --- /dev/null +++ b/magefiles/common.go @@ -0,0 +1,120 @@ +package main + +import ( + "io/fs" + "os" + "path/filepath" + "strings" + + "code.gitea.io/sdk/gitea" + "github.com/magefile/mage/sh" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "golang.org/x/exp/slices" +) + +const defaultDirPermissions = 0o755 + +var ( + GoSourceFiles []string + GeneratedMockFiles []string + WorkingDir string + OutDir string + GitCommit string + GiteaClient *gitea.Client + dirsToIgnore = []string{ + ".git", + "magefiles", + ".concourse", + ".run", + ".task", + } +) + +func init() { + if currentCommit, err := sh.Output("git", "rev-parse", "HEAD"); err != nil { + panic(err) + } else { + GitCommit = currentCommit + } + + if wd, err := os.Getwd(); err != nil { + panic(err) + } else { + WorkingDir = wd + } + + OutDir = filepath.Join(WorkingDir, "out") + + if err := os.MkdirAll(OutDir, defaultDirPermissions); err != nil { + panic(err) + } + + if err := initLogging(); err != nil { + panic(err) + } + + if err := initSourceFiles(); err != nil { + panic(err) + } + + if giteaToken := os.Getenv("GITEA_TOKEN"); giteaToken != "" { + if client, err := gitea.NewClient("https://code.icb4dc0.de", gitea.SetToken(giteaToken)); err == nil { + GiteaClient = client + } + } + + zap.L().Info("Completed initialization", zap.String("commit", GitCommit)) +} + +func initLogging() error { + cfg := zap.NewDevelopmentConfig() + cfg.Encoding = "console" + cfg.Level = zap.NewAtomicLevelAt(zapcore.InfoLevel) + + if logger, err := cfg.Build(); err != nil { + return err + } else { + zap.ReplaceGlobals(logger) + } + + return nil +} + +func initSourceFiles() error { + return filepath.WalkDir(WorkingDir, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + if d.IsDir() { + if slices.Contains(dirsToIgnore, filepath.Base(path)) { + return fs.SkipDir + } + return nil + } + + _, ext, found := strings.Cut(filepath.Base(path), ".") + if !found { + return nil + } + + switch ext { + case "mock.go": + GeneratedMockFiles = append(GeneratedMockFiles, path) + case "go": + GoSourceFiles = append(GoSourceFiles, path) + } + + return nil + }) +} + +func commitStatusOption(context, description string) gitea.CreateStatusOption { + return gitea.CreateStatusOption{ + Context: context, + Description: description, + State: gitea.StatusPending, + TargetURL: "https://concourse.icb4dc0.de/teams/main/pipelines", + } +} diff --git a/magefiles/format.go b/magefiles/format.go new file mode 100644 index 0000000..be0ba3d --- /dev/null +++ b/magefiles/format.go @@ -0,0 +1,30 @@ +package main + +import ( + "github.com/magefile/mage/mg" + "github.com/magefile/mage/sh" +) + +func Format() { + mg.Deps(GoImports, GoFumpt) +} + +func GoImports() error { + if err := ensureGoTool("goimports", "golang.org/x/tools/cmd/goimports", "latest"); err != nil { + return err + } + + return sh.RunV( + "goimports", + "-local=inetmock.icb4dc0.de/inetmock", + "-w", + WorkingDir, + ) +} + +func GoFumpt() error { + if err := ensureGoTool("gofumpt", "mvdan.cc/gofumpt", "latest"); err != nil { + return err + } + return sh.RunV("gofumpt", "-l", "-w", WorkingDir) +} diff --git a/magefiles/lint.go b/magefiles/lint.go new file mode 100644 index 0000000..64b8802 --- /dev/null +++ b/magefiles/lint.go @@ -0,0 +1,38 @@ +package main + +import ( + "context" + + "code.gitea.io/sdk/gitea" + "github.com/magefile/mage/mg" + "github.com/magefile/mage/sh" + "go.uber.org/multierr" +) + +func Lint(ctx context.Context) { + mg.CtxDeps(ctx, Format, LintGo) +} + +func LintGo(ctx context.Context) (err error) { + status := commitStatusOption("concourse-ci/lint/golangci-lint", "Lint Go files") + if err := setCommitStatus(ctx, status); err != nil { + return err + } + + defer func() { + if err == nil { + status.State = gitea.StatusSuccess + } else { + status.State = gitea.StatusFailure + } + + err = multierr.Append(err, setCommitStatus(ctx, status)) + }() + + return sh.RunV( + "golangci-lint", + "run", + "-v", + "--issues-exit-code=1", + ) +} diff --git a/magefiles/reporting.go b/magefiles/reporting.go new file mode 100644 index 0000000..5c70830 --- /dev/null +++ b/magefiles/reporting.go @@ -0,0 +1,38 @@ +package main + +import ( + "context" + "fmt" + "net/http" + + "code.gitea.io/sdk/gitea" +) + +type CommitState string + +func (s CommitState) String() string { + return string(s) +} + +func setCommitStatus(ctx context.Context, notification gitea.CreateStatusOption) error { + if GiteaClient == nil { + return nil + } + + GiteaClient.SetContext(ctx) + + _, resp, err := GiteaClient.CreateStatus("inetmock", "inetmock", GitCommit, notification) + if err != nil { + return err + } + + defer func() { + _ = resp.Close + }() + + if resp.StatusCode >= http.StatusMultipleChoices { + return fmt.Errorf("failed to update commit status - %d - %s", resp.StatusCode, resp.Status) + } + + return nil +} diff --git a/magefiles/tools.go b/magefiles/tools.go new file mode 100644 index 0000000..2c4e500 --- /dev/null +++ b/magefiles/tools.go @@ -0,0 +1,34 @@ +package main + +import ( + "fmt" + "os/exec" + + "github.com/magefile/mage/sh" + "go.uber.org/zap" +) + +var ( + GoReleaser = sh.RunCmd("goreleaser") + GoInstall = sh.RunCmd("go", "install") + GoBuild = sh.RunCmd("go", "build") +) + +func ensureGoTool(toolName, importPath, version string) error { + return checkForTool(toolName, func() error { + logger := zap.L() + toolToInstall := fmt.Sprintf("%s@%s", importPath, version) + logger.Info("Installing Go tool", zap.String("toolToInstall", toolToInstall)) + return GoInstall(toolToInstall) + }) +} + +func checkForTool(toolName string, fallbackAction func() error) error { + logger := zap.L() + if _, err := exec.LookPath(toolName); err != nil { + logger.Warn("tool is missing", zap.String("toolName", toolName)) + return fallbackAction() + } + + return nil +} diff --git a/validation/jsonpath_test.go b/validation/jsonpath_test.go index 4f89afb..31c2cb8 100644 --- a/validation/jsonpath_test.go +++ b/validation/jsonpath_test.go @@ -6,7 +6,7 @@ import ( "code.icb4dc0.de/prskr/nurse/validation" ) -type jsonPathValidator_EqualsTestCase[V validation.Value] struct { +type jsonPathValidatorEqualsTestCase[V validation.Value] struct { testName string expected V jsonPath string @@ -14,12 +14,12 @@ type jsonPathValidator_EqualsTestCase[V validation.Value] struct { wantErr bool } -func (tt jsonPathValidator_EqualsTestCase[V]) name() string { +func (tt jsonPathValidatorEqualsTestCase[V]) name() string { return tt.testName } //nolint:thelper // is not a helper -func (tt jsonPathValidator_EqualsTestCase[V]) run(t *testing.T) { +func (tt jsonPathValidatorEqualsTestCase[V]) run(t *testing.T) { t.Parallel() t.Helper() validator, err := validation.JSONPathValidatorFor(tt.jsonPath, tt.expected) @@ -37,42 +37,42 @@ func (tt jsonPathValidator_EqualsTestCase[V]) run(t *testing.T) { func TestJSONPathValidator_Equals(t *testing.T) { t.Parallel() tests := []testCase{ - jsonPathValidator_EqualsTestCase[string]{ + jsonPathValidatorEqualsTestCase[string]{ testName: "Simple object navigation", expected: "hello", jsonPath: "$.greeting", json: `{"greeting": "hello"}`, wantErr: false, }, - jsonPathValidator_EqualsTestCase[string]{ + jsonPathValidatorEqualsTestCase[string]{ testName: "Simple object navigation - number as string", expected: "42", jsonPath: "$.number", json: `{"number": 42}`, wantErr: false, }, - jsonPathValidator_EqualsTestCase[string]{ + jsonPathValidatorEqualsTestCase[string]{ testName: "Simple array navigation", expected: "world", jsonPath: "$[1]", json: `["hello", "world"]`, wantErr: false, }, - jsonPathValidator_EqualsTestCase[int]{ + jsonPathValidatorEqualsTestCase[int]{ testName: "Simple array navigation - string to int", expected: 37, jsonPath: "$[1]", json: `["13", "37"]`, wantErr: false, }, - jsonPathValidator_EqualsTestCase[int]{ + jsonPathValidatorEqualsTestCase[int]{ testName: "Simple array navigation - string to int - wrong value", expected: 42, jsonPath: "$[1]", json: `["13", "37"]`, wantErr: true, }, - jsonPathValidator_EqualsTestCase[string]{ + jsonPathValidatorEqualsTestCase[string]{ testName: "Simple array navigation - int to string", expected: "37", jsonPath: "$[1]", diff --git a/validation/jsonval_test.go b/validation/jsonval_test.go index 0669696..e7aa484 100644 --- a/validation/jsonval_test.go +++ b/validation/jsonval_test.go @@ -11,7 +11,7 @@ type testCase interface { name() string } -type jsonValueComparator_EqualsTestCase[V validation.Value] struct { +type jsonValueComparatorEqualsTestCase[V validation.Value] struct { testName string expected V got any @@ -19,7 +19,7 @@ type jsonValueComparator_EqualsTestCase[V validation.Value] struct { } //nolint:thelper // is not a helper -func (tt jsonValueComparator_EqualsTestCase[V]) run(t *testing.T) { +func (tt jsonValueComparatorEqualsTestCase[V]) run(t *testing.T) { t.Parallel() t.Helper() comparator, err := validation.JSONValueComparatorFor(tt.expected) @@ -34,140 +34,140 @@ func (tt jsonValueComparator_EqualsTestCase[V]) run(t *testing.T) { } } -func (tt jsonValueComparator_EqualsTestCase[V]) name() string { +func (tt jsonValueComparatorEqualsTestCase[V]) name() string { return tt.testName } func TestJSONValueComparator_Equals(t *testing.T) { t.Parallel() tests := []testCase{ - jsonValueComparator_EqualsTestCase[int]{ + jsonValueComparatorEqualsTestCase[int]{ testName: "Test int equality", expected: 42, got: 42, wantErr: false, }, - jsonValueComparator_EqualsTestCase[int]{ + jsonValueComparatorEqualsTestCase[int]{ testName: "Test int equality - wrong value", expected: 42, got: 43, wantErr: true, }, - jsonValueComparator_EqualsTestCase[int]{ + jsonValueComparatorEqualsTestCase[int]{ testName: "Test int equality - string value", expected: 42, got: "42", wantErr: false, }, - jsonValueComparator_EqualsTestCase[int]{ + jsonValueComparatorEqualsTestCase[int]{ testName: "Test int equality - []byte value", expected: 42, got: []byte("42"), wantErr: false, }, - jsonValueComparator_EqualsTestCase[int]{ + jsonValueComparatorEqualsTestCase[int]{ testName: "Test int equality - float value", expected: 42, got: 42.0, wantErr: false, }, - jsonValueComparator_EqualsTestCase[int8]{ + jsonValueComparatorEqualsTestCase[int8]{ testName: "Test int8 equality", expected: 42, got: 42, wantErr: false, }, - jsonValueComparator_EqualsTestCase[int8]{ + jsonValueComparatorEqualsTestCase[int8]{ testName: "Test int8 equality - wrong value", expected: 42, got: 43, wantErr: true, }, - jsonValueComparator_EqualsTestCase[int8]{ + jsonValueComparatorEqualsTestCase[int8]{ testName: "Test int8 equality - int16 value", expected: 42, got: int16(42), wantErr: false, }, - jsonValueComparator_EqualsTestCase[int8]{ + jsonValueComparatorEqualsTestCase[int8]{ testName: "Test int8 equality - uint16 value", expected: 42, got: uint16(42), wantErr: false, }, - jsonValueComparator_EqualsTestCase[float32]{ + jsonValueComparatorEqualsTestCase[float32]{ testName: "Test float32 equality - float value", expected: 42.0, got: 42.0, wantErr: false, }, - jsonValueComparator_EqualsTestCase[float32]{ + jsonValueComparatorEqualsTestCase[float32]{ testName: "Test float32 equality - float value", expected: 42.0, got: float64(42.0), wantErr: false, }, - jsonValueComparator_EqualsTestCase[float64]{ + jsonValueComparatorEqualsTestCase[float64]{ testName: "Test float64 equality - float value", expected: 42.0, got: 42.0, wantErr: false, }, - jsonValueComparator_EqualsTestCase[float64]{ + jsonValueComparatorEqualsTestCase[float64]{ testName: "Test float64 equality - int value", expected: 42.0, got: 42, wantErr: false, }, - jsonValueComparator_EqualsTestCase[float64]{ + jsonValueComparatorEqualsTestCase[float64]{ testName: "Test float64 equality - []byte value", expected: 42.0, got: []byte("42"), wantErr: false, }, - jsonValueComparator_EqualsTestCase[float64]{ + jsonValueComparatorEqualsTestCase[float64]{ testName: "Test float64 equality - float32 value", expected: 42.0, got: float32(42.0), wantErr: false, }, - jsonValueComparator_EqualsTestCase[float64]{ + jsonValueComparatorEqualsTestCase[float64]{ testName: "Test float64 equality - string value", expected: 42.0, got: "42.0", wantErr: false, }, - jsonValueComparator_EqualsTestCase[float64]{ + jsonValueComparatorEqualsTestCase[float64]{ testName: "Test float64 equality - string value without dot", expected: 42.0, got: "42", wantErr: false, }, - jsonValueComparator_EqualsTestCase[string]{ + jsonValueComparatorEqualsTestCase[string]{ testName: "Test string equality", expected: "hello", got: "hello", wantErr: false, }, - jsonValueComparator_EqualsTestCase[string]{ + jsonValueComparatorEqualsTestCase[string]{ testName: "Test string equality - []byte value", expected: "hello", got: []byte("hello"), wantErr: false, }, - jsonValueComparator_EqualsTestCase[string]{ + jsonValueComparatorEqualsTestCase[string]{ testName: "Test string equality - int value", expected: "1337", got: 1337, wantErr: false, }, - jsonValueComparator_EqualsTestCase[string]{ + jsonValueComparatorEqualsTestCase[string]{ testName: "Test string equality - float value", expected: "13.37", got: 13.37, wantErr: false, }, - jsonValueComparator_EqualsTestCase[string]{ + jsonValueComparatorEqualsTestCase[string]{ testName: "Test string equality - wrong case", expected: "hello", got: "HELLO",