@@ -6,12 +6,23 @@ import (
6
6
"encoding/json"
7
7
"errors"
8
8
"fmt"
9
+ "github.com/diggerhq/digger/backend/middleware"
9
10
"github.com/diggerhq/digger/backend/segment"
11
+ backend_utils "github.com/diggerhq/digger/backend/utils"
10
12
"github.com/diggerhq/digger/libs/ci"
13
+ dg_github "github.com/diggerhq/digger/libs/ci/github"
14
+ dg_configuration "github.com/diggerhq/digger/libs/digger_config"
11
15
orchestrator_scheduler "github.com/diggerhq/digger/libs/scheduler"
12
16
"github.com/diggerhq/digger/next/ci_backends"
17
+ "github.com/diggerhq/digger/next/dbmodels"
13
18
"github.com/diggerhq/digger/next/model"
14
19
"github.com/diggerhq/digger/next/services"
20
+ next_utils "github.com/diggerhq/digger/next/utils"
21
+ "github.com/dominikbraun/graph"
22
+ "github.com/gin-gonic/gin"
23
+ "github.com/google/go-github/v61/github"
24
+ "github.com/samber/lo"
25
+ "golang.org/x/oauth2"
15
26
"gorm.io/gorm"
16
27
"log"
17
28
"math/rand"
@@ -22,18 +33,6 @@ import (
22
33
"reflect"
23
34
"strconv"
24
35
"strings"
25
-
26
- "github.com/diggerhq/digger/backend/middleware"
27
- backend_utils "github.com/diggerhq/digger/backend/utils"
28
- dg_github "github.com/diggerhq/digger/libs/ci/github"
29
- dg_configuration "github.com/diggerhq/digger/libs/digger_config"
30
- "github.com/diggerhq/digger/next/dbmodels"
31
- next_utils "github.com/diggerhq/digger/next/utils"
32
- "github.com/dominikbraun/graph"
33
- "github.com/gin-gonic/gin"
34
- "github.com/google/go-github/v61/github"
35
- "github.com/samber/lo"
36
- "golang.org/x/oauth2"
37
36
)
38
37
39
38
type DiggerController struct {
@@ -65,14 +64,6 @@ func (d DiggerController) GithubAppWebHook(c *gin.Context) {
65
64
66
65
switch event := event .(type ) {
67
66
case * github.InstallationEvent :
68
- log .Printf ("InstallationEvent, action: %v\n " , * event .Action )
69
- if * event .Action == "created" {
70
- err := handleInstallationCreatedEvent (event )
71
- if err != nil {
72
- c .String (http .StatusInternalServerError , "Failed to handle webhook event." )
73
- return
74
- }
75
- }
76
67
77
68
if * event .Action == "deleted" {
78
69
err := handleInstallationDeletedEvent (event )
@@ -81,20 +72,7 @@ func (d DiggerController) GithubAppWebHook(c *gin.Context) {
81
72
return
82
73
}
83
74
}
84
- case * github.InstallationRepositoriesEvent :
85
- log .Printf ("InstallationRepositoriesEvent, action: %v\n " , * event .Action )
86
- if * event .Action == "added" {
87
- err := handleInstallationRepositoriesAddedEvent (gh , event )
88
- if err != nil {
89
- c .String (http .StatusInternalServerError , "Failed to handle installation repo added event." )
90
- }
91
- }
92
- if * event .Action == "removed" {
93
- err := handleInstallationRepositoriesDeletedEvent (event )
94
- if err != nil {
95
- c .String (http .StatusInternalServerError , "Failed to handle installation repo deleted event." )
96
- }
97
- }
75
+
98
76
case * github.IssueCommentEvent :
99
77
log .Printf ("IssueCommentEvent, action: %v\n " , * event .Action )
100
78
case * github.PullRequestEvent :
@@ -277,9 +255,11 @@ func createOrGetDiggerRepoForGithubRepo(ghRepoFullName string, ghRepoOrganisatio
277
255
278
256
diggerRepoName := strings .ReplaceAll (ghRepoFullName , "/" , "-" )
279
257
280
- repo , err := dbmodels .DB .GetRepo (orgId , diggerRepoName )
258
+ // using Unscoped because we also need to include deleted repos (and undelete them if they exist)
259
+ var existingRepo model.Repo
260
+ r := dbmodels .DB .GormDB .Unscoped ().Where ("organization_id=? AND repos.name=?" , orgId , diggerRepoName ).Find (& existingRepo )
281
261
282
- if err != nil {
262
+ if r . Error != nil {
283
263
if errors .Is (err , gorm .ErrRecordNotFound ) {
284
264
log .Printf ("repo not found, will proceed with repo creation" )
285
265
} else {
@@ -288,12 +268,14 @@ func createOrGetDiggerRepoForGithubRepo(ghRepoFullName string, ghRepoOrganisatio
288
268
}
289
269
}
290
270
291
- if repo != nil {
292
- log .Printf ("Digger repo already exists: %v" , repo )
293
- return repo , org , nil
271
+ if r .RowsAffected > 0 {
272
+ existingRepo .DeletedAt = gorm.DeletedAt {}
273
+ dbmodels .DB .GormDB .Save (& existingRepo )
274
+ log .Printf ("Digger repo already exists: %v" , existingRepo )
275
+ return & existingRepo , org , nil
294
276
}
295
277
296
- repo , err = dbmodels .DB .CreateRepo (diggerRepoName , ghRepoFullName , ghRepoOrganisation , ghRepoName , ghRepoUrl , org , `
278
+ repo , err : = dbmodels .DB .CreateRepo (diggerRepoName , ghRepoFullName , ghRepoOrganisation , ghRepoName , ghRepoUrl , org , `
297
279
generate_projects:
298
280
include: "."
299
281
` )
@@ -305,72 +287,6 @@ generate_projects:
305
287
return repo , org , nil
306
288
}
307
289
308
- func handleInstallationRepositoriesAddedEvent (ghClientProvider next_utils.GithubClientProvider , payload * github.InstallationRepositoriesEvent ) error {
309
- installationId := * payload .Installation .ID
310
- login := * payload .Installation .Account .Login
311
- accountId := * payload .Installation .Account .ID
312
- appId := * payload .Installation .AppID
313
-
314
- for _ , repo := range payload .RepositoriesAdded {
315
- repoFullName := * repo .FullName
316
- repoOwner := strings .Split (* repo .FullName , "/" )[0 ]
317
- repoName := * repo .Name
318
- repoUrl := fmt .Sprintf ("https://github.com/%v" , repoFullName )
319
- _ , err := dbmodels .DB .GithubRepoAdded (installationId , appId , login , accountId , repoFullName )
320
- if err != nil {
321
- log .Printf ("GithubRepoAdded failed, error: %v\n " , err )
322
- return err
323
- }
324
-
325
- _ , _ , err = createOrGetDiggerRepoForGithubRepo (repoFullName , repoOwner , repoName , repoUrl , installationId )
326
- if err != nil {
327
- log .Printf ("createOrGetDiggerRepoForGithubRepo failed, error: %v\n " , err )
328
- return err
329
- }
330
- }
331
- return nil
332
- }
333
-
334
- func handleInstallationRepositoriesDeletedEvent (payload * github.InstallationRepositoriesEvent ) error {
335
- installationId := * payload .Installation .ID
336
- appId := * payload .Installation .AppID
337
- for _ , repo := range payload .RepositoriesRemoved {
338
- repoFullName := * repo .FullName
339
- _ , err := dbmodels .DB .GithubRepoRemoved (installationId , appId , repoFullName )
340
- if err != nil {
341
- return err
342
- }
343
-
344
- // todo: change the status of DiggerRepo to InActive
345
- }
346
- return nil
347
- }
348
-
349
- func handleInstallationCreatedEvent (installation * github.InstallationEvent ) error {
350
- installationId := * installation .Installation .ID
351
- login := * installation .Installation .Account .Login
352
- accountId := * installation .Installation .Account .ID
353
- appId := * installation .Installation .AppID
354
-
355
- for _ , repo := range installation .Repositories {
356
- repoFullName := * repo .FullName
357
- repoOwner := strings .Split (* repo .FullName , "/" )[0 ]
358
- repoName := * repo .Name
359
- repoUrl := fmt .Sprintf ("https://github.com/%v" , repoFullName )
360
-
361
- log .Printf ("Adding a new installation %d for repo: %s" , installationId , repoFullName )
362
- _ , err := dbmodels .DB .GithubRepoAdded (installationId , appId , login , accountId , repoFullName )
363
- if err != nil {
364
- return err
365
- }
366
- _ , _ , err = createOrGetDiggerRepoForGithubRepo (repoFullName , repoOwner , repoName , repoUrl , installationId )
367
- if err != nil {
368
- return err
369
- }
370
- }
371
- return nil
372
- }
373
-
374
290
func handleInstallationDeletedEvent (installation * github.InstallationEvent ) error {
375
291
installationId := * installation .Installation .ID
376
292
appId := * installation .Installation .AppID
@@ -387,7 +303,7 @@ func handleInstallationDeletedEvent(installation *github.InstallationEvent) erro
387
303
for _ , repo := range installation .Repositories {
388
304
repoFullName := * repo .FullName
389
305
log .Printf ("Removing an installation %d for repo: %s" , installationId , repoFullName )
390
- _ , err := dbmodels .DB .GithubRepoRemoved (installationId , appId , repoFullName )
306
+ _ , err := dbmodels .DB .GithubRepoRemoved (installationId , appId , repoFullName , link . OrganizationID )
391
307
if err != nil {
392
308
return err
393
309
}
@@ -752,13 +668,14 @@ func (d DiggerController) GithubAppCallbackPage(c *gin.Context) {
752
668
return
753
669
}
754
670
755
- result , err := validateGithubCallback (d .GithubClientProvider , clientId , clientSecret , code , installationId64 )
671
+ result , installation , err := validateGithubCallback (d .GithubClientProvider , clientId , clientSecret , code , installationId64 )
756
672
if ! result {
757
673
log .Printf ("Failed to validated installation id, %v\n " , err )
758
674
c .String (http .StatusInternalServerError , "Failed to validate installation_id." )
759
675
return
760
676
}
761
677
678
+ // retrive org for current orgID
762
679
orgId := c .GetString (middleware .ORGANISATION_ID_KEY )
763
680
org , err := dbmodels .DB .GetOrganisationById (orgId )
764
681
if err != nil {
@@ -767,13 +684,70 @@ func (d DiggerController) GithubAppCallbackPage(c *gin.Context) {
767
684
return
768
685
}
769
686
687
+ // create a github installation link (org ID matched to installation ID)
770
688
_ , err = dbmodels .DB .CreateGithubInstallationLink (org , installationId64 )
771
689
if err != nil {
772
- log .Printf ("Error saving CreateGithubInstallationLink to database: %v" , err )
690
+ log .Printf ("Error saving GithubInstallationLink to database: %v" , err )
773
691
c .JSON (http .StatusInternalServerError , gin.H {"error" : "Error updating GitHub installation" })
774
692
return
775
693
}
776
694
695
+ client , _ , err := d .GithubClientProvider .Get (* installation .AppID , installationId64 )
696
+ if err != nil {
697
+ log .Printf ("Error retriving github client: %v" , err )
698
+ c .JSON (http .StatusInternalServerError , gin.H {"error" : "Error fetching organisation" })
699
+ return
700
+
701
+ }
702
+
703
+ // we get repos accessible to this installation
704
+ listRepos , _ , err := client .Apps .ListRepos (context .Background (), nil )
705
+ if err != nil {
706
+ log .Printf ("Failed to validated list existing repos, %v\n " , err )
707
+ c .String (http .StatusInternalServerError , "Failed to list existing repos: %v" , err )
708
+ return
709
+ }
710
+ repos := listRepos .Repositories
711
+
712
+ // resets all existing installations (soft delete)
713
+ var AppInstallation model.GithubAppInstallation
714
+ err = dbmodels .DB .GormDB .Model (& AppInstallation ).Where ("github_installation_id=?" , installationId ).Update ("status" , dbmodels .GithubAppInstallDeleted ).Error
715
+ if err != nil {
716
+ log .Printf ("Failed to update github installations: %v" , err )
717
+ c .String (http .StatusInternalServerError , "Failed to update github installations: %v" , err )
718
+ return
719
+ }
720
+
721
+ // reset all existing repos (soft delete)
722
+ var ExistingRepos []model.Repo
723
+ err = dbmodels .DB .GormDB .Delete (ExistingRepos , "organization_id=?" , orgId ).Error
724
+ if err != nil {
725
+ log .Printf ("could not delete repos: %v" , err )
726
+ c .String (http .StatusInternalServerError , "could not delete repos: %v" , err )
727
+ return
728
+ }
729
+
730
+ // here we mark repos that are available one by one
731
+ for _ , repo := range repos {
732
+ repoFullName := * repo .FullName
733
+ repoOwner := strings .Split (* repo .FullName , "/" )[0 ]
734
+ repoName := * repo .Name
735
+ repoUrl := fmt .Sprintf ("https://github.com/%v" , repoFullName )
736
+ _ , err := dbmodels .DB .GithubRepoAdded (installationId64 , * installation .AppID , * installation .Account .Login , * installation .Account .ID , repoFullName )
737
+ if err != nil {
738
+ log .Printf ("github repos added error: %v" , err )
739
+ c .String (http .StatusInternalServerError , "github repos added error: %v" , err )
740
+ return
741
+ }
742
+
743
+ _ , _ , err = createOrGetDiggerRepoForGithubRepo (repoFullName , repoOwner , repoName , repoUrl , installationId64 )
744
+ if err != nil {
745
+ log .Printf ("createOrGetDiggerRepoForGithubRepo error: %v" , err )
746
+ c .String (http .StatusInternalServerError , "createOrGetDiggerRepoForGithubRepo error: %v" , err )
747
+ return
748
+ }
749
+ }
750
+
777
751
c .HTML (http .StatusOK , "github_success.tmpl" , gin.H {})
778
752
}
779
753
@@ -824,7 +798,7 @@ func (d DiggerController) GithubReposPage(c *gin.Context) {
824
798
825
799
// why this validation is needed: https://roadie.io/blog/avoid-leaking-github-org-data/
826
800
// 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
827
- func validateGithubCallback (githubClientProvider next_utils.GithubClientProvider , clientId string , clientSecret string , code string , installationId int64 ) (bool , error ) {
801
+ func validateGithubCallback (githubClientProvider next_utils.GithubClientProvider , clientId string , clientSecret string , code string , installationId int64 ) (bool , * github. Installation , error ) {
828
802
ctx := context .Background ()
829
803
type OAuthAccessResponse struct {
830
804
AccessToken string `json:"access_token"`
@@ -835,22 +809,22 @@ func validateGithubCallback(githubClientProvider next_utils.GithubClientProvider
835
809
reqURL := fmt .Sprintf ("https://%v/login/oauth/access_token?client_id=%s&client_secret=%s&code=%s" , githubHostname , clientId , clientSecret , code )
836
810
req , err := http .NewRequest (http .MethodPost , reqURL , nil )
837
811
if err != nil {
838
- return false , fmt .Errorf ("could not create HTTP request: %v\n " , err )
812
+ return false , nil , fmt .Errorf ("could not create HTTP request: %v\n " , err )
839
813
}
840
814
req .Header .Set ("accept" , "application/json" )
841
815
842
816
res , err := httpClient .Do (req )
843
817
if err != nil {
844
- return false , fmt .Errorf ("request to login/oauth/access_token failed: %v\n " , err )
818
+ return false , nil , fmt .Errorf ("request to login/oauth/access_token failed: %v\n " , err )
845
819
}
846
820
847
821
if err != nil {
848
- return false , fmt .Errorf ("Failed to read response's body: %v\n " , err )
822
+ return false , nil , fmt .Errorf ("Failed to read response's body: %v\n " , err )
849
823
}
850
824
851
825
var t OAuthAccessResponse
852
826
if err := json .NewDecoder (res .Body ).Decode (& t ); err != nil {
853
- return false , fmt .Errorf ("could not parse JSON response: %v\n " , err )
827
+ return false , nil , fmt .Errorf ("could not parse JSON response: %v\n " , err )
854
828
}
855
829
856
830
ts := oauth2 .StaticTokenSource (
@@ -867,25 +841,28 @@ func validateGithubCallback(githubClientProvider next_utils.GithubClientProvider
867
841
client , err := githubClientProvider .NewClient (tc )
868
842
if err != nil {
869
843
log .Printf ("could create github client: %v" , err )
870
- return false , fmt .Errorf ("could not create github client: %v" , err )
844
+ return false , nil , fmt .Errorf ("could not create github client: %v" , err )
871
845
}
872
846
873
847
installationIdMatch := false
874
848
// list all installations for the user
849
+ var matchedInstallation * github.Installation
875
850
installations , _ , err := client .Apps .ListUserInstallations (ctx , nil )
876
851
if err != nil {
877
852
log .Printf ("could not retrieve installations: %v" , err )
878
- return false , fmt .Errorf ("could not retrieve installations: %v" , installationId )
853
+ return false , nil , fmt .Errorf ("could not retrieve installations: %v" , installationId )
879
854
}
880
855
log .Printf ("installations %v" , installations )
881
856
for _ , v := range installations {
882
857
log .Printf ("installation id: %v\n " , * v .ID )
883
858
if * v .ID == installationId {
859
+ matchedInstallation = v
884
860
installationIdMatch = true
885
861
}
886
862
}
887
863
if ! installationIdMatch {
888
- return false , fmt .Errorf ("InstallationId %v doesn't match any id for specified user\n " , installationId )
864
+ return false , nil , fmt .Errorf ("InstallationId %v doesn't match any id for specified user\n " , installationId )
889
865
}
890
- return true , nil
866
+
867
+ return true , matchedInstallation , nil
891
868
}
0 commit comments