Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Git signatures #64

Merged
merged 1 commit into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion cmd/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,24 @@ var statusCmd = &cobra.Command{
}
fmt.Printf(" Need to reboot: %s\n", needToReboot)
fmt.Printf(" Fetcher\n")
if status.Fetcher.RepositoryStatus.SelectedCommitShouldBeSigned {
if status.Fetcher.RepositoryStatus.SelectedCommitSigned {
fmt.Printf(" Commit %s signed by %s\n", status.Fetcher.RepositoryStatus.SelectedCommitId, status.Fetcher.RepositoryStatus.SelectedCommitSignedBy)
} else {
fmt.Printf(" Commit %s is not signed while it should be\n", status.Fetcher.RepositoryStatus.SelectedCommitId)
}
}
for _, r := range status.Fetcher.RepositoryStatus.Remotes {
fmt.Printf(" Remote %s %s fetched %s\n",
r.Name, r.Url, humanize.Time(r.FetchedAt),
)
}
fmt.Printf(" Builder\n")
builder.GenerationShow(*status.Builder.Generation)
if status.Builder.Generation != nil {
builder.GenerationShow(*status.Builder.Generation)
} else {
fmt.Printf(" No build available\n")
}
status.Deployer.Show(" ")
},
}
Expand Down
18 changes: 18 additions & 0 deletions docs/generated-module-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,24 @@ string



## services\.comin\.gpgPublicKeyPaths



A list of GPG public key file paths\. Each of this file should contains an armored GPG key\.



*Type:*
list of Concatenated string



*Default:*
` [ ] `



## services\.comin\.hostname


Expand Down
8 changes: 8 additions & 0 deletions docs/howtos.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,11 @@ the `/etc/machine-id` file), comin won't deploy the configuration.
So, to migrate to another machine, you have to update this
option in the `testing-<hostname>` branch in order to only deploy this
configuration to the new machine.

## Check Git commit signatures

The option `services.comin.gpgPublicKeyPaths` allows to declare a list
of GPG public keys. If `services.comin.gpgPublicKeyPaths != []`, comin **only** evaluates commits signed
by one of these GPG keys. Note only the last commit needs to be signed.

The file containing a GPG public key has to be created with `gpg --armor --export [email protected]`.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/nlewo/comin
go 1.22

require (
github.com/ProtonMail/go-crypto v1.1.5
github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df
github.com/dustin/go-humanize v1.0.1
github.com/go-co-op/gocron/v2 v2.11.0
Expand All @@ -18,10 +19,9 @@ require (
require (
dario.cat/mergo v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cloudflare/circl v1.3.6 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
Expand Down
47 changes: 4 additions & 43 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE=
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4=
github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
Expand All @@ -13,12 +13,10 @@ github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df h1:GSoSVRLo
github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df/go.mod h1:hiVxq5OP2bUGBRNS3Z/bt/reCLFNbdcST6gISi1fiOM=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cloudflare/circl v1.3.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg=
github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
Expand Down Expand Up @@ -104,74 +102,37 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
14 changes: 8 additions & 6 deletions internal/config/config.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package config

import (
"github.com/nlewo/comin/internal/types"
"github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"
"os"
"path/filepath"
"strings"

"github.com/nlewo/comin/internal/types"
"github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"
)

func Read(path string) (config types.Configuration, err error) {
Expand Down Expand Up @@ -57,8 +58,9 @@ func Read(path string) (config types.Configuration, err error) {

func MkGitConfig(config types.Configuration) types.GitConfig {
return types.GitConfig{
Path: filepath.Join(config.StateDir, "repository"),
Dir: config.FlakeSubdirectory,
Remotes: config.Remotes,
Path: filepath.Join(config.StateDir, "repository"),
Dir: config.FlakeSubdirectory,
Remotes: config.Remotes,
GpgPublicKeyPaths: config.GpgPublicKeyPaths,
}
}
8 changes: 6 additions & 2 deletions internal/manager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,12 @@ func (m *Manager) FetchAndBuild() {
for {
select {
case rs := <-m.Fetcher.RepositoryStatusCh:
logrus.Infof("manager: a generation is evaluating for commit %s", rs.SelectedCommitId)
m.builder.Eval(rs)
if !rs.SelectedCommitShouldBeSigned || rs.SelectedCommitSigned {
logrus.Infof("manager: a generation is evaluating for commit %s", rs.SelectedCommitId)
m.builder.Eval(rs)
} else {
logrus.Infof("manager: the commit %s is not evaluated because it is not signed", rs.SelectedCommitId)
}
case generation := <-m.builder.EvaluationDone:
if generation.EvalErr != nil {
continue
Expand Down
13 changes: 13 additions & 0 deletions internal/repository/fail.public
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----

mDMEZ4oDaRYJKwYBBAHaRw8BAQdA91zbRSdMphKMs7wP+3/mOpDkxEfeWrfblS5t
uf5xw1O0F2ZhaWwgPGZhaWxAY29taW4uc3BhY2U+iJQEExYKADwWIQSNo3AzK05c
jADI4rwfTCYbHTKLkgUCZ4oDaQIbAwUJBaOagAQLCQgHBBUKCQgFFgIDAQACHgUC
F4AACgkQH0wmGx0yi5IEyAD/ck8A4aPUK8+g7EzMLRnl+twUccwmS7wIthLsA7Sm
s0sA/2RMyImXOK82hesQi8VqV/XNsu/n5Lg6bAfkTHQR1CwLuDgEZ4oDaRIKKwYB
BAGXVQEFAQEHQLr2P/jpdMyluCmFv1mmtHxNy4rOAstT61B+Zsq+8/wtAwEIB4h+
BBgWCgAmFiEEjaNwMytOXIwAyOK8H0wmGx0yi5IFAmeKA2kCGwwFCQWjmoAACgkQ
H0wmGx0yi5IXxwD6AwMQTzw4uXuMJiNC3lsaX5+L9vJDy4tSu/bufc4EKPoA/iiu
kbksGGr4c6gTHOovFhEklvJhjPcEcwdvdEnioWgL
=zjq4
-----END PGP PUBLIC KEY BLOCK-----
28 changes: 9 additions & 19 deletions internal/repository/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package repository
import (
"context"
"fmt"
"io/ioutil"
"time"

"github.com/ProtonMail/go-crypto/openpgp"
"github.com/go-git/go-git/v5"
gitConfig "github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
Expand Down Expand Up @@ -222,31 +222,21 @@ func manageRemote(r *git.Repository, remote types.Remote) error {
return nil
}

func verifyHead(r *git.Repository, config types.GitConfig) error {
func headSignedBy(r *git.Repository, publicKeys []string) (signedBy *openpgp.Entity, err error) {
head, err := r.Head()
if head == nil {
return fmt.Errorf("Repository HEAD should not be nil")
return nil, fmt.Errorf("Repository HEAD should not be nil")
}
logrus.Debugf("Repository HEAD is %s", head.Strings()[1])

commit, err := r.CommitObject(head.Hash())
if err != nil {
return err
return nil, err
}

for _, keyPath := range config.GpgPublicKeyPaths {
key, err := ioutil.ReadFile(keyPath)
if err != nil {
return err
}
entity, err := commit.Verify(string(key))
if err != nil {
logrus.Debug(err)
} else {
for _, k := range publicKeys {
entity, err := commit.Verify(k)
if err == nil {
logrus.Debugf("Commit %s signed by %s", head.Hash(), entity.PrimaryIdentity().Name)
return nil
return entity, nil
}

}
return fmt.Errorf("Commit %s is not signed", head.Hash())
return nil, fmt.Errorf("Commit %s is not signed", head.Hash())
}
42 changes: 37 additions & 5 deletions internal/repository/git_test.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
package repository

import (
"os"
"path/filepath"
"testing"
"time"

"github.com/ProtonMail/go-crypto/openpgp"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/stretchr/testify/assert"
"io/ioutil"
"path/filepath"
"testing"
"time"
)

func commitFile(remoteRepository *git.Repository, dir, branch, content string) (commitId string, err error) {
return commitFileAndSign(remoteRepository, dir, branch, content, nil)
}

func commitFileAndSign(remoteRepository *git.Repository, dir, branch, content string, signKey *openpgp.Entity) (commitId string, err error) {
w, err := remoteRepository.Worktree()
if err != nil {
return
Expand All @@ -22,7 +28,7 @@ func commitFile(remoteRepository *git.Repository, dir, branch, content string) (
})

filename := filepath.Join(dir, content)
err = ioutil.WriteFile(filename, []byte(content), 0644)
err = os.WriteFile(filename, []byte(content), 0644)
if err != nil {
return
}
Expand All @@ -36,6 +42,7 @@ func commitFile(remoteRepository *git.Repository, dir, branch, content string) (
Email: "[email protected]",
When: time.Unix(0, 0),
},
SignKey: signKey,
})
if err != nil {
return
Expand Down Expand Up @@ -119,3 +126,28 @@ func TestIsAncestor(t *testing.T) {

//time.Sleep(100*time.Second)
}

func TestHeadSignedBy(t *testing.T) {
dir := t.TempDir()
remoteRepository, _ := git.PlainInit(dir, false)

r, err := os.Open("./test.private")
entityList, _ := openpgp.ReadArmoredKeyRing(r)
commitFileAndSign(remoteRepository, dir, "main", "file-1", entityList[0])

failPublic, _ := os.ReadFile("./fail.public")
testPublic, _ := os.ReadFile("./test.public")
signedBy, err := headSignedBy(remoteRepository, []string{string(failPublic), string(testPublic)})
assert.Nil(t, err)
assert.Equal(t, "test <[email protected]>", signedBy.PrimaryIdentity().Name)

signedBy, err = headSignedBy(remoteRepository, []string{string(failPublic)})
assert.ErrorContains(t, err, "is not signed")
assert.Nil(t, signedBy)

commitFileAndSign(remoteRepository, dir, "main", "file-2", nil)
signedBy, err = headSignedBy(remoteRepository, []string{string(failPublic), string(testPublic)})
assert.ErrorContains(t, err, "is not signed")
assert.Nil(t, signedBy)

}
1 change: 1 addition & 0 deletions internal/repository/invalid.public
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Not a valid armored GPG pub key
Loading