From fb43bd4b31cca4206f0469b1afffcb1666685ab2 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 31 Aug 2021 00:35:17 +0300 Subject: [PATCH] feat: provide gpg identity signature check Breaking change: `.conform.yaml` `gpg: bool` field was changed to be a structure: ```yaml gpg: required: true identity: gitHubOrganization: talos-systems ``` (`identity` is not required) This enforces that GPG signature should come from one the members of the GitHub organization to succeed, i.e. commit is signed by the member of the organization. Signed-off-by: Andrey Smirnov --- .conform.yaml | 5 +- .drone.yml | 2 +- .../golangci-lint.yaml => .golangci-lint.yaml | 2 + Dockerfile | 6 +- Makefile | 3 +- cmd/enforce.go | 6 +- go.mod | 9 +- go.sum | 102 +++++++------ hack/test.sh | 53 ------- internal/enforcer/enforcer.go | 15 +- internal/git/git.go | 61 ++++++-- .../commit/check_conventional_commit.go | 2 +- internal/policy/commit/check_gpg_identity.go | 137 ++++++++++++++++++ internal/policy/commit/check_gpg_signature.go | 6 +- internal/policy/commit/check_jira_test.go | 14 +- internal/policy/commit/commit.go | 38 ++++- internal/policy/commit/commit_test.go | 3 +- internal/policy/license/license.go | 5 +- internal/reporter/reporter.go | 7 +- 19 files changed, 325 insertions(+), 151 deletions(-) rename hack/golangci-lint.yaml => .golangci-lint.yaml (99%) delete mode 100755 hack/test.sh create mode 100644 internal/policy/commit/check_gpg_identity.go diff --git a/.conform.yaml b/.conform.yaml index 5429f01c..ecb4bd49 100644 --- a/.conform.yaml +++ b/.conform.yaml @@ -9,7 +9,10 @@ policies: body: required: true dco: true - gpg: false + gpg: + required: true + identity: + gitHubOrganization: talos-systems spellcheck: locale: US maximumOfOneCommit: true diff --git a/.drone.yml b/.drone.yml index 3dcbf4a1..d5ae777d 100644 --- a/.drone.yml +++ b/.drone.yml @@ -4,7 +4,7 @@ name: default services: - name: docker - image: docker:19.03-dind + image: docker:20.10-dind entrypoint: [dockerd] privileged: true volumes: diff --git a/hack/golangci-lint.yaml b/.golangci-lint.yaml similarity index 99% rename from hack/golangci-lint.yaml rename to .golangci-lint.yaml index 7d40d603..b2646577 100644 --- a/hack/golangci-lint.yaml +++ b/.golangci-lint.yaml @@ -112,6 +112,8 @@ linters: - exhaustivestruct - errorlint - wrapcheck + - forbidigo + - paralleltest disable-all: false fast: false diff --git a/Dockerfile b/Dockerfile index 64b4a9d3..d099d3be 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,9 +26,9 @@ RUN go build -o /conform-${GOOS}-${GOARCH} -ldflags "-s -w -X \"github.com/talos FROM common AS test ENV GOOS linux ENV GOARCH amd64 -COPY ./hack ./hack -RUN chmod +x ./hack/test.sh -RUN ./hack/test.sh --all +RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.42.0 +RUN golangci-lint run +RUN CGO_ENABLED=1 go test -v -race -covermode=atomic -coverprofile=/coverage.txt ./... FROM alpine:3.11 as ca-certificates RUN apk add --update --no-cache ca-certificates diff --git a/Makefile b/Makefile index 3034cda0..388ab0de 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ SHA ?= $(shell git describe --match=none --always --abbrev=8) TAG ?= $(shell git describe --tag --always) -GOLANG_IMAGE ?= golang:1.14 +GOLANG_IMAGE ?= golang:1.16 COMMON_ARGS := -f ./Dockerfile --build-arg GOLANG_IMAGE=$(GOLANG_IMAGE) --build-arg SHA=$(SHA) --build-arg TAG=$(TAG) . @@ -23,6 +23,7 @@ build: test: @docker build \ + --network=host \ -t conform/$@:$(TAG) \ --target=$@ \ $(COMMON_ARGS) diff --git a/cmd/enforce.go b/cmd/enforce.go index f9cae9c2..1088e2e7 100644 --- a/cmd/enforce.go +++ b/cmd/enforce.go @@ -43,11 +43,7 @@ var enforceCmd = &cobra.Command{ opts = append(opts, policy.WithCommitRef(commitRef)) } - if err := e.Enforce(opts...); err != nil { - return err - } - - return nil + return e.Enforce(opts...) }, } diff --git a/go.mod b/go.mod index f6401903..3a98768a 100644 --- a/go.mod +++ b/go.mod @@ -4,24 +4,25 @@ require ( github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect github.com/deckarep/golang-set v1.7.1 // indirect github.com/denormal/go-gitignore v0.0.0-20180930084346-ae8ad1d07817 - github.com/go-git/go-git/v5 v5.0.0 + github.com/go-git/go-git/v5 v5.4.2 github.com/golangci/misspell v0.3.4 github.com/google/go-cmp v0.3.1 // indirect github.com/google/go-github v17.0.0+incompatible github.com/google/go-querystring v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 github.com/mingrammer/commonregex v1.0.1 // indirect github.com/mitchellh/mapstructure v0.0.0-20170523030023-d0303fe80992 github.com/montanaflynn/stats v0.5.0 // indirect github.com/neurosnap/sentences v1.0.6 // indirect - github.com/pkg/errors v0.8.1 + github.com/pkg/errors v0.9.1 github.com/spf13/cobra v0.0.3 github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/net v0.0.0-20201021035429-f5854403a974 // indirect + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c gonum.org/v1/gonum v0.6.1 // indirect gopkg.in/jdkato/prose.v2 v2.0.0-20180825173540-767a23049b9e gopkg.in/neurosnap/sentences.v1 v1.0.6 // indirect - gopkg.in/yaml.v2 v2.2.4 + gopkg.in/yaml.v2 v2.3.0 ) go 1.13 diff --git a/go.sum b/go.sum index f675cacd..43e50f15 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,11 @@ +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= +github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= +github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= +github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= -github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -24,12 +29,13 @@ github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= -github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM= -github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp/pqnefH+Bc= -github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= -github.com/go-git/go-git/v5 v5.0.0 h1:k5RWPm4iJwYtfWoxIJy4wJX9ON7ihPeZZYC1fLYDnpg= -github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA= +github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= +github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8= +github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= +github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= +github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golangci/misspell v0.3.4 h1:kAD5J0611Zo2VirUn9KFP15RjEqkmfEfnFxyIVxZCYg= github.com/golangci/misspell v0.3.4/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= @@ -40,21 +46,28 @@ github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4r github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= -github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 h1:cTxwSmnaqLoo+4tLukHoB9iqHOu3LmLhRmgUxZo6Vp4= +github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 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/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= +github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mingrammer/commonregex v1.0.1 h1:QY0Z1Bl80jw9M3+488HJXPWnZmvtu3UdvxyodP2FTyY= github.com/mingrammer/commonregex v1.0.1/go.mod h1:/HNZq7qReKgXBxJxce5SOxf33y0il/ZqL4Kxgo2NLcA= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -65,53 +78,52 @@ github.com/montanaflynn/stats v0.5.0 h1:2EkzeTSqBB4V4bJwWrt5gIIrZmpJBcoIRGS2kWLg github.com/montanaflynn/stats v0.5.0/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/neurosnap/sentences v1.0.6 h1:iBVUivNtlwGkYsJblWV8GGVFmXzZzak907Ci8aA0VTE= github.com/neurosnap/sentences v1.0.6/go.mod h1:pg1IapvYpWCJJm/Etxeh0+gtMf1rI1STY9S7eUCPbDc= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= -github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= +github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de h1:xSjD6HQTqT0H/k60N5yYBtnN1OEkVy7WIo/DYyxKRO0= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2 h1:y102fOLFqhV41b+4GPiJoa0k/x+pJcEi2/HB1Y5T6fU= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210326060303-6b1517762897 h1:KrsHThm5nFk34YtATK1LsThyGhGbGe1olrte/HInHvs= +golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79 h1:RX8C8PRZc2hTIod4ds8ij+/4RQX3AqhYj3uOHmyaz4E= +golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -124,18 +136,20 @@ gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPj gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/jdkato/prose.v2 v2.0.0-20180825173540-767a23049b9e h1:yDDVJ347kU7Ro+H2CRgKmAEbFXfHwjyPuXgVd6H+3N8= gopkg.in/jdkato/prose.v2 v2.0.0-20180825173540-767a23049b9e/go.mod h1:1uCyb8jSeRMeIfMJgVyxYssmCTAlxLBkueX+Iu2UilA= gopkg.in/neurosnap/sentences.v1 v1.0.6 h1:v7ElyP020iEZQONyLld3fHILHWOPs+ntzuQTNPkul8E= gopkg.in/neurosnap/sentences.v1 v1.0.6/go.mod h1:YlK+SN+fLQZj+kY3r8DkGDhDr91+S3JmTb5LSxFRQo0= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/hack/test.sh b/hack/test.sh deleted file mode 100755 index bbb10aad..00000000 --- a/hack/test.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash - -set -e - -CGO_ENABLED=1 - -lint_packages() { - if [ "${lint}" = true ]; then - echo "linting packages" - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $GOPATH/bin v1.32.2 - golangci-lint run --config "${BASH_SOURCE%/*}/golangci-lint.yaml" - fi -} - -go_test() { - if [ "${short}" = true ]; then - echo "performing short tests" - go test -v -short ./... - fi - - if [ "${tests}" = true ]; then - echo "performing tests" - go test -v -race -covermode=atomic -coverprofile=/coverage.txt ./... - fi -} - -lint=false -short=false -tests=false - -case $1 in - --lint) - lint=true - ;; - --short) - short=true - ;; - --integration) - tests=true - ;; - --all) - lint=true - short=true - tests=true - ;; - *) - ;; -esac - -go_test -lint_packages - -exit 0 diff --git a/internal/enforcer/enforcer.go b/internal/enforcer/enforcer.go index 8cdfe137..c39f4f1c 100644 --- a/internal/enforcer/enforcer.go +++ b/internal/enforcer/enforcer.go @@ -99,7 +99,7 @@ func (c *Conform) Enforce(setters ...policy.Option) error { pass = false } else { - fmt.Fprintf(w, "%s\t%s\t%s\t%s\t\n", p.Type, check.Name(), "PASS", "") + fmt.Fprintf(w, "%s\t%s\t%s\t%s\t\n", p.Type, check.Name(), "PASS", check.Message()) if err := c.reporter.SetStatus("success", p.Type, check.Name(), check.Message()); err != nil { log.Printf("WARNING: report failed: %+v", err) @@ -125,6 +125,19 @@ func (c *Conform) enforce(declaration *PolicyDeclaration, opts *policy.Options) p := policyMap[declaration.Type] + // backwards compatibility, convert `gpg: bool` into `gpg: required: bool` + if declaration.Type == "commit" { + if spec, ok := declaration.Spec.(map[interface{}]interface{}); ok { + if gpg, ok := spec["gpg"]; ok { + if val, ok := gpg.(bool); ok { + spec["gpg"] = map[string]interface{}{ + "required": val, + } + } + } + } + } + err := mapstructure.Decode(declaration.Spec, p) if err != nil { return nil, errors.Errorf("Internal error: %v", err) diff --git a/internal/git/git.go b/internal/git/git.go index 623598c7..e44bc039 100644 --- a/internal/git/git.go +++ b/internal/git/git.go @@ -10,12 +10,14 @@ import ( "os" "path" "path/filepath" + "strings" git "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/plumbing/storer" + "github.com/keybase/go-crypto/openpgp" ) // Git is a helper for git. @@ -101,6 +103,49 @@ func (g *Git) HasGPGSignature() (ok bool, err error) { return ok, err } +// VerifyPGPSignature validates PGP signature against a keyring. +func (g *Git) VerifyPGPSignature(armoredKeyrings []string) (*openpgp.Entity, error) { + ref, err := g.repo.Head() + if err != nil { + return nil, err + } + + commit, err := g.repo.CommitObject(ref.Hash()) + if err != nil { + return nil, err + } + + var keyring openpgp.EntityList + + for _, armoredKeyring := range armoredKeyrings { + var el openpgp.EntityList + + el, err = openpgp.ReadArmoredKeyRing(strings.NewReader(armoredKeyring)) + if err != nil { + return nil, err + } + + keyring = append(keyring, el...) + } + + // Extract signature. + signature := strings.NewReader(commit.PGPSignature) + + encoded := &plumbing.MemoryObject{} + + // Encode commit components, excluding signature and get a reader object. + if err = commit.EncodeWithoutSignature(encoded); err != nil { + return nil, err + } + + er, err := encoded.Reader() + if err != nil { + return nil, err + } + + return openpgp.CheckArmoredDetachedSignature(keyring, er, signature) +} + // FetchPullRequest fetches a remote PR. func (g *Git) FetchPullRequest(remote string, number int) (err error) { opts := &git.FetchOptions{ @@ -110,11 +155,7 @@ func (g *Git) FetchPullRequest(remote string, number int) (err error) { }, } - if err = g.repo.Fetch(opts); err != nil { - return err - } - - return nil + return g.repo.Fetch(opts) } // CheckoutPullRequest checks out pull request. @@ -128,11 +169,7 @@ func (g *Git) CheckoutPullRequest(number int) (err error) { Branch: plumbing.ReferenceName(fmt.Sprintf("pr/%d", number)), } - if err := w.Checkout(opts); err != nil { - return err - } - - return nil + return w.Checkout(opts) } // SHA returns the sha of the current commit. @@ -162,7 +199,7 @@ func (g *Git) AheadBehind(ref string) (ahead int, behind int, err error) { commit2, err := object.GetCommit(g.repo.Storer, ref2.Hash()) if err != nil { - return 0, 0, nil + return 0, 0, nil //nolint:nilerr } var count int @@ -180,7 +217,7 @@ func (g *Git) AheadBehind(ref string) (ahead int, behind int, err error) { }) if err != nil { - return 0, 0, nil + return 0, 0, nil //nolint:nilerr } return count, 0, nil diff --git a/internal/policy/commit/check_conventional_commit.go b/internal/policy/commit/check_conventional_commit.go index 227082e2..91cf2c0a 100644 --- a/internal/policy/commit/check_conventional_commit.go +++ b/internal/policy/commit/check_conventional_commit.go @@ -61,7 +61,7 @@ func (c ConventionalCommitCheck) Errors() []error { } // ValidateConventionalCommit returns the commit type. -// nolint: gocyclo +//nolint:gocyclo,cyclop func (c Commit) ValidateConventionalCommit() policy.Check { check := &ConventionalCommitCheck{} groups := parseHeader(c.msg) diff --git a/internal/policy/commit/check_gpg_identity.go b/internal/policy/commit/check_gpg_identity.go new file mode 100644 index 00000000..b2fe73e8 --- /dev/null +++ b/internal/policy/commit/check_gpg_identity.go @@ -0,0 +1,137 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package commit + +import ( + "context" + "fmt" + "io/ioutil" + "net/http" + "sync" + + "github.com/google/go-github/github" + "golang.org/x/sync/errgroup" + + "github.com/talos-systems/conform/internal/git" + "github.com/talos-systems/conform/internal/policy" +) + +// GPGIdentityCheck ensures that the commit is cryptographically signed using known identity. +type GPGIdentityCheck struct { + errors []error + identity string +} + +// Name returns the name of the check. +func (g GPGIdentityCheck) Name() string { + return "GPG Identity" +} + +// Message returns to check message. +func (g GPGIdentityCheck) Message() string { + if len(g.errors) != 0 { + return g.errors[0].Error() + } + + return fmt.Sprintf("Signed by %q", g.identity) +} + +// Errors returns any violations of the check. +func (g GPGIdentityCheck) Errors() []error { + return g.errors +} + +// ValidateGPGIdentity checks the commit GPG signature for a known identity. +func (c Commit) ValidateGPGIdentity(g *git.Git) policy.Check { + check := &GPGIdentityCheck{} + + switch { + case c.GPG.Identity.GitHubOrganization != "": + githubClient := github.NewClient(nil) + + list, _, err := githubClient.Organizations.ListMembers(context.Background(), c.GPG.Identity.GitHubOrganization, &github.ListMembersOptions{}) + if err != nil { + check.errors = append(check.errors, err) + + return check + } + + members := make([]string, len(list)) + + for i := range list { + members[i] = list[i].GetLogin() + } + + keyrings, err := getKeyring(context.Background(), members) + if err != nil { + check.errors = append(check.errors, err) + + return check + } + + entity, err := g.VerifyPGPSignature(keyrings) + if err != nil { + check.errors = append(check.errors, err) + + return check + } + + for identity := range entity.Identities { + check.identity = identity + + break + } + default: + check.errors = append(check.errors, fmt.Errorf("no signature identity configuration found")) + } + + return check +} + +func getKeyring(ctx context.Context, members []string) ([]string, error) { + var ( + result []string + mu sync.Mutex + ) + + eg, ctx := errgroup.WithContext(ctx) + + for _, member := range members { + member := member + + eg.Go(func() error { + key, err := getKey(ctx, member) + + mu.Lock() + result = append(result, key) + mu.Unlock() + + return err + }) + } + + err := eg.Wait() + + return result, err +} + +func getKey(ctx context.Context, login string) (string, error) { + // GitHub client doesn't have a method to fetch a key unauthenticated + req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://github.com/%s.gpg", login), nil) + if err != nil { + return "", err + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + + defer resp.Body.Close() //nolint:errcheck + + buf, err := ioutil.ReadAll(resp.Body) + + return string(buf), err +} diff --git a/internal/policy/commit/check_gpg_signature.go b/internal/policy/commit/check_gpg_signature.go index a343c649..649a24b0 100644 --- a/internal/policy/commit/check_gpg_signature.go +++ b/internal/policy/commit/check_gpg_signature.go @@ -46,11 +46,11 @@ func (c Commit) ValidateGPGSign(g *git.Git) policy.Check { return check } - if ok { + if !ok { + check.errors = append(check.errors, errors.Errorf("Commit does not have a GPG signature")) + return check } - check.errors = append(check.errors, errors.Errorf("Commit does not have a GPG signature")) - return check } diff --git a/internal/policy/commit/check_jira_test.go b/internal/policy/commit/check_jira_test.go index d628c284..9b933e1b 100644 --- a/internal/policy/commit/check_jira_test.go +++ b/internal/policy/commit/check_jira_test.go @@ -131,12 +131,14 @@ func TestCommit_ValidateJiraCheck(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { c := Commit{ - SpellCheck: tt.fields.SpellCheck, - Conventional: tt.fields.Conventional, - Header: tt.fields.Header, - Body: tt.fields.Body, - DCO: tt.fields.DCO, - GPG: tt.fields.GPG, + SpellCheck: tt.fields.SpellCheck, + Conventional: tt.fields.Conventional, + Header: tt.fields.Header, + Body: tt.fields.Body, + DCO: tt.fields.DCO, + GPG: &GPG{ + Required: tt.fields.GPG, + }, MaximumOfOneCommit: tt.fields.MaximumOfOneCommit, msg: tt.fields.msg, } diff --git a/internal/policy/commit/commit.go b/internal/policy/commit/commit.go index db13f7af..63e25f00 100644 --- a/internal/policy/commit/commit.go +++ b/internal/policy/commit/commit.go @@ -42,8 +42,22 @@ type BodyChecks struct { Required bool `mapstructure:"required"` } +// GPG is the configuration for checks GPG signature on the commit. +type GPG struct { + // Required enforces that the current commit has a signature. + Required bool `mapstructure:"required"` + // Identity configures identity of the signature. + Identity *struct { + // GitHubOrganization enforces that commit should be signed with the key + // of one of the organization public members. + GitHubOrganization string `mapstructure:"gitHubOrganization"` + } `mapstructure:"identity"` +} + // Commit implements the policy.Policy interface and enforces commit // messages to conform the Conventional Commit standard. +// +//nolint:maligned type Commit struct { // SpellCheck enforces correct spelling. SpellCheck *SpellCheck `mapstructure:"spellcheck"` @@ -55,8 +69,11 @@ type Commit struct { Body *BodyChecks `mapstructure:"body"` // DCO enables the Developer Certificate of Origin check. DCO bool `mapstructure:"dco"` - // GPG enables the GPG signature check. - GPG bool `mapstructure:"gpg"` + // GPG is the user specified settings for the GPG signature check. + GPG *GPG `mapstructure:"gpg"` + // GPGSignatureGitHubOrganization enforces that GPG signature should come from + // one of the members of the GitHub org. + GPGSignatureGitHubOrganization string `mapstructure:"gpgSignatureGitHubOrg"` // MaximumOfOneCommit enforces that the current commit is only one commit // ahead of a specified ref. MaximumOfOneCommit bool `mapstructure:"maximumOfOneCommit"` @@ -69,14 +86,13 @@ type Commit struct { var FirstWordRegex = regexp.MustCompile(`^\s*([a-zA-Z0-9]+)`) // Compliance implements the policy.Policy.Compliance function. -// nolint: gocyclo +//nolint: gocyclo,cyclop func (c *Commit) Compliance(options *policy.Options) (*policy.Report, error) { var err error report := &policy.Report{} // Setup the policy for all checks. - var g *git.Git if g, err = git.NewGit(); err != nil { @@ -125,8 +141,14 @@ func (c *Commit) Compliance(options *policy.Options) (*policy.Report, error) { report.AddCheck(c.ValidateDCO()) } - if c.GPG { - report.AddCheck(c.ValidateGPGSign(g)) + if c.GPG != nil { + if c.GPG.Required { + report.AddCheck(c.ValidateGPGSign(g)) + + if c.GPG.Identity != nil { + report.AddCheck(c.ValidateGPGIdentity(g)) + } + } } if c.Conventional != nil { @@ -158,11 +180,11 @@ func (c Commit) firstWord() (string, error) { if c.Conventional != nil { groups = parseHeader(c.msg) - if len(groups) != 6 { + if len(groups) != 7 { return "", errors.Errorf("Invalid conventional commit format") } - msg = groups[4] + msg = groups[5] } else { msg = c.msg } diff --git a/internal/policy/commit/commit_test.go b/internal/policy/commit/commit_test.go index caf28127..eb23e115 100644 --- a/internal/policy/commit/commit_test.go +++ b/internal/policy/commit/commit_test.go @@ -16,8 +16,7 @@ import ( ) func RemoveAll(dir string) { - err := os.RemoveAll(dir) - if err != nil { + if err := os.RemoveAll(dir); err != nil { log.Fatal(err) } } diff --git a/internal/policy/license/license.go b/internal/policy/license/license.go index e1861c6b..08ec9f3d 100644 --- a/internal/policy/license/license.go +++ b/internal/policy/license/license.go @@ -70,7 +70,8 @@ func (l HeaderCheck) Errors() []error { // ValidateLicenseHeader checks the header of a file and ensures it contains the // provided value. -// nolint: gocyclo +// +//nolint:gocyclo,cyclop func (l License) ValidateLicenseHeader() policy.Check { var buf bytes.Buffer @@ -125,7 +126,7 @@ func (l License) ValidateLicenseHeader() policy.Check { if contents, err = ioutil.ReadFile(path); err != nil { check.errors = append(check.errors, errors.Errorf("Failed to open %s", path)) - return nil + return nil //nolint:nilerr } if bytes.HasPrefix(contents, value) { diff --git a/internal/reporter/reporter.go b/internal/reporter/reporter.go index a7b6901c..31012cea 100644 --- a/internal/reporter/reporter.go +++ b/internal/reporter/reporter.go @@ -36,8 +36,7 @@ type GitHub struct { } // Noop is a reporter that does nothing. -type Noop struct { -} +type Noop struct{} // SetStatus is a noop func. func (n *Noop) SetStatus(state, policy, check, message string) error { @@ -97,8 +96,8 @@ func NewGitHubReporter() (*GitHub, error) { } // SetStatus sets the status of a GitHub check. -// Valid statuses are "error", "failure", "pending", "success" -// nolint: godot +// +// Valid statuses are "error", "failure", "pending", "success". func (gh *GitHub) SetStatus(state, policy, check, message string) error { if gh.token == "" { return errors.New("no token")