@@ -6,12 +6,23 @@ import (
66 "encoding/json"
77 "errors"
88 "fmt"
9+ "github.com/diggerhq/digger/backend/middleware"
910 "github.com/diggerhq/digger/backend/segment"
11+ backend_utils "github.com/diggerhq/digger/backend/utils"
1012 "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"
1115 orchestrator_scheduler "github.com/diggerhq/digger/libs/scheduler"
1216 "github.com/diggerhq/digger/next/ci_backends"
17+ "github.com/diggerhq/digger/next/dbmodels"
1318 "github.com/diggerhq/digger/next/model"
1419 "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"
1526 "gorm.io/gorm"
1627 "log"
1728 "math/rand"
@@ -22,18 +33,6 @@ import (
2233 "reflect"
2334 "strconv"
2435 "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"
3736)
3837
3938type DiggerController struct {
@@ -65,14 +64,6 @@ func (d DiggerController) GithubAppWebHook(c *gin.Context) {
6564
6665 switch event := event .(type ) {
6766 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- }
7667
7768 if * event .Action == "deleted" {
7869 err := handleInstallationDeletedEvent (event )
@@ -81,20 +72,7 @@ func (d DiggerController) GithubAppWebHook(c *gin.Context) {
8172 return
8273 }
8374 }
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+
9876 case * github.IssueCommentEvent :
9977 log .Printf ("IssueCommentEvent, action: %v\n " , * event .Action )
10078 case * github.PullRequestEvent :
@@ -277,9 +255,11 @@ func createOrGetDiggerRepoForGithubRepo(ghRepoFullName string, ghRepoOrganisatio
277255
278256 diggerRepoName := strings .ReplaceAll (ghRepoFullName , "/" , "-" )
279257
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 )
281261
282- if err != nil {
262+ if r . Error != nil {
283263 if errors .Is (err , gorm .ErrRecordNotFound ) {
284264 log .Printf ("repo not found, will proceed with repo creation" )
285265 } else {
@@ -288,12 +268,14 @@ func createOrGetDiggerRepoForGithubRepo(ghRepoFullName string, ghRepoOrganisatio
288268 }
289269 }
290270
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
294276 }
295277
296- repo , err = dbmodels .DB .CreateRepo (diggerRepoName , ghRepoFullName , ghRepoOrganisation , ghRepoName , ghRepoUrl , org , `
278+ repo , err : = dbmodels .DB .CreateRepo (diggerRepoName , ghRepoFullName , ghRepoOrganisation , ghRepoName , ghRepoUrl , org , `
297279generate_projects:
298280 include: "."
299281` )
@@ -305,72 +287,6 @@ generate_projects:
305287 return repo , org , nil
306288}
307289
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-
374290func handleInstallationDeletedEvent (installation * github.InstallationEvent ) error {
375291 installationId := * installation .Installation .ID
376292 appId := * installation .Installation .AppID
@@ -387,7 +303,7 @@ func handleInstallationDeletedEvent(installation *github.InstallationEvent) erro
387303 for _ , repo := range installation .Repositories {
388304 repoFullName := * repo .FullName
389305 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 )
391307 if err != nil {
392308 return err
393309 }
@@ -752,13 +668,14 @@ func (d DiggerController) GithubAppCallbackPage(c *gin.Context) {
752668 return
753669 }
754670
755- result , err := validateGithubCallback (d .GithubClientProvider , clientId , clientSecret , code , installationId64 )
671+ result , installation , err := validateGithubCallback (d .GithubClientProvider , clientId , clientSecret , code , installationId64 )
756672 if ! result {
757673 log .Printf ("Failed to validated installation id, %v\n " , err )
758674 c .String (http .StatusInternalServerError , "Failed to validate installation_id." )
759675 return
760676 }
761677
678+ // retrive org for current orgID
762679 orgId := c .GetString (middleware .ORGANISATION_ID_KEY )
763680 org , err := dbmodels .DB .GetOrganisationById (orgId )
764681 if err != nil {
@@ -767,13 +684,70 @@ func (d DiggerController) GithubAppCallbackPage(c *gin.Context) {
767684 return
768685 }
769686
687+ // create a github installation link (org ID matched to installation ID)
770688 _ , err = dbmodels .DB .CreateGithubInstallationLink (org , installationId64 )
771689 if err != nil {
772- log .Printf ("Error saving CreateGithubInstallationLink to database: %v" , err )
690+ log .Printf ("Error saving GithubInstallationLink to database: %v" , err )
773691 c .JSON (http .StatusInternalServerError , gin.H {"error" : "Error updating GitHub installation" })
774692 return
775693 }
776694
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+
777751 c .HTML (http .StatusOK , "github_success.tmpl" , gin.H {})
778752}
779753
@@ -824,7 +798,7 @@ func (d DiggerController) GithubReposPage(c *gin.Context) {
824798
825799// why this validation is needed: https://roadie.io/blog/avoid-leaking-github-org-data/
826800// 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 ) {
828802 ctx := context .Background ()
829803 type OAuthAccessResponse struct {
830804 AccessToken string `json:"access_token"`
@@ -835,22 +809,22 @@ func validateGithubCallback(githubClientProvider next_utils.GithubClientProvider
835809 reqURL := fmt .Sprintf ("https://%v/login/oauth/access_token?client_id=%s&client_secret=%s&code=%s" , githubHostname , clientId , clientSecret , code )
836810 req , err := http .NewRequest (http .MethodPost , reqURL , nil )
837811 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 )
839813 }
840814 req .Header .Set ("accept" , "application/json" )
841815
842816 res , err := httpClient .Do (req )
843817 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 )
845819 }
846820
847821 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 )
849823 }
850824
851825 var t OAuthAccessResponse
852826 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 )
854828 }
855829
856830 ts := oauth2 .StaticTokenSource (
@@ -867,25 +841,28 @@ func validateGithubCallback(githubClientProvider next_utils.GithubClientProvider
867841 client , err := githubClientProvider .NewClient (tc )
868842 if err != nil {
869843 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 )
871845 }
872846
873847 installationIdMatch := false
874848 // list all installations for the user
849+ var matchedInstallation * github.Installation
875850 installations , _ , err := client .Apps .ListUserInstallations (ctx , nil )
876851 if err != nil {
877852 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 )
879854 }
880855 log .Printf ("installations %v" , installations )
881856 for _ , v := range installations {
882857 log .Printf ("installation id: %v\n " , * v .ID )
883858 if * v .ID == installationId {
859+ matchedInstallation = v
884860 installationIdMatch = true
885861 }
886862 }
887863 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 )
889865 }
890- return true , nil
866+
867+ return true , matchedInstallation , nil
891868}
0 commit comments