Skip to content

Commit dd36b10

Browse files
authored
also delete repository when deletion event comes through (#1674)
* also delete repository when deletion event comes through
1 parent dcf035f commit dd36b10

File tree

2 files changed

+122
-119
lines changed

2 files changed

+122
-119
lines changed

next/controllers/github.go

Lines changed: 95 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -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

3938
type 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, `
297279
generate_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-
374290
func 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

Comments
 (0)