Skip to content

Commit cdf310e

Browse files
committed
Move drift reconciliation hook from EE to CE
1 parent f3fbcfd commit cdf310e

File tree

4 files changed

+201
-194
lines changed

4 files changed

+201
-194
lines changed
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
package hooks
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"regexp"
7+
"strconv"
8+
"strings"
9+
10+
"github.com/diggerhq/digger/backend/ci_backends"
11+
controllers "github.com/diggerhq/digger/backend/controllers"
12+
"github.com/diggerhq/digger/backend/locking"
13+
"github.com/diggerhq/digger/backend/models"
14+
"github.com/diggerhq/digger/backend/utils"
15+
"github.com/diggerhq/digger/libs/ci/generic"
16+
dg_github "github.com/diggerhq/digger/libs/ci/github"
17+
comment_updater "github.com/diggerhq/digger/libs/comment_utils/reporting"
18+
"github.com/diggerhq/digger/libs/digger_config"
19+
dg_locking "github.com/diggerhq/digger/libs/locking"
20+
"github.com/diggerhq/digger/libs/scheduler"
21+
"github.com/google/go-github/v61/github"
22+
"github.com/samber/lo"
23+
)
24+
25+
// DriftReconcilliationHook handles drift Issue comments (not PRs) allowing
26+
// "digger apply" or "digger unlock" on issues with titles like
27+
// "Drift detected in project: <projectName>".
28+
//
29+
// Implementation is verbatim from EE, with CE imports.
30+
var DriftReconcilliationHook controllers.IssueCommentHook = func(gh utils.GithubClientProvider, payload *github.IssueCommentEvent, ciBackendProvider ci_backends.CiBackendProvider) error {
31+
log.Printf("handling the drift reconcilliation hook")
32+
installationId := *payload.Installation.ID
33+
repoName := *payload.Repo.Name
34+
repoOwner := *payload.Repo.Owner.Login
35+
repoFullName := *payload.Repo.FullName
36+
cloneURL := *payload.Repo.CloneURL
37+
issueTitle := *payload.Issue.Title
38+
issueNumber := *payload.Issue.Number
39+
userCommentId := *payload.GetComment().ID
40+
actor := *payload.Sender.Login
41+
commentBody := *payload.Comment.Body
42+
defaultBranch := *payload.Repo.DefaultBranch
43+
isPullRequest := payload.Issue.IsPullRequest()
44+
45+
if isPullRequest {
46+
log.Printf("Comment is not an issue, ignoring")
47+
return nil
48+
}
49+
50+
// checking that the title of the issue matches regex
51+
var projectName string
52+
re := regexp.MustCompile(`^Drift detected in project:\s*(\S+)`)
53+
matches := re.FindStringSubmatch(issueTitle)
54+
if len(matches) > 1 {
55+
projectName = matches[1]
56+
} else {
57+
log.Printf("does not look like a drift issue, ignoring")
58+
}
59+
60+
link, err := models.DB.GetGithubAppInstallationLink(installationId)
61+
if err != nil {
62+
log.Printf("Error getting GetGithubAppInstallationLink: %v", err)
63+
return fmt.Errorf("error getting github app link")
64+
}
65+
orgId := link.OrganisationId
66+
67+
if *payload.Action != "created" {
68+
log.Printf("comment is not of type 'created', ignoring")
69+
return nil
70+
}
71+
72+
allowedCommands := []string{"digger apply", "digger unlock"}
73+
if !lo.Contains(allowedCommands, strings.TrimSpace(*payload.Comment.Body)) {
74+
log.Printf("comment is not in allowed commands, ignoring")
75+
log.Printf("allowed commands: %v", allowedCommands)
76+
return nil
77+
}
78+
79+
diggerYmlStr, ghService, config, projectsGraph, err := controllers.GetDiggerConfigForBranch(gh, installationId, repoFullName, repoOwner, repoName, cloneURL, defaultBranch, nil, nil)
80+
if err != nil {
81+
log.Printf("Error loading digger.yml: %v", err)
82+
return fmt.Errorf("error loading digger.yml")
83+
}
84+
85+
commentIdStr := strconv.FormatInt(userCommentId, 10)
86+
err = ghService.CreateCommentReaction(commentIdStr, string(dg_github.GithubCommentEyesReaction))
87+
if err != nil {
88+
log.Printf("CreateCommentReaction error: %v", err)
89+
}
90+
91+
diggerCommand, err := scheduler.GetCommandFromComment(*payload.Comment.Body)
92+
if err != nil {
93+
log.Printf("unknown digger command in comment: %v", *payload.Comment.Body)
94+
utils.InitCommentReporter(ghService, issueNumber, fmt.Sprintf(":x: Could not recognise comment, error: %v", err))
95+
return fmt.Errorf("unknown digger command in comment %v", err)
96+
}
97+
98+
// attempting to lock for performing drift apply command
99+
prLock := dg_locking.PullRequestLock{
100+
InternalLock: locking.BackendDBLock{
101+
OrgId: orgId,
102+
},
103+
CIService: ghService,
104+
Reporter: comment_updater.NoopReporter{},
105+
ProjectName: projectName,
106+
ProjectNamespace: repoFullName,
107+
PrNumber: issueNumber,
108+
}
109+
err = dg_locking.PerformLockingActionFromCommand(prLock, *diggerCommand)
110+
if err != nil {
111+
utils.InitCommentReporter(ghService, issueNumber, fmt.Sprintf(":x: Failed perform lock action on project: %v %v", projectName, err))
112+
return fmt.Errorf("failed perform lock action on project: %v %v", projectName, err)
113+
}
114+
115+
if *diggerCommand == scheduler.DiggerCommandUnlock {
116+
utils.InitCommentReporter(ghService, issueNumber, fmt.Sprintf(":white_check_mark: Command %v completed successfully", *diggerCommand))
117+
return nil
118+
}
119+
120+
// === if we get here its a "digger apply command and we are already locked for this project ====
121+
// perform apply here then unlock the project
122+
commentReporter, err := utils.InitCommentReporter(ghService, issueNumber, ":construction_worker: Digger starting....")
123+
if err != nil {
124+
log.Printf("Error initializing comment reporter: %v", err)
125+
return fmt.Errorf("error initializing comment reporter")
126+
}
127+
128+
impactedProjects := config.GetProjects(projectName)
129+
jobs, coverAllImpactedProjects, err := generic.ConvertIssueCommentEventToJobs(repoFullName, actor, issueNumber, commentBody, impactedProjects, nil, config.Workflows, defaultBranch, defaultBranch, false)
130+
if err != nil {
131+
log.Printf("Error converting event to jobs: %v", err)
132+
utils.InitCommentReporter(ghService, issueNumber, fmt.Sprintf(":x: Error converting event to jobs: %v", err))
133+
return fmt.Errorf("error converting event to jobs")
134+
}
135+
log.Printf("GitHub IssueComment event converted to Jobs successfully\n")
136+
137+
err = utils.ReportInitialJobsStatus(commentReporter, jobs)
138+
if err != nil {
139+
log.Printf("Failed to comment initial status for jobs: %v", err)
140+
utils.InitCommentReporter(ghService, issueNumber, fmt.Sprintf(":x: Failed to comment initial status for jobs: %v", err))
141+
return fmt.Errorf("failed to comment initial status for jobs")
142+
}
143+
144+
impactedProjectsMap := make(map[string]digger_config.Project)
145+
for _, p := range impactedProjects {
146+
impactedProjectsMap[p.Name] = p
147+
}
148+
149+
impactedProjectsJobMap := make(map[string]scheduler.Job)
150+
for _, j := range jobs {
151+
impactedProjectsJobMap[j.ProjectName] = j
152+
}
153+
154+
reporterCommentId, err := strconv.ParseInt(commentReporter.CommentId, 10, 64)
155+
if err != nil {
156+
log.Printf("strconv.ParseInt error: %v", err)
157+
utils.InitCommentReporter(ghService, issueNumber, fmt.Sprintf(":x: could not handle commentId: %v", err))
158+
}
159+
160+
batchId, _, err := utils.ConvertJobsToDiggerJobs(*diggerCommand, "github", orgId, impactedProjectsJobMap, impactedProjectsMap, projectsGraph, installationId, defaultBranch, issueNumber, repoOwner, repoName, repoFullName, "", reporterCommentId, diggerYmlStr, 0, "", false, coverAllImpactedProjects, nil)
161+
if err != nil {
162+
log.Printf("ConvertJobsToDiggerJobs error: %v", err)
163+
utils.InitCommentReporter(ghService, issueNumber, fmt.Sprintf(":x: ConvertJobsToDiggerJobs error: %v", err))
164+
return fmt.Errorf("error convertingjobs")
165+
}
166+
167+
ciBackend, err := ciBackendProvider.GetCiBackend(
168+
ci_backends.CiBackendOptions{
169+
GithubClientProvider: gh,
170+
GithubInstallationId: installationId,
171+
RepoName: repoName,
172+
RepoOwner: repoOwner,
173+
RepoFullName: repoFullName,
174+
},
175+
)
176+
if err != nil {
177+
log.Printf("GetCiBackend error: %v", err)
178+
utils.InitCommentReporter(ghService, issueNumber, fmt.Sprintf(":x: GetCiBackend error: %v", err))
179+
return fmt.Errorf("error fetching ci backed %v", err)
180+
}
181+
182+
err = controllers.TriggerDiggerJobs(ciBackend, repoFullName, repoOwner, repoName, batchId, issueNumber, ghService, gh)
183+
if err != nil {
184+
log.Printf("TriggerDiggerJobs error: %v", err)
185+
utils.InitCommentReporter(ghService, issueNumber, fmt.Sprintf(":x: TriggerDiggerJobs error: %v", err))
186+
return fmt.Errorf("error triggering Digger Jobs")
187+
}
188+
189+
// === now unlocking the project ===
190+
err = dg_locking.PerformLockingActionFromCommand(prLock, scheduler.DiggerCommandUnlock)
191+
if err != nil {
192+
utils.InitCommentReporter(ghService, issueNumber, fmt.Sprintf(":x: Failed perform lock action on project: %v %v", projectName, err))
193+
return fmt.Errorf("failed perform lock action on project: %v %v", projectName, err)
194+
}
195+
196+
return nil
197+
}
198+

backend/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/diggerhq/digger/backend/ci_backends"
88
"github.com/diggerhq/digger/backend/config"
99
"github.com/diggerhq/digger/backend/controllers"
10+
"github.com/diggerhq/digger/backend/hooks"
1011
"github.com/diggerhq/digger/backend/utils"
1112
)
1213

@@ -17,7 +18,7 @@ func main() {
1718
ghController := controllers.DiggerController{
1819
CiBackendProvider: ci_backends.DefaultBackendProvider{},
1920
GithubClientProvider: utils.DiggerGithubRealClientProvider{},
20-
GithubWebhookPostIssueCommentHooks: make([]controllers.IssueCommentHook, 0),
21+
GithubWebhookPostIssueCommentHooks: []controllers.IssueCommentHook{hooks.DriftReconcilliationHook},
2122
}
2223
r := bootstrap.Bootstrap(templates, ghController)
2324
r.GET("/", controllers.Home)

0 commit comments

Comments
 (0)