Skip to content

Commit 578a153

Browse files
authored
refactor github controllers (#2124)
* allow user to create slack connect channel * support disabling of digger apply comment * refactor github controllers * refactor
1 parent 4d71d2b commit 578a153

File tree

9 files changed

+2616
-2812
lines changed

9 files changed

+2616
-2812
lines changed

backend/controllers/github.go

Lines changed: 52 additions & 2809 deletions
Large diffs are not rendered by default.
Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
package controllers
2+
3+
import (
4+
"fmt"
5+
"github.com/diggerhq/digger/backend/models"
6+
"github.com/diggerhq/digger/backend/utils"
7+
"github.com/diggerhq/digger/libs/ci/github"
8+
"github.com/gin-gonic/gin"
9+
"github.com/google/uuid"
10+
"log/slog"
11+
"net/http"
12+
"strconv"
13+
"strings"
14+
)
15+
16+
func (d DiggerController) GithubAppCallbackPage(c *gin.Context) {
17+
installationIdParams, installationIdExists := c.Request.URL.Query()["installation_id"]
18+
if !installationIdExists || len(installationIdParams) == 0 {
19+
slog.Error("There was no installation_id in the url query parameters")
20+
c.String(http.StatusBadRequest, "could not find the installation_id query parameter for github app")
21+
return
22+
}
23+
installationId := installationIdParams[0]
24+
if len(installationId) < 1 {
25+
slog.Error("Installation_id parameter is empty")
26+
c.String(http.StatusBadRequest, "installation_id parameter for github app is empty")
27+
return
28+
}
29+
//setupAction := c.Request.URL.Query()["setup_action"][0]
30+
codeParams, codeExists := c.Request.URL.Query()["code"]
31+
if !codeExists || len(codeParams) == 0 {
32+
slog.Error("There was no code in the url query parameters")
33+
c.String(http.StatusBadRequest, "could not find the code query parameter for github app")
34+
return
35+
}
36+
code := codeParams[0]
37+
if len(code) < 1 {
38+
slog.Error("Code parameter is empty")
39+
c.String(http.StatusBadRequest, "code parameter for github app is empty")
40+
return
41+
}
42+
appId := c.Request.URL.Query().Get("state")
43+
44+
slog.Info("Processing GitHub app callback", "installationId", installationId, "appId", appId)
45+
46+
clientId, clientSecret, _, _, err := d.GithubClientProvider.FetchCredentials(appId)
47+
if err != nil {
48+
slog.Error("Could not fetch credentials for GitHub app", "appId", appId, "error", err)
49+
c.String(http.StatusInternalServerError, "could not find credentials for github app")
50+
return
51+
}
52+
53+
installationId64, err := strconv.ParseInt(installationId, 10, 64)
54+
if err != nil {
55+
slog.Error("Failed to parse installation ID",
56+
"installationId", installationId,
57+
"error", err,
58+
)
59+
c.String(http.StatusInternalServerError, "Failed to parse installation_id.")
60+
return
61+
}
62+
63+
slog.Debug("Validating GitHub callback", "installationId", installationId64, "clientId", clientId)
64+
65+
result, installation, err := validateGithubCallback(d.GithubClientProvider, clientId, clientSecret, code, installationId64)
66+
if !result {
67+
slog.Error("Failed to validate installation ID",
68+
"installationId", installationId64,
69+
"error", err,
70+
)
71+
c.String(http.StatusInternalServerError, "Failed to validate installation_id.")
72+
return
73+
}
74+
75+
// TODO: Lookup org in GithubAppInstallation by installationID if found use that installationID otherwise
76+
// create a new org for this installationID
77+
// retrieve org for current orgID
78+
installationIdInt64, err := strconv.ParseInt(installationId, 10, 64)
79+
if err != nil {
80+
slog.Error("Failed to parse installation ID as int64",
81+
"installationId", installationId,
82+
"error", err,
83+
)
84+
c.JSON(http.StatusInternalServerError, gin.H{"error": "installationId could not be parsed"})
85+
return
86+
}
87+
88+
slog.Debug("Looking up GitHub app installation link", "installationId", installationIdInt64)
89+
90+
var link *models.GithubAppInstallationLink
91+
link, err = models.DB.GetGithubAppInstallationLink(installationIdInt64)
92+
if err != nil {
93+
slog.Error("Error getting GitHub app installation link",
94+
"installationId", installationIdInt64,
95+
"error", err,
96+
)
97+
c.JSON(http.StatusInternalServerError, gin.H{"error": "error getting github app link"})
98+
return
99+
}
100+
101+
if link == nil {
102+
slog.Info("No existing link found, creating new organization and link",
103+
"installationId", installationId,
104+
)
105+
106+
name := fmt.Sprintf("dggr-def-%v", uuid.NewString()[:8])
107+
externalId := uuid.NewString()
108+
109+
slog.Debug("Creating new organization",
110+
"name", name,
111+
"externalId", externalId,
112+
)
113+
114+
org, err := models.DB.CreateOrganisation(name, "digger", externalId)
115+
if err != nil {
116+
slog.Error("Error creating organization",
117+
"name", name,
118+
"error", err,
119+
)
120+
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error with CreateOrganisation"})
121+
return
122+
}
123+
124+
slog.Debug("Creating GitHub installation link",
125+
"orgId", org.ID,
126+
"installationId", installationId64,
127+
)
128+
129+
link, err = models.DB.CreateGithubInstallationLink(org, installationId64)
130+
if err != nil {
131+
slog.Error("Error creating GitHub installation link",
132+
"orgId", org.ID,
133+
"installationId", installationId64,
134+
"error", err,
135+
)
136+
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error with CreateGithubInstallationLink"})
137+
return
138+
}
139+
140+
slog.Info("Created new organization and installation link",
141+
"orgId", org.ID,
142+
"installationId", installationId64,
143+
)
144+
} else {
145+
slog.Info("Found existing installation link",
146+
"orgId", link.OrganisationId,
147+
"installationId", installationId64,
148+
)
149+
}
150+
151+
org := link.Organisation
152+
orgId := link.OrganisationId
153+
154+
// create a github installation link (org ID matched to installation ID)
155+
_, err = models.DB.CreateGithubInstallationLink(org, installationId64)
156+
if err != nil {
157+
slog.Error("Error creating GitHub installation link",
158+
"orgId", orgId,
159+
"installationId", installationId64,
160+
"error", err,
161+
)
162+
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error updating GitHub installation"})
163+
return
164+
}
165+
166+
slog.Debug("Getting GitHub client",
167+
"appId", *installation.AppID,
168+
"installationId", installationId64,
169+
)
170+
171+
client, _, err := d.GithubClientProvider.Get(*installation.AppID, installationId64)
172+
if err != nil {
173+
slog.Error("Error retrieving GitHub client",
174+
"appId", *installation.AppID,
175+
"installationId", installationId64,
176+
"error", err,
177+
)
178+
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error fetching organisation"})
179+
return
180+
}
181+
182+
// we get repos accessible to this installation
183+
slog.Debug("Listing repositories for installation", "installationId", installationId64)
184+
185+
repos, err := github.ListGithubRepos(client)
186+
if err != nil {
187+
slog.Error("Failed to list existing repositories",
188+
"installationId", installationId64,
189+
"error", err,
190+
)
191+
c.String(http.StatusInternalServerError, "Failed to list existing repos: %v", err)
192+
return
193+
}
194+
195+
// resets all existing installations (soft delete)
196+
slog.Debug("Resetting existing GitHub installations",
197+
"installationId", installationId,
198+
)
199+
200+
var AppInstallation models.GithubAppInstallation
201+
err = models.DB.GormDB.Model(&AppInstallation).Where("github_installation_id=?", installationId).Update("status", models.GithubAppInstallDeleted).Error
202+
if err != nil {
203+
slog.Error("Failed to update GitHub installations",
204+
"installationId", installationId,
205+
"error", err,
206+
)
207+
c.String(http.StatusInternalServerError, "Failed to update github installations: %v", err)
208+
return
209+
}
210+
211+
// reset all existing repos (soft delete)
212+
slog.Debug("Soft deleting existing repositories",
213+
"orgId", orgId,
214+
)
215+
216+
var ExistingRepos []models.Repo
217+
err = models.DB.GormDB.Delete(ExistingRepos, "organisation_id=?", orgId).Error
218+
if err != nil {
219+
slog.Error("Could not delete repositories",
220+
"orgId", orgId,
221+
"error", err,
222+
)
223+
c.String(http.StatusInternalServerError, "could not delete repos: %v", err)
224+
return
225+
}
226+
227+
// here we mark repos that are available one by one
228+
slog.Info("Adding repositories to organization",
229+
"orgId", orgId,
230+
"repoCount", len(repos),
231+
)
232+
233+
for i, repo := range repos {
234+
repoFullName := *repo.FullName
235+
repoOwner := strings.Split(*repo.FullName, "/")[0]
236+
repoName := *repo.Name
237+
repoUrl := fmt.Sprintf("https://%v/%v", utils.GetGithubHostname(), repoFullName)
238+
239+
slog.Debug("Processing repository",
240+
"index", i+1,
241+
"repoFullName", repoFullName,
242+
"repoOwner", repoOwner,
243+
"repoName", repoName,
244+
)
245+
246+
_, err := models.DB.GithubRepoAdded(
247+
installationId64,
248+
*installation.AppID,
249+
*installation.Account.Login,
250+
*installation.Account.ID,
251+
repoFullName,
252+
)
253+
if err != nil {
254+
slog.Error("Error recording GitHub repository",
255+
"repoFullName", repoFullName,
256+
"error", err,
257+
)
258+
c.String(http.StatusInternalServerError, "github repos added error: %v", err)
259+
return
260+
}
261+
262+
cloneUrl := *repo.CloneURL
263+
defaultBranch := *repo.DefaultBranch
264+
265+
_, _, err = createOrGetDiggerRepoForGithubRepo(repoFullName, repoOwner, repoName, repoUrl, installationId64, *installation.AppID, defaultBranch, cloneUrl)
266+
if err != nil {
267+
slog.Error("Error creating or getting Digger repo",
268+
"repoFullName", repoFullName,
269+
"error", err,
270+
)
271+
c.String(http.StatusInternalServerError, "createOrGetDiggerRepoForGithubRepo error: %v", err)
272+
return
273+
}
274+
}
275+
276+
slog.Info("GitHub app callback processed successfully",
277+
"installationId", installationId64,
278+
"orgId", orgId,
279+
"repoCount", len(repos),
280+
)
281+
282+
c.HTML(http.StatusOK, "github_success.tmpl", gin.H{})
283+
}

0 commit comments

Comments
 (0)