feat(github): add github support #9

Merged
kjuulh merged 2 commits from feat/with-github-support into main 2023-08-09 15:48:38 +02:00
12 changed files with 454 additions and 28 deletions

View File

@ -41,15 +41,13 @@ what the project will look like once feature-complete.
- Includes basic setup such as working server bot, and installation command,
automation is missing however. Also only gitea support for now, because this
is where the project initially is supposed to be in use.
- [ ] 0.2.0
- Add GitHub support
- [x] 0.2.0
- Add GitHub support, only github app support for now. This means that install is not needed, because a github app will automatically receive webhooks if setup properly. docs are missing for this (tbd).
- [ ] 0.3.0
- Add Delegation support (not clustering, just delegation of renovate jobs)
- [ ] 0.4.0
- Slack integration
- [ ] 0.5.0
- GitHub App and such support
- [ ] 0.6.0
- Add api key support
## Getting started

View File

@ -50,16 +50,23 @@ func serverCmd() *cobra.Command {
var (
url string
token string
githubAppID int64
githubInstallationID int64
githubPrivateKeyPath string
)
giteaClient := providers.NewGiteaClient(&url, &token)
githubClient := providers.NewGitHubClient(&githubAppID, &githubInstallationID, &githubPrivateKeyPath)
renovateClient := renovate.NewRenovateClient("")
queue := queue.NewGoQueue()
botHandler := bot.NewBotHandler(giteaClient)
botHandler := bot.NewBotHandler(giteaClient, githubClient)
giteaWebhook := features.NewGiteaWebhook(botHandler, queue)
githubWebhook := features.NewGitHubWebhook(botHandler, queue)
features.RegisterGiteaQueues(queue, renovateClient, giteaClient)
features.RegisterGitHubQueues(queue, renovateClient, githubClient)
cmd := &cobra.Command{
Use: "server",
@ -68,7 +75,11 @@ func serverCmd() *cobra.Command {
cmd.PersistentFlags().StringVar(&url, "url", "", "the api url of the server")
cmd.PersistentFlags().StringVar(&token, "token", "", "the token to authenticate with")
cmd.AddCommand(serverServeCmd(&url, &token, giteaWebhook))
cmd.PersistentFlags().Int64Var(&githubAppID, "github-app-id", 0, "github app id to authenticate with")
cmd.PersistentFlags().Int64Var(&githubInstallationID, "github-installation-id", 0, "github installation id to authenticate with")
cmd.PersistentFlags().StringVar(&githubPrivateKeyPath, "github-private-key-path", "", "path to the github app private key")
cmd.AddCommand(serverServeCmd(&url, &token, giteaWebhook, githubWebhook))
return cmd
}
@ -77,16 +88,34 @@ func serverServeCmd(
url *string,
token *string,
giteaWebhook *features.GiteaWebhook,
githubWebhook *features.GitHubWebhook,
) *cobra.Command {
cmd := &cobra.Command{
Use: "serve",
Run: func(cmd *cobra.Command, args []string) {
engine := gin.Default()
github := engine.Group("/github")
{
github.POST("/webhook", func(ctx *gin.Context) {
var request features.GitHubWebhookRequest
if err := ctx.BindJSON(&request); err != nil {
ctx.AbortWithError(500, err)
return
}
if err := githubWebhook.HandleGitHubWebhook(ctx.Request.Context(), &request); err != nil {
ctx.AbortWithError(500, err)
return
}
ctx.Status(204)
})
}
gitea := engine.Group("/gitea")
{
gitea.POST("/webhook", func(ctx *gin.Context) {
var request features.GiteaWebhookRequest
if err := ctx.BindJSON(&request); err != nil {
ctx.AbortWithError(500, err)
@ -102,7 +131,7 @@ func serverServeCmd(
})
}
engine.Run("0.0.0.0:8080")
engine.Run("0.0.0.0:9111")
},
}

9
go.mod
View File

@ -5,6 +5,7 @@ go 1.20
require (
dagger.io/dagger v0.8.1
github.com/gin-gonic/gin v1.9.1
github.com/google/go-github/v53 v53.2.0
github.com/google/uuid v1.3.0
github.com/joho/godotenv v1.5.1
github.com/spf13/cobra v1.7.0
@ -13,15 +14,21 @@ require (
require (
github.com/99designs/gqlgen v0.17.31 // indirect
github.com/Khan/genqlient v0.6.0 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect
github.com/adrg/xdg v0.4.0 // indirect
github.com/bradleyfalzon/ghinstallation/v2 v2.6.0 // indirect
github.com/bytedance/sonic v1.9.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
@ -39,10 +46,12 @@ require (
golang.org/x/crypto v0.11.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.12.0 // indirect
golang.org/x/oauth2 v0.8.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/text v0.11.0 // indirect
golang.org/x/tools v0.11.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

78
go.sum
View File

@ -1,9 +1,12 @@
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
dagger.io/dagger v0.8.1 h1:jLNPGubxrLWUfsX+snjaw913B1lxVmWftzdVehB+RQU=
dagger.io/dagger v0.8.1/go.mod h1:CZwYt0FfVsEEYTFytzf2ihESB2P4H1S3/UfnrVxjBsE=
github.com/99designs/gqlgen v0.17.31 h1:VncSQ82VxieHkea8tz11p7h/zSbvHSxSDZfywqWt158=
github.com/99designs/gqlgen v0.17.31/go.mod h1:i4rEatMrzzu6RXaHydq1nmEPZkb3bKQsnxNRHS4DQB4=
github.com/Khan/genqlient v0.6.0 h1:Bwb1170ekuNIVIwTJEqvO8y7RxBxXu639VJOkKSrwAk=
github.com/Khan/genqlient v0.6.0/go.mod h1:rvChwWVTqXhiapdhLDV4bp9tz/Xvtewwkon4DpWWCRM=
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA=
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
@ -11,12 +14,19 @@ github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVb
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/bradleyfalzon/ghinstallation/v2 v2.6.0 h1:IRY7Xy588KylkoycsUhFpW7cdGpy5Y5BPsz4IfuJtGk=
github.com/bradleyfalzon/ghinstallation/v2 v2.6.0/go.mod h1:oQ3etOwN3TRH4EwgW5/7MxSVMGlMlzG/O8TU7eYdoSk=
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@ -37,9 +47,21 @@ github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github/v53 v53.2.0 h1:wvz3FyF53v4BK+AsnvCmeNhf8AkTaeh2SoYu/XUvTtI=
github.com/google/go-github/v53 v53.2.0/go.mod h1:XhFRObz+m/l+UCm9b7KSIC3lT3NWSXGt7mOsAWEloao=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -97,29 +119,81 @@ github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4d
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/vektah/gqlparser/v2 v2.5.6 h1:Ou14T0N1s191eRMZ1gARVqohcbe1e8FrcONScsq8cRU=
github.com/vektah/gqlparser/v2 v2.5.6/go.mod h1:z8xXUff237NntSuH8mLFijZ+1tjV1swDbpDqjJmk6ME=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
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.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.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
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.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
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-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.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
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.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/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-20220704084225-05e143d24a9e/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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/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.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
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.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8=
golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -11,10 +11,11 @@ import (
type BotHandler struct {
giteaClient *providers.GiteaClient
githubClient *providers.GitHubClient
}
func NewBotHandler(gitea *providers.GiteaClient) *BotHandler {
return &BotHandler{giteaClient: gitea}
func NewBotHandler(gitea *providers.GiteaClient, github *providers.GitHubClient) *BotHandler {
return &BotHandler{giteaClient: gitea, githubClient: github}
}
func (b *BotHandler) Handle(input string) (output string, err error) {
@ -62,6 +63,14 @@ func (b *BotHandler) AppendComment(
repository string,
pullRequest int,
comment string,
backend models.SupportedBackend,
) (*models.AddCommentResponse, error) {
switch backend {
case models.SupportedBackendGitHub:
return b.githubClient.AddComment(owner, repository, pullRequest, comment)
case models.SupportedBackendGitea:
return b.giteaClient.AddComment(owner, repository, pullRequest, comment)
default:
panic("backend chosen was not a valid option")
}
}

View File

@ -14,23 +14,23 @@ import (
func RegisterGiteaQueues(goqueue *queue.GoQueue, renovate *renovate.RenovateClient, giteaClient *providers.GiteaClient) {
goqueue.Subscribe(
models.MessageTypeRefreshRepository,
models.MessageTypeRefreshGiteaRepository,
func(ctx context.Context, item *queue.QueueMessage) error {
log.Printf("handling message: %s, content: %s", item.Type, item.Content)
return nil
},
)
goqueue.Subscribe(
models.MessageTypeRefreshRepositoryDone,
models.MessageTypeRefreshGiteaRepositoryDone,
func(ctx context.Context, item *queue.QueueMessage) error {
log.Printf("handling message: %s, content: %s", item.Type, item.Content)
return nil
},
)
goqueue.Subscribe(
models.MessageTypeRefreshRepository,
models.MessageTypeRefreshGiteaRepository,
func(ctx context.Context, item *queue.QueueMessage) error {
var request models.RefreshRepositoryRequest
var request models.RefreshGiteaRepositoryRequest
if err := json.Unmarshal([]byte(item.Content), &request); err != nil {
log.Printf("failed to unmarshal request body: %s", err.Error())
return err
@ -40,7 +40,7 @@ func RegisterGiteaQueues(goqueue *queue.GoQueue, renovate *renovate.RenovateClie
defer cancel()
if err := renovate.RefreshRepository(cancelCtx, request.Owner, request.Repository); err != nil {
goqueue.Insert(models.MessageTypeRefreshRepositoryDone, models.RefreshDoneRepositoryRequest{
goqueue.Insert(models.MessageTypeRefreshGiteaRepositoryDone, models.RefreshGiteaRepositoryDoneRequest{
Repository: request.Repository,
Owner: request.Owner,
PullRequestID: request.PullRequestID,
@ -54,7 +54,7 @@ func RegisterGiteaQueues(goqueue *queue.GoQueue, renovate *renovate.RenovateClie
return err
}
goqueue.Insert(models.MessageTypeRefreshRepositoryDone, models.RefreshDoneRepositoryRequest{
goqueue.Insert(models.MessageTypeRefreshGiteaRepositoryDone, models.RefreshGiteaRepositoryDoneRequest{
Repository: request.Repository,
Owner: request.Owner,
PullRequestID: request.PullRequestID,
@ -70,9 +70,9 @@ func RegisterGiteaQueues(goqueue *queue.GoQueue, renovate *renovate.RenovateClie
)
goqueue.Subscribe(
models.MessageTypeRefreshRepositoryDone,
models.MessageTypeRefreshGiteaRepositoryDone,
func(ctx context.Context, item *queue.QueueMessage) error {
var doneRequest models.RefreshDoneRepositoryRequest
var doneRequest models.RefreshGiteaRepositoryDoneRequest
if err := json.Unmarshal([]byte(item.Content), &doneRequest); err != nil {
log.Printf("failed to unmarshal request body: %s", err.Error())
return err

View File

@ -54,12 +54,13 @@ func (gw *GiteaWebhook) HandleGiteaWebhook(ctx context.Context, request *GiteaWe
parts[1],
request.Issue.Number,
output,
models.SupportedBackendGitea,
)
if err != nil {
return err
}
if err := gw.queue.Insert(models.MessageTypeRefreshRepository, models.RefreshRepositoryRequest{
if err := gw.queue.Insert(models.MessageTypeRefreshGiteaRepository, models.RefreshGiteaRepositoryRequest{
Repository: parts[1],
Owner: parts[0],
PullRequestID: request.Issue.Number,

View File

@ -0,0 +1,84 @@
package features
import (
"context"
"encoding/json"
"log"
"time"
"git.front.kjuulh.io/kjuulh/contractor/internal/models"
"git.front.kjuulh.io/kjuulh/contractor/internal/providers"
"git.front.kjuulh.io/kjuulh/contractor/internal/queue"
"git.front.kjuulh.io/kjuulh/contractor/internal/renovate"
)
func RegisterGitHubQueues(goqueue *queue.GoQueue, renovate *renovate.RenovateClient, giteaClient *providers.GitHubClient) {
goqueue.Subscribe(
models.MessageTypeRefreshGitHubRepository,
func(ctx context.Context, item *queue.QueueMessage) error {
log.Printf("handling message: %s, content: %s", item.Type, item.Content)
return nil
},
)
goqueue.Subscribe(
models.MessageTypeRefreshGitHubRepositoryDone,
func(ctx context.Context, item *queue.QueueMessage) error {
log.Printf("handling message: %s, content: %s", item.Type, item.Content)
return nil
},
)
goqueue.Subscribe(
models.MessageTypeRefreshGitHubRepository,
func(ctx context.Context, item *queue.QueueMessage) error {
var request models.RefreshGitHubRepositoryRequest
if err := json.Unmarshal([]byte(item.Content), &request); err != nil {
log.Printf("failed to unmarshal request body: %s", err.Error())
return err
}
cancelCtx, cancel := context.WithTimeout(ctx, time.Minute*5)
defer cancel()
if err := renovate.RefreshRepository(cancelCtx, request.Owner, request.Repository); err != nil {
goqueue.Insert(models.MessageTypeRefreshGitHubRepositoryDone, models.RefreshGitHubRepositoryDoneRequest{
Repository: request.Repository,
Owner: request.Owner,
PullRequestID: request.PullRequestID,
CommentID: request.CommentID,
CommentBody: request.CommentBody,
ReportProgress: request.ReportProgress,
Status: "failed",
Error: err.Error(),
})
return err
}
goqueue.Insert(models.MessageTypeRefreshGitHubRepositoryDone, models.RefreshGitHubRepositoryDoneRequest{
Repository: request.Repository,
Owner: request.Owner,
PullRequestID: request.PullRequestID,
CommentID: request.CommentID,
CommentBody: request.CommentBody,
ReportProgress: request.ReportProgress,
Status: "done",
Error: "",
})
return nil
},
)
goqueue.Subscribe(
models.MessageTypeRefreshGitHubRepositoryDone,
func(ctx context.Context, item *queue.QueueMessage) error {
var doneRequest models.RefreshGitHubRepositoryDoneRequest
if err := json.Unmarshal([]byte(item.Content), &doneRequest); err != nil {
log.Printf("failed to unmarshal request body: %s", err.Error())
return err
}
return giteaClient.EditComment(ctx, &doneRequest)
},
)
}

View File

@ -0,0 +1,76 @@
package features
import (
"context"
"log"
"strings"
"git.front.kjuulh.io/kjuulh/contractor/internal/bot"
"git.front.kjuulh.io/kjuulh/contractor/internal/models"
"git.front.kjuulh.io/kjuulh/contractor/internal/queue"
)
type GitHubWebhook struct {
botHandler *bot.BotHandler
queue *queue.GoQueue
}
type GitHubWebhookRequest struct {
Action string `json:"action"`
Issue struct {
Id int `json:"id"`
Number int `json:"number"`
} `json:"issue"`
Comment struct {
Body string `json:"body"`
} `json:"comment"`
Repository struct {
FullName string `json:"full_name"`
}
}
func NewGitHubWebhook(botHandler *bot.BotHandler, queue *queue.GoQueue) *GitHubWebhook {
return &GitHubWebhook{
botHandler: botHandler,
queue: queue,
}
}
func (gw *GitHubWebhook) HandleGitHubWebhook(ctx context.Context, request *GitHubWebhookRequest) error {
command, ok := validateBotComment(request.Comment.Body)
if ok {
log.Printf("got webhook request: contractor %s", command)
bot := gw.botHandler
output, err := bot.Handle(command)
if err != nil {
log.Printf("failed to run bot handler with error: %s", err.Error())
}
parts := strings.Split(request.Repository.FullName, "/")
comment, err := bot.AppendComment(
parts[0],
parts[1],
request.Issue.Number,
output,
models.SupportedBackendGitHub,
)
if err != nil {
return err
}
if err := gw.queue.Insert(models.MessageTypeRefreshGitHubRepository, models.RefreshGitHubRepositoryRequest{
Repository: parts[1],
Owner: parts[0],
PullRequestID: request.Issue.Number,
CommentID: comment.ID,
CommentBody: comment.Body,
ReportProgress: true,
}); err != nil {
return err
}
}
return nil
}

View File

@ -1,8 +1,10 @@
package models
const (
MessageTypeRefreshRepository = "refresh_repository"
MessageTypeRefreshRepositoryDone = "refresh_repository_done"
MessageTypeRefreshGiteaRepository = "refresh_gitea_repository"
MessageTypeRefreshGiteaRepositoryDone = "refresh_gitea_repository_done"
MessageTypeRefreshGitHubRepository = "refresh_github_repository"
MessageTypeRefreshGitHubRepositoryDone = "refresh_github_repository_done"
)
type CreateHook struct {
@ -14,7 +16,7 @@ type CreateHook struct {
Type string `json:"type"`
}
type RefreshRepositoryRequest struct {
type RefreshGiteaRepositoryRequest struct {
Repository string `json:"repository"`
Owner string `json:"owner"`
PullRequestID int `json:"pullRequestId"`
@ -23,7 +25,27 @@ type RefreshRepositoryRequest struct {
ReportProgress bool `json:"reportProgress"`
}
type RefreshDoneRepositoryRequest struct {
type RefreshGiteaRepositoryDoneRequest struct {
Repository string `json:"repository"`
Owner string `json:"owner"`
PullRequestID int `json:"pullRequestId"`
CommentID int `json:"commentId"`
CommentBody string `json:"commentBody"`
ReportProgress bool `json:"reportProgress"`
Status string `json:"status"`
Error string `json:"error"`
}
type RefreshGitHubRepositoryRequest struct {
Repository string `json:"repository"`
Owner string `json:"owner"`
PullRequestID int `json:"pullRequestId"`
CommentID int `json:"commentId"`
CommentBody string `json:"commentBody"`
ReportProgress bool `json:"reportProgress"`
}
type RefreshGitHubRepositoryDoneRequest struct {
Repository string `json:"repository"`
Owner string `json:"owner"`
PullRequestID int `json:"pullRequestId"`
@ -38,3 +60,10 @@ type AddCommentResponse struct {
Body string `json:"body"`
ID int `json:"id"`
}
type SupportedBackend string
const (
SupportedBackendGitHub SupportedBackend = "github"
SupportedBackendGitea SupportedBackend = "gitea"
)

View File

@ -31,7 +31,7 @@ func NewGiteaClient(url, token *string) *GiteaClient {
func (gc *GiteaClient) EditComment(
ctx context.Context,
doneRequest *models.RefreshDoneRepositoryRequest,
doneRequest *models.RefreshGiteaRepositoryDoneRequest,
) error {
commentBody := html.UnescapeString(doneRequest.CommentBody)
startCmnt := "<!-- Status update start -->"

View File

@ -0,0 +1,117 @@
package providers
import (
"context"
"fmt"
"html"
"log"
"net/http"
"strings"
"git.front.kjuulh.io/kjuulh/contractor/internal/models"
"github.com/bradleyfalzon/ghinstallation/v2"
"github.com/google/go-github/v53/github"
)
type GitHubClient struct {
appID *int64
installationID *int64
privateKeyPath *string
client *github.Client
}
func NewGitHubClient(appID, installationID *int64, privateKeyPath *string) *GitHubClient {
return &GitHubClient{
appID: appID,
installationID: installationID,
privateKeyPath: privateKeyPath,
}
}
func (gc *GitHubClient) makeSureClientExist() {
if gc.client != nil {
return
}
tr := http.DefaultTransport
itr, err := ghinstallation.NewKeyFromFile(tr, *gc.appID, *gc.installationID, *gc.privateKeyPath)
if err != nil {
log.Fatal(err)
}
client := github.NewClient(&http.Client{Transport: itr})
gc.client = client
}
func (gc *GitHubClient) EditComment(
ctx context.Context,
doneRequest *models.RefreshGitHubRepositoryDoneRequest,
) error {
gc.makeSureClientExist()
commentBody := html.UnescapeString(doneRequest.CommentBody)
startCmnt := "<!-- Status update start -->"
startIdx := strings.Index(commentBody, startCmnt)
endIdx := strings.Index(commentBody, "<!-- Status update end -->")
if startIdx >= 0 && endIdx >= 0 {
log.Println("found comment to replace")
var content string
if doneRequest.Error != "" {
content = fmt.Sprintf("<pre>ERROR: %s</pre><br>", doneRequest.Error)
}
if doneRequest.Status != "" {
content = fmt.Sprintf("%s<p>%s</p>", content, doneRequest.Status)
}
doneRequest.CommentBody = fmt.Sprintf(
"%s<br><hr>%s<hr><br>%s",
commentBody[:startIdx+len(startCmnt)],
content,
commentBody[endIdx:],
)
}
_, _, err := gc.client.Issues.EditComment(ctx, doneRequest.Owner, doneRequest.Repository, int64(doneRequest.CommentID), &github.IssueComment{
Body: &doneRequest.CommentBody,
})
if err != nil {
log.Printf("failed to update comment: %s", err.Error())
return err
}
return nil
}
func (gc *GitHubClient) CreateWebhook(owner, repository string) error {
gc.makeSureClientExist()
// TODO: support for personal access tokens
// We implicitly get support via. github apps
return nil
}
func (gc *GitHubClient) AddComment(
owner, repository string,
pullRequest int,
comment string,
) (*models.AddCommentResponse, error) {
gc.makeSureClientExist()
resp, _, err := gc.client.Issues.CreateComment(context.Background(), owner, repository, pullRequest, &github.IssueComment{
Body: &comment,
})
if err != nil {
return nil, err
}
return &models.AddCommentResponse{
Body: *resp.Body,
ID: int(*resp.ID),
}, nil
}