Skip to content

Commit

Permalink
feat: support installation via Github app
Browse files Browse the repository at this point in the history
  • Loading branch information
vrivellino committed Jan 29, 2024
1 parent c9c58f9 commit efa7d14
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 45 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ go.work
git-rev.txt
/argo-diff
/.env.sh
*.pem
14 changes: 12 additions & 2 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/rs/zerolog/log"

"github.com/vince-riv/argo-diff/internal/argocd"
"github.com/vince-riv/argo-diff/internal/github"
"github.com/vince-riv/argo-diff/internal/server"
)

Expand Down Expand Up @@ -75,11 +76,20 @@ func main() {
log.Fatal().Msg("ARGOCD_SERVER_ADDR environment variable not set")
}
if os.Getenv("GITHUB_PERSONAL_ACCESS_TOKEN") == "" {
log.Fatal().Msg("GITHUB_PERSONAL_ACCESS_TOKEN environment variable not set")
log.Info().Msg("GITHUB_PERSONAL_ACCESS_TOKEN environment variable not set - assuming Github App installation")
for _, e := range []string{"GITHUB_APP_ID", "GITHUB_INSTALLATION_ID", "GITHUB_PRIVATE_KEY_FILE"} {
if os.Getenv(e) == "" {
log.Fatal().Msgf("%s environment variable is not set for Github App installations", e)
}
}
}

if err := argocd.ConnectivityCheck(); err != nil {
log.Fatal().Err(err).Msg("Connectivity check failed")
log.Fatal().Err(err).Msg("Connectivity check to ArgoCD failed")
}

if err := github.ConnectivityCheck(); err != nil {
log.Fatal().Err(err).Msg("Connectivity check to Github API failed")
}

server.StartWebhookProcessor("", 8080, githubWebhookSecret, devMode)
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.21.3
require (
github.com/argoproj/argo-cd/v2 v2.9.5
github.com/argoproj/gitops-engine v0.7.1-0.20230906152414-b0fffe419a0f
github.com/bradleyfalzon/ghinstallation/v2 v2.9.0
github.com/google/go-github/v58 v58.0.0
github.com/hexops/gotextdiff v1.0.3
github.com/rs/zerolog v1.31.0
Expand All @@ -25,7 +26,6 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/bmatcuk/doublestar/v4 v4.6.0 // indirect
github.com/bombsimon/logrusr/v2 v2.0.1 // indirect
github.com/bradleyfalzon/ghinstallation/v2 v2.6.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
Expand Down Expand Up @@ -60,7 +60,7 @@ require (
github.com/google/btree v1.1.2 // indirect
github.com/google/gnostic v0.6.9 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-github/v53 v53.2.0 // indirect
github.com/google/go-github/v57 v57.0.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
Expand Down
14 changes: 4 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/compute v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg=
cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
Expand Down Expand Up @@ -63,7 +62,6 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg=
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
Expand Down Expand Up @@ -92,14 +90,13 @@ github.com/bmatcuk/doublestar/v4 v4.6.0 h1:HTuxyug8GyFbRkrffIpzNCSK4luc0TY3wzXvz
github.com/bmatcuk/doublestar/v4 v4.6.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bombsimon/logrusr/v2 v2.0.1 h1:1VgxVNQMCvjirZIYaT9JYn6sAVGVEcNtRE0y4mvaOAM=
github.com/bombsimon/logrusr/v2 v2.0.1/go.mod h1:ByVAX+vHdLGAfdroiMg6q0zgq2FODY2lc5YJvzmOJio=
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/bradleyfalzon/ghinstallation/v2 v2.9.0 h1:HmxIYqnxubRYcYGRc5v3wUekmo5Wv2uX3gukmWJ0AFk=
github.com/bradleyfalzon/ghinstallation/v2 v2.9.0/go.mod h1:wmkTDJf8CmVypxE8ijIStFnKoTa6solK5QfdmJrP9KI=
github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao=
github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
Expand All @@ -112,7 +109,6 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
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/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
Expand Down Expand Up @@ -288,8 +284,8 @@ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/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-github/v57 v57.0.0 h1:L+Y3UPTY8ALM8x+TV0lg+IEBI+upibemtBD8Q9u7zHs=
github.com/google/go-github/v57 v57.0.0/go.mod h1:s0omdnye0hvK/ecLvpsGfJMiRt85PimQh4oygmLIxHw=
github.com/google/go-github/v58 v58.0.0 h1:Una7GGERlF/37XfkPwpzYJe0Vp4dt2k1kCjlxwjIvzw=
github.com/google/go-github/v58 v58.0.0/go.mod h1:k4hxDKEfoWpSqFlc8LTpGd9fu2KrV1YAa6Hi6FmDNY4=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
Expand Down Expand Up @@ -703,7 +699,6 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU=
golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down Expand Up @@ -771,7 +766,6 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210608053332-aa57babbf139/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/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-20211019181941-9d821ace8654/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-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
109 changes: 84 additions & 25 deletions internal/github/comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,119 @@ package github

import (
"context"
"errors"
"fmt"
"net/http"
"os"
"strconv"
"strings"
"sync"
"time"

ghinstallation "github.com/bradleyfalzon/ghinstallation/v2"
"github.com/google/go-github/v58/github"
"github.com/rs/zerolog/log"
)

var (
commentClient *github.Client
commentUser *github.User
mux *sync.RWMutex
commentClient *github.Client
appsClient *github.Client
commentClientIsApp bool
commentLogin string
mux *sync.RWMutex
)

const commentIdentifier = "<!-- comment produced by argo-diff -->"

func init() {
githubPAT := os.Getenv("GITHUB_PERSONAL_ACCESS_TOKEN")
if githubPAT == "" {
log.Error().Msg("Cannot create github client - GITHUB_PERSONAL_ACCESS_TOKEN is empty")
} else {
commentClientIsApp = false
mux = &sync.RWMutex{}
// Create Github API client
if githubPAT := os.Getenv("GITHUB_PERSONAL_ACCESS_TOKEN"); githubPAT != "" {
commentClient = github.NewClient(nil).WithAuthToken(githubPAT)
} else {
tr := http.DefaultTransport
appId, err := strconv.ParseInt(os.Getenv("GITHUB_APP_ID"), 10, 64)
if err != nil {
log.Error().Err(err).Msgf("Unable to parse %s", os.Getenv("GITHUB_APP_ID"))
return
}
installId, err := strconv.ParseInt(os.Getenv("GITHUB_INSTALLATION_ID"), 10, 64)
if err != nil {
log.Error().Err(err).Msgf("Unable to parse %s", os.Getenv("GITHUB_INSTALLATION_ID"))
return
}
privKeyFile := os.Getenv("GITHUB_PRIVATE_KEY_FILE")
atr, err := ghinstallation.NewAppsTransportKeyFromFile(tr, appId, privKeyFile)
if err != nil {
log.Error().Err(err).Msgf("Failed to create jwt transport: appId %d, privKeyFile %s", appId, privKeyFile)
return
}
itr := ghinstallation.NewFromAppsTransport(atr, installId)
commentClient = github.NewClient(&http.Client{Transport: itr})
appsClient = github.NewClient(&http.Client{Transport: atr}) // /app endpoints need separate client
commentClientIsApp = true
}
mux = &sync.RWMutex{}
}

// Populates commentUser singleton with the Github user associated with our github client
func ConnectivityCheck() error {
if commentClient == nil {
return errors.New("github client is not initialized")
}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
log.Info().Msg("Calling Github API for a connectivity test")
return getCommentUser(ctx)
}

// Populates commentLogin singleton with the Github user associated with our github client
func getCommentUser(ctx context.Context) error {
if commentClient == nil {
log.Error().Msg("Cannot call github API - I don't have a client set")
return fmt.Errorf("no github commenter client")
}
log.Debug().Msg("Calling Github API to determine comment user")
mux.RLock()
if commentUser != nil {
if commentLogin != "" {
mux.RUnlock()
return nil
}
mux.RUnlock()
user, resp, err := commentClient.Users.Get(ctx, "")
if resp != nil {
log.Info().Msgf("%s received when calling client.Users.Get() via go-github", resp.Status)
}
if err != nil {
log.Error().Err(err).Msg("Unable to determine get my github user")
return err
}
if user == nil {
log.Error().Msg("Empty user returned - not sure how I got here")
return nil
if commentClientIsApp {
app, resp, err := appsClient.Apps.Get(ctx, "")
if resp != nil {
log.Info().Msgf("%s received when calling client.Apps.Get() via go-github", resp.Status)
}
if err != nil {
log.Error().Err(err).Msg("Unable to determine get my github app")
return err
}
log.Trace().Msgf("Github App: %+v", app)
if app == nil || app.Name == nil {
log.Error().Msg("Empty user returned - not sure how I got here")
return fmt.Errorf("empty app info")
}
mux.Lock()
commentLogin = *app.Name + "[bot]"
mux.Unlock()
} else {
user, resp, err := commentClient.Users.Get(ctx, "")
if resp != nil {
log.Info().Msgf("%s received when calling client.Users.Get() via go-github", resp.Status)
}
if err != nil {
log.Error().Err(err).Msg("Unable to determine get my github user")
return err
}
if user == nil || user.Login == nil {
log.Error().Msg("Empty user returned - not sure how I got here")
return fmt.Errorf("empty user info")
}
mux.Lock()
commentLogin = *user.Login
mux.Unlock()
}
mux.Lock()
commentUser = user
mux.Unlock()
log.Info().Msgf("Github Comment user name: %s", commentLogin)
return nil
}

Expand Down Expand Up @@ -139,7 +198,7 @@ func getExistingComments(ctx context.Context, owner, repo string, prNum int) ([]
}
log.Debug().Msgf("Checking %d comments in %s/%s#%d", len(comments), owner, repo, prNum)
for _, c := range comments {
if *c.User.Login == *commentUser.Login && strings.Contains(*c.Body, commentIdentifier) {
if *c.User.Login == commentLogin && strings.Contains(*c.Body, commentIdentifier) {
res = append(res, c)
}
}
Expand Down
32 changes: 26 additions & 6 deletions internal/github/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package github
import (
"context"
"fmt"
"net/http"
"os"
"strconv"

ghinstallation "github.com/bradleyfalzon/ghinstallation/v2"
"github.com/google/go-github/v58/github"
"github.com/rs/zerolog/log"
)
Expand All @@ -22,16 +25,33 @@ const StatusFailure = "failure"
const StatusError = "error"

func init() {
githubPAT := os.Getenv("GITHUB_PERSONAL_ACCESS_TOKEN")
if githubPAT == "" {
log.Error().Msg("Cannot create github client - GITHUB_PERSONAL_ACCESS_TOKEN is empty")
} else {
statusClient = github.NewClient(nil).WithAuthToken(githubPAT)
}
statusContextEnv := os.Getenv("GITHUB_STATUS_CONTEXT_STR")
if statusContextEnv != "" {
statusContextStr = statusContextEnv
}
// Create Github API client
if githubPAT := os.Getenv("GITHUB_PERSONAL_ACCESS_TOKEN"); githubPAT != "" {
statusClient = github.NewClient(nil).WithAuthToken(githubPAT)
return
}
tr := http.DefaultTransport
appId, err := strconv.ParseInt(os.Getenv("GITHUB_APP_ID"), 10, 64)
if err != nil {
log.Error().Err(err).Msgf("Unable to parse %s", os.Getenv("GITHUB_APP_ID"))
return
}
installId, err := strconv.ParseInt(os.Getenv("GITHUB_INSTALLATION_ID"), 10, 64)
if err != nil {
log.Error().Err(err).Msgf("Unable to parse %s", os.Getenv("GITHUB_INSTALLATION_ID"))
return
}
privKeyFile := os.Getenv("GITHUB_PRIVATE_KEY_FILE")
itr, err := ghinstallation.NewKeyFromFile(tr, appId, installId, privKeyFile)
if err != nil {
log.Error().Err(err).Msgf("Failed to create github client: appId %d, installId %d, privKeyFile %s", appId, installId, privKeyFile)
return
}
statusClient = github.NewClient(&http.Client{Transport: itr})
}

// Helper that sets commit status for the request commit sha
Expand Down

0 comments on commit efa7d14

Please sign in to comment.