Skip to content

Commit 83dc11f

Browse files
authored
Merge pull request #1582 from diggerhq/feat/github-private-vcs-support-ee
GitHub EE private vcs support
2 parents 765cf7c + 97a1a56 commit 83dc11f

File tree

11 files changed

+175
-38
lines changed

11 files changed

+175
-38
lines changed

backend/bootstrap/main.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
// based on https://www.digitalocean.com/community/tutorials/using-ldflags-to-set-version-information-for-go-applications
2525
var Version = "dev"
2626

27-
func Bootstrap(templates embed.FS, githubController controllers.GithubController) *gin.Engine {
27+
func Bootstrap(templates embed.FS, diggerController controllers.DiggerController) *gin.Engine {
2828
defer segment.CloseClient()
2929
initLogging()
3030
cfg := config.DiggerConfig
@@ -79,19 +79,19 @@ func Bootstrap(templates embed.FS, githubController controllers.GithubController
7979
r.LoadHTMLGlob("templates/*.tmpl")
8080
}
8181

82-
r.POST("/github-app-webhook", githubController.GithubAppWebHook)
83-
r.POST("/github-app-webhook/aam", controllers.GithubAppWebHookAfterMerge)
82+
r.POST("/github-app-webhook", diggerController.GithubAppWebHook)
83+
r.POST("/github-app-webhook/aam", diggerController.GithubAppWebHookAfterMerge)
8484

8585
tenantActionsGroup := r.Group("/api/tenants")
8686
tenantActionsGroup.Use(middleware.CORSMiddleware())
8787
tenantActionsGroup.Any("/associateTenantIdToDiggerOrg", controllers.AssociateTenantIdToDiggerOrg)
8888

8989
githubGroup := r.Group("/github")
9090
githubGroup.Use(middleware.GetWebMiddleware())
91-
githubGroup.GET("/callback", controllers.GithubAppCallbackPage)
92-
githubGroup.GET("/repos", controllers.GithubReposPage)
91+
githubGroup.GET("/callback", diggerController.GithubAppCallbackPage)
92+
githubGroup.GET("/repos", diggerController.GithubReposPage)
9393
githubGroup.GET("/setup", controllers.GithubAppSetup)
94-
githubGroup.GET("/exchange-code", controllers.GithubSetupExchangeCode)
94+
githubGroup.GET("/exchange-code", diggerController.GithubSetupExchangeCode)
9595

9696
authorized := r.Group("/")
9797
authorized.Use(middleware.GetApiMiddleware(), middleware.AccessLevel(models.CliJobAccessType, models.AccessPolicyType, models.AdminPolicyType))
@@ -114,7 +114,7 @@ func Bootstrap(templates embed.FS, githubController controllers.GithubController
114114
authorized.GET("/repos/:repo/projects/:projectName/runs", controllers.RunHistoryForProject)
115115
authorized.POST("/repos/:repo/projects/:projectName/runs", controllers.CreateRunForProject)
116116

117-
authorized.POST("/repos/:repo/projects/:projectName/jobs/:jobId/set-status", controllers.SetJobStatusForProject)
117+
authorized.POST("/repos/:repo/projects/:projectName/jobs/:jobId/set-status", diggerController.SetJobStatusForProject)
118118

119119
authorized.GET("/repos/:repo/projects", controllers.FindProjectsForRepo)
120120
authorized.POST("/repos/:repo/report-projects", controllers.ReportProjectsForRepo)

backend/ci_backends/ci_backends.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package ci_backends
22

33
import (
44
"github.com/diggerhq/digger/backend/models"
5+
"github.com/diggerhq/digger/backend/utils"
56
)
67

78
type CiBackend interface {
@@ -11,6 +12,7 @@ type CiBackend interface {
1112
type JenkinsCi struct{}
1213

1314
type CiBackendOptions struct {
15+
GithubClientProvider utils.GithubClientProvider
1416
GithubInstallationId int64
1517
RepoFullName string
1618
RepoOwner string

backend/ci_backends/provider.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ type CiBackendProvider interface {
1313
type DefaultBackendProvider struct{}
1414

1515
func (d DefaultBackendProvider) GetCiBackend(options CiBackendOptions) (CiBackend, error) {
16-
client, _, err := utils.GetGithubClient(&utils.DiggerGithubRealClientProvider{}, options.GithubInstallationId, options.RepoFullName)
16+
client, _, err := utils.GetGithubClient(options.GithubClientProvider, options.GithubInstallationId, options.RepoFullName)
1717
if err != nil {
1818
log.Printf("GetCiBackend: could not get github client: %v", err)
1919
return nil, fmt.Errorf("could not get github client: %v", err)

backend/controllers/github.go

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,14 @@ import (
3636
"golang.org/x/oauth2"
3737
)
3838

39-
type GithubController struct {
40-
CiBackendProvider ci_backends.CiBackendProvider
39+
type DiggerController struct {
40+
CiBackendProvider ci_backends.CiBackendProvider
41+
GithubClientProvider utils.GithubClientProvider
4142
}
4243

43-
func (g GithubController) GithubAppWebHook(c *gin.Context) {
44+
func (d DiggerController) GithubAppWebHook(c *gin.Context) {
4445
c.Header("Content-Type", "application/json")
45-
gh := &utils.DiggerGithubRealClientProvider{}
46+
gh := d.GithubClientProvider
4647
log.Printf("GithubAppWebHook")
4748

4849
payload, err := github.ValidatePayload(c.Request, []byte(os.Getenv("GITHUB_WEBHOOK_SECRET")))
@@ -100,15 +101,15 @@ func (g GithubController) GithubAppWebHook(c *gin.Context) {
100101
c.String(http.StatusOK, "OK")
101102
return
102103
}
103-
err := handleIssueCommentEvent(gh, event, g.CiBackendProvider)
104+
err := handleIssueCommentEvent(gh, event, d.CiBackendProvider)
104105
if err != nil {
105106
log.Printf("handleIssueCommentEvent error: %v", err)
106107
c.String(http.StatusInternalServerError, err.Error())
107108
return
108109
}
109110
case *github.PullRequestEvent:
110111
log.Printf("Got pull request event for %d", *event.PullRequest.ID)
111-
err := handlePullRequestEvent(gh, event, g.CiBackendProvider)
112+
err := handlePullRequestEvent(gh, event, d.CiBackendProvider)
112113
if err != nil {
113114
log.Printf("handlePullRequestEvent error: %v", err)
114115
c.String(http.StatusInternalServerError, err.Error())
@@ -191,9 +192,10 @@ func GithubAppSetup(c *gin.Context) {
191192
},
192193
}
193194

195+
githubHostname := getGithubHostname()
194196
url := &url.URL{
195197
Scheme: "https",
196-
Host: "github.com",
198+
Host: githubHostname,
197199
Path: "/settings/apps/new",
198200
}
199201

@@ -212,16 +214,36 @@ func GithubAppSetup(c *gin.Context) {
212214
c.HTML(http.StatusOK, "github_setup.tmpl", gin.H{"Target": url.String(), "Manifest": string(jsonManifest)})
213215
}
214216

217+
func getGithubHostname() string {
218+
githubHostname := os.Getenv("DIGGER_GITHUB_HOSTNAME")
219+
if githubHostname == "" {
220+
githubHostname = "github.com"
221+
}
222+
return githubHostname
223+
}
224+
215225
// GithubSetupExchangeCode handles the user coming back from creating their app
216226
// A code query parameter is exchanged for this app's ID, key, and webhook_secret
217227
// Implements https://developer.github.com/apps/building-github-apps/creating-github-apps-from-a-manifest/#implementing-the-github-app-manifest-flow
218-
func GithubSetupExchangeCode(c *gin.Context) {
228+
func (d DiggerController) GithubSetupExchangeCode(c *gin.Context) {
219229
code := c.Query("code")
220230
if code == "" {
221231
c.Error(fmt.Errorf("Ignoring callback, missing code query parameter"))
222232
}
223233

224-
client := github.NewClient(nil)
234+
// TODO: to make tls verification configurable for debug purposes
235+
//var transport *http.Transport = nil
236+
//_, exists := os.LookupEnv("DIGGER_GITHUB_SKIP_TLS")
237+
//if exists {
238+
// transport = &http.Transport{
239+
// TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
240+
// }
241+
//}
242+
243+
client, err := d.GithubClientProvider.NewClient(nil)
244+
if err != nil {
245+
c.Error(fmt.Errorf("could not create github client: %v", err))
246+
}
225247
cfg, _, err := client.Apps.CompleteAppManifest(context.Background(), code)
226248
if err != nil {
227249
c.Error(fmt.Errorf("Failed to exchange code for github app: %s", err))
@@ -584,6 +606,7 @@ func handlePullRequestEvent(gh utils.GithubClientProvider, payload *github.PullR
584606

585607
ciBackend, err := ciBackendProvider.GetCiBackend(
586608
ci_backends.CiBackendOptions{
609+
GithubClientProvider: gh,
587610
GithubInstallationId: installationId,
588611
RepoName: repoName,
589612
RepoOwner: repoOwner,
@@ -870,6 +893,7 @@ func handleIssueCommentEvent(gh utils.GithubClientProvider, payload *github.Issu
870893

871894
ciBackend, err := ciBackendProvider.GetCiBackend(
872895
ci_backends.CiBackendOptions{
896+
GithubClientProvider: gh,
873897
GithubInstallationId: installationId,
874898
RepoName: repoName,
875899
RepoOwner: repoOwner,
@@ -1057,7 +1081,7 @@ jobs:
10571081
return nil
10581082
}
10591083

1060-
func GithubAppCallbackPage(c *gin.Context) {
1084+
func (d DiggerController) GithubAppCallbackPage(c *gin.Context) {
10611085
installationId := c.Request.URL.Query()["installation_id"][0]
10621086
//setupAction := c.Request.URL.Query()["setup_action"][0]
10631087
code := c.Request.URL.Query()["code"][0]
@@ -1077,7 +1101,7 @@ func GithubAppCallbackPage(c *gin.Context) {
10771101
return
10781102
}
10791103

1080-
result, err := validateGithubCallback(clientId, clientSecret, code, installationId64)
1104+
result, err := validateGithubCallback(d.GithubClientProvider, clientId, clientSecret, code, installationId64)
10811105
if !result {
10821106
log.Printf("Failed to validated installation id, %v\n", err)
10831107
c.String(http.StatusInternalServerError, "Failed to validate installation_id.")
@@ -1100,7 +1124,7 @@ func GithubAppCallbackPage(c *gin.Context) {
11001124
c.HTML(http.StatusOK, "github_success.tmpl", gin.H{})
11011125
}
11021126

1103-
func GithubReposPage(c *gin.Context) {
1127+
func (d DiggerController) GithubReposPage(c *gin.Context) {
11041128
orgId, exists := c.Get(middleware.ORGANISATION_ID_KEY)
11051129
if !exists {
11061130
log.Printf("Organisation ID not found in context")
@@ -1127,7 +1151,7 @@ func GithubReposPage(c *gin.Context) {
11271151
return
11281152
}
11291153

1130-
gh := &utils.DiggerGithubRealClientProvider{}
1154+
gh := d.GithubClientProvider
11311155
client, _, err := gh.Get(installations[0].GithubAppId, installations[0].GithubInstallationId)
11321156
if err != nil {
11331157
log.Printf("failed to create github client, %v", err)
@@ -1147,14 +1171,15 @@ func GithubReposPage(c *gin.Context) {
11471171

11481172
// why this validation is needed: https://roadie.io/blog/avoid-leaking-github-org-data/
11491173
// validation based on https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app , step 3
1150-
func validateGithubCallback(clientId string, clientSecret string, code string, installationId int64) (bool, error) {
1174+
func validateGithubCallback(githubClientProvider utils.GithubClientProvider, clientId string, clientSecret string, code string, installationId int64) (bool, error) {
11511175
ctx := context.Background()
11521176
type OAuthAccessResponse struct {
11531177
AccessToken string `json:"access_token"`
11541178
}
11551179
httpClient := http.Client{}
11561180

1157-
reqURL := fmt.Sprintf("https://github.com/login/oauth/access_token?client_id=%s&client_secret=%s&code=%s", clientId, clientSecret, code)
1181+
githubHostname := getGithubHostname()
1182+
reqURL := fmt.Sprintf("https://%v/login/oauth/access_token?client_id=%s&client_secret=%s&code=%s", githubHostname, clientId, clientSecret, code)
11581183
req, err := http.NewRequest(http.MethodPost, reqURL, nil)
11591184
if err != nil {
11601185
return false, fmt.Errorf("could not create HTTP request: %v\n", err)
@@ -1179,11 +1204,27 @@ func validateGithubCallback(clientId string, clientSecret string, code string, i
11791204
&oauth2.Token{AccessToken: t.AccessToken},
11801205
)
11811206
tc := oauth2.NewClient(ctx, ts)
1182-
client := github.NewClient(tc)
1207+
//tc := &http.Client{
1208+
// Transport: &oauth2.Transport{
1209+
// Base: httpClient.Transport,
1210+
// Source: oauth2.ReuseTokenSource(nil, ts),
1211+
// },
1212+
//}
1213+
1214+
client, err := githubClientProvider.NewClient(tc)
1215+
if err != nil {
1216+
log.Printf("could create github client: %v", err)
1217+
return false, fmt.Errorf("could not create github client: %v", err)
1218+
}
11831219

11841220
installationIdMatch := false
11851221
// list all installations for the user
11861222
installations, _, err := client.Apps.ListUserInstallations(ctx, nil)
1223+
if err != nil {
1224+
log.Printf("could not retrieve installations: %v", err)
1225+
return false, fmt.Errorf("could not retrieve installations: %v", installationId)
1226+
}
1227+
log.Printf("installations %v", installations)
11871228
for _, v := range installations {
11881229
log.Printf("installation id: %v\n", *v.ID)
11891230
if *v.ID == installationId {

backend/controllers/github_after_merge.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ import (
1818
"strings"
1919
)
2020

21-
func GithubAppWebHookAfterMerge(c *gin.Context) {
21+
func (d DiggerController) GithubAppWebHookAfterMerge(c *gin.Context) {
2222
c.Header("Content-Type", "application/json")
23-
gh := &utils.DiggerGithubRealClientProvider{}
23+
gh := d.GithubClientProvider
2424
log.Printf("GithubAppWebHook")
2525

2626
payload, err := github.ValidatePayload(c.Request, []byte(os.Getenv("GITHUB_WEBHOOK_SECRET")))

backend/controllers/projects.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ type SetJobStatusRequest struct {
321321
TerraformOutput string `json:"terraform_output""`
322322
}
323323

324-
func SetJobStatusForProject(c *gin.Context) {
324+
func (d DiggerController) SetJobStatusForProject(c *gin.Context) {
325325
jobId := c.Param("jobId")
326326

327327
orgId, exists := c.Get(middleware.ORGANISATION_ID_KEY)
@@ -359,7 +359,7 @@ func SetJobStatusForProject(c *gin.Context) {
359359
return
360360
}
361361

362-
client, _, err := utils.GetGithubClient(&utils.DiggerGithubRealClientProvider{}, job.Batch.GithubInstallationId, job.Batch.RepoFullName)
362+
client, _, err := utils.GetGithubClient(d.GithubClientProvider, job.Batch.GithubInstallationId, job.Batch.RepoFullName)
363363
if err != nil {
364364
log.Printf("Error Creating github client: %v", err)
365365
} else {
@@ -397,7 +397,7 @@ func SetJobStatusForProject(c *gin.Context) {
397397
log.Printf("Recovered from panic while executing goroutine dispatching digger jobs: %v ", r)
398398
}
399399
}()
400-
ghClientProvider := &utils.DiggerGithubRealClientProvider{}
400+
ghClientProvider := d.GithubClientProvider
401401
installationLink, err := models.DB.GetGithubInstallationLinkForOrg(orgId)
402402
if err != nil {
403403
log.Printf("Error fetching installation link: %v", err)
@@ -477,7 +477,7 @@ func SetJobStatusForProject(c *gin.Context) {
477477
return
478478
}
479479

480-
err = AutomergePRforBatchIfEnabled(&utils.DiggerGithubRealClientProvider{}, batch)
480+
err = AutomergePRforBatchIfEnabled(d.GithubClientProvider, batch)
481481
if err != nil {
482482
log.Printf("Error merging PR with automerge option: %v", err)
483483
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error merging PR with automerge option"})
@@ -491,7 +491,7 @@ func SetJobStatusForProject(c *gin.Context) {
491491

492492
}
493493

494-
UpdateCommentsForBatchGroup(&utils.DiggerGithubRealClientProvider{}, batch, res.Jobs)
494+
UpdateCommentsForBatchGroup(d.GithubClientProvider, batch, res.Jobs)
495495

496496
c.JSON(http.StatusOK, res)
497497
}

backend/main.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@ import (
77
"github.com/diggerhq/digger/backend/ci_backends"
88
"github.com/diggerhq/digger/backend/config"
99
"github.com/diggerhq/digger/backend/controllers"
10+
"github.com/diggerhq/digger/backend/utils"
1011
)
1112

1213
//go:embed templates
1314
var templates embed.FS
1415

1516
func main() {
16-
ghController := controllers.GithubController{
17-
CiBackendProvider: ci_backends.DefaultBackendProvider{},
17+
ghController := controllers.DiggerController{
18+
CiBackendProvider: ci_backends.DefaultBackendProvider{},
19+
GithubClientProvider: utils.DiggerGithubRealClientProvider{},
1820
}
1921
r := bootstrap.Bootstrap(templates, ghController)
2022
r.GET("/", controllers.Home)

backend/utils/github.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,15 @@ type DiggerGithubClientMockProvider struct {
7171
}
7272

7373
type GithubClientProvider interface {
74+
NewClient(netClient *net.Client) (*github.Client, error)
7475
Get(githubAppId int64, installationId int64) (*github.Client, *string, error)
7576
}
7677

78+
func (gh DiggerGithubRealClientProvider) NewClient(netClient *net.Client) (*github.Client, error) {
79+
ghClient := github.NewClient(netClient)
80+
return ghClient, nil
81+
}
82+
7783
func (gh DiggerGithubRealClientProvider) Get(githubAppId int64, installationId int64) (*github.Client, *string, error) {
7884
githubAppPrivateKey := ""
7985
githubAppPrivateKeyB64 := os.Getenv("GITHUB_APP_PRIVATE_KEY_BASE64")
@@ -103,12 +109,20 @@ func (gh DiggerGithubRealClientProvider) Get(githubAppId int64, installationId i
103109
if err != nil {
104110
return nil, nil, fmt.Errorf("error initialising git app token: %v\n", err)
105111
}
106-
ghClient := github.NewClient(&net.Client{Transport: itr})
112+
ghClient, err := gh.NewClient(&net.Client{Transport: itr})
113+
if err != nil {
114+
log.Printf("error creating new client: %v", err)
115+
}
107116
return ghClient, &token, nil
108117
}
109118

110-
func (gh *DiggerGithubClientMockProvider) Get(githubAppId int64, installationId int64) (*github.Client, *string, error) {
119+
func (gh DiggerGithubClientMockProvider) NewClient(netClient *net.Client) (*github.Client, error) {
111120
ghClient := github.NewClient(gh.MockedHTTPClient)
121+
return ghClient, nil
122+
}
123+
124+
func (gh *DiggerGithubClientMockProvider) Get(githubAppId int64, installationId int64) (*github.Client, *string, error) {
125+
ghClient, _ := gh.NewClient(gh.MockedHTTPClient)
112126
token := "token"
113127
return ghClient, &token, nil
114128
}

ee/backend/ci_backends/provider.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func (b EEBackendProvider) GetCiBackend(options ci_backends.CiBackendOptions) (c
1515
ciBackendType := os.Getenv("CI_BACKEND")
1616
switch ciBackendType {
1717
case "github_actions", "":
18-
client, _, err := utils.GetGithubClient(&utils.DiggerGithubRealClientProvider{}, options.GithubInstallationId, options.RepoFullName)
18+
client, _, err := utils.GetGithubClient(options.GithubClientProvider, options.GithubInstallationId, options.RepoFullName)
1919
if err != nil {
2020
log.Printf("GetCiBackend: could not get github client: %v", err)
2121
return nil, fmt.Errorf("could not get github client: %v", err)

ee/backend/main.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/diggerhq/digger/backend/middleware"
1010
ci_backends2 "github.com/diggerhq/digger/ee/backend/ci_backends"
1111
"github.com/diggerhq/digger/ee/backend/controllers"
12+
"github.com/diggerhq/digger/ee/backend/providers/github"
1213
"github.com/diggerhq/digger/libs/license"
1314
"github.com/gin-gonic/gin"
1415
"log"
@@ -28,8 +29,9 @@ func main() {
2829
log.Printf("error checking license %v", err)
2930
os.Exit(1)
3031
}
31-
ghController := ce_controllers.GithubController{
32-
CiBackendProvider: ci_backends2.EEBackendProvider{},
32+
ghController := ce_controllers.DiggerController{
33+
CiBackendProvider: ci_backends2.EEBackendProvider{},
34+
GithubClientProvider: github.DiggerGithubEEClientProvider{},
3335
}
3436

3537
r := bootstrap.Bootstrap(templates, ghController)

0 commit comments

Comments
 (0)