Skip to content

Commit ebc8d22

Browse files
Fix GitHub App OAuth flow for repository updates (#2471)
- Make code parameter optional in callback handler - Add webhook handler for InstallationRepositoriesEvent - Add test coverage for repository add/remove events Fixes issue where users cannot add/remove individual repositories from existing GitHub App installations. GitHub only provides OAuth code during fresh installations, not during repository updates.
1 parent 9165fb6 commit ebc8d22

File tree

4 files changed

+120
-3
lines changed

4 files changed

+120
-3
lines changed

backend/controllers/github.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,20 @@ func (d DiggerController) GithubAppWebHook(c *gin.Context) {
7474
return
7575
}
7676
}
77+
case *github.InstallationRepositoriesEvent:
78+
slog.Info("Processing InstallationRepositoriesEvent",
79+
"action", *event.Action,
80+
"installationId", *event.Installation.ID,
81+
"repositoriesAdded", len(event.RepositoriesAdded),
82+
"repositoriesRemoved", len(event.RepositoriesRemoved),
83+
)
84+
85+
err := handleInstallationRepositoriesEvent(event, appId64)
86+
if err != nil {
87+
slog.Error("Failed to handle installation repositories event", "error", err)
88+
c.String(http.StatusAccepted, "Failed to handle webhook event.")
89+
return
90+
}
7791
case *github.PushEvent:
7892
slog.Info("Processing PushEvent",
7993
"repo", *event.Repo.FullName,

backend/controllers/github_callback.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,19 @@ func (d DiggerController) GithubAppCallbackPage(c *gin.Context) {
2828
c.String(http.StatusBadRequest, "installation_id parameter for github app is empty")
2929
return
3030
}
31+
3132
//setupAction := c.Request.URL.Query()["setup_action"][0]
3233
codeParams, codeExists := c.Request.URL.Query()["code"]
34+
35+
// Code parameter is only provided for fresh installations, not for updates
36+
// If code is missing, this is likely an update - just show success page
37+
// The actual repository changes will be handled by the InstallationRepositoriesEvent webhook
3338
if !codeExists || len(codeParams) == 0 {
34-
slog.Error("There was no code in the url query parameters")
35-
c.String(http.StatusBadRequest, "could not find the code query parameter for github app")
39+
slog.Info("No code parameter - likely an installation update, showing success page")
40+
c.HTML(http.StatusOK, "github_success.tmpl", gin.H{})
3641
return
3742
}
43+
3844
code := codeParams[0]
3945
if len(code) < 1 {
4046
slog.Error("Code parameter is empty")

backend/controllers/github_installation.go

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package controllers
22

33
import (
4+
"log/slog"
5+
46
"github.com/diggerhq/digger/backend/models"
57
"github.com/google/go-github/v61/github"
6-
"log/slog"
78
)
89

910
func handleInstallationDeletedEvent(installation *github.InstallationEvent, appId int64) error {
@@ -47,3 +48,68 @@ func handleInstallationDeletedEvent(installation *github.InstallationEvent, appI
4748
slog.Info("Successfully handled installation deleted event", "installationId", installationId)
4849
return nil
4950
}
51+
52+
func handleInstallationRepositoriesEvent(event *github.InstallationRepositoriesEvent, appId int64) error {
53+
installationId := *event.Installation.ID
54+
action := *event.Action
55+
56+
slog.Info("Handling installation repositories event",
57+
"installationId", installationId,
58+
"appId", appId,
59+
"action", action,
60+
"repositoriesAdded", len(event.RepositoriesAdded),
61+
"repositoriesRemoved", len(event.RepositoriesRemoved),
62+
)
63+
64+
// Handle removed repositories
65+
for _, repo := range event.RepositoriesRemoved {
66+
repoFullName := *repo.FullName
67+
slog.Info("Removing repository from installation",
68+
"installationId", installationId,
69+
"repoFullName", repoFullName,
70+
)
71+
72+
_, err := models.DB.GithubRepoRemoved(installationId, appId, repoFullName)
73+
if err != nil {
74+
slog.Error("Error removing GitHub repo",
75+
"installationId", installationId,
76+
"repoFullName", repoFullName,
77+
"error", err,
78+
)
79+
return err
80+
}
81+
}
82+
83+
// Handle added repositories
84+
for _, repo := range event.RepositoriesAdded {
85+
repoFullName := *repo.FullName
86+
slog.Info("Adding repository to installation",
87+
"installationId", installationId,
88+
"repoFullName", repoFullName,
89+
)
90+
91+
_, err := models.DB.GithubRepoAdded(
92+
installationId,
93+
appId,
94+
*event.Installation.Account.Login,
95+
*event.Installation.Account.ID,
96+
repoFullName,
97+
)
98+
if err != nil {
99+
slog.Error("Error adding GitHub repo",
100+
"installationId", installationId,
101+
"repoFullName", repoFullName,
102+
"error", err,
103+
)
104+
return err
105+
}
106+
}
107+
108+
slog.Info("Successfully handled installation repositories event",
109+
"installationId", installationId,
110+
"repositoriesAdded", len(event.RepositoriesAdded),
111+
"repositoriesRemoved", len(event.RepositoriesRemoved),
112+
)
113+
114+
return nil
115+
}

backend/controllers/github_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,3 +866,34 @@ func TestJobsTreeWithThreeLevels(t *testing.T) {
866866
assert.NoError(t, err)
867867
assert.Equal(t, result["333"].DiggerJobID, parentLinks[0].ParentDiggerJobId)
868868
}
869+
870+
func TestHandleInstallationRepositoriesEvent(t *testing.T) {
871+
teardownSuite, database := setupSuite(t)
872+
defer teardownSuite(t)
873+
874+
// Test repositories added event
875+
var addedEvent github.InstallationRepositoriesEvent
876+
err := json.Unmarshal([]byte(installationRepositoriesAddedPayload), &addedEvent)
877+
assert.NoError(t, err)
878+
879+
err = handleInstallationRepositoriesEvent(&addedEvent, 360162)
880+
assert.NoError(t, err)
881+
882+
// Verify repo was added
883+
installation, err := database.GetGithubAppInstallationByIdAndRepo(41584295, "diggerhq/test-github-action")
884+
assert.NoError(t, err)
885+
assert.NotNil(t, installation)
886+
assert.Equal(t, "diggerhq/test-github-action", installation.Repo)
887+
888+
// Test repositories removed event
889+
var removedEvent github.InstallationRepositoriesEvent
890+
err = json.Unmarshal([]byte(installationRepositoriesDeletedPayload), &removedEvent)
891+
assert.NoError(t, err)
892+
893+
err = handleInstallationRepositoriesEvent(&removedEvent, 360162)
894+
assert.NoError(t, err)
895+
896+
// Verify repo was removed (soft deleted)
897+
installation, err = database.GetGithubAppInstallationByIdAndRepo(41584295, "diggerhq/test-github-action")
898+
assert.Error(t, err) // Should error because repo is deleted
899+
}

0 commit comments

Comments
 (0)