Skip to content

Commit 3f0a92b

Browse files
authored
Feat bitbucket support (#1890)
* bb support
1 parent bab3696 commit 3f0a92b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1477
-152
lines changed

backend/bootstrap/main.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@ package bootstrap
33
import (
44
"embed"
55
"fmt"
6-
"github.com/diggerhq/digger/backend/config"
7-
"github.com/diggerhq/digger/backend/segment"
8-
pprof_gin "github.com/gin-contrib/pprof"
96
"html/template"
107
"io/fs"
118
"log"
@@ -15,6 +12,10 @@ import (
1512
"runtime"
1613
"runtime/pprof"
1714

15+
"github.com/diggerhq/digger/backend/config"
16+
"github.com/diggerhq/digger/backend/segment"
17+
pprof_gin "github.com/gin-contrib/pprof"
18+
1819
"time"
1920

2021
"github.com/diggerhq/digger/backend/controllers"
@@ -216,14 +217,20 @@ func Bootstrap(templates embed.FS, diggerController controllers.DiggerController
216217

217218
if enableApi := os.Getenv("DIGGER_ENABLE_API_ENDPOINTS"); enableApi == "true" {
218219
apiGroup := r.Group("/api")
219-
apiGroup.Use(middleware.HeadersApiAuth())
220+
apiGroup.Use(middleware.InternalApiAuth(), middleware.HeadersApiAuth())
220221

221222
reposApiGroup := apiGroup.Group("/repos")
222223
reposApiGroup.GET("/", controllers.ListReposApi)
223224
reposApiGroup.GET("/:repo_id/jobs", controllers.GetJobsForRepoApi)
224225

225226
githubApiGroup := apiGroup.Group("/github")
226227
githubApiGroup.POST("/link", controllers.LinkGithubInstallationToOrgApi)
228+
229+
vcsApiGroup := apiGroup.Group("/connections")
230+
vcsApiGroup.GET("/:id", controllers.GetVCSConnection)
231+
vcsApiGroup.GET("/", controllers.ListVCSConnectionsApi)
232+
vcsApiGroup.POST("/", controllers.CreateVCSConnectionApi)
233+
vcsApiGroup.DELETE("/:id", controllers.DeleteVCSConnection)
227234
}
228235

229236
return r

backend/controllers/bitbucket.go

Lines changed: 0 additions & 1 deletion
This file was deleted.

backend/controllers/cache.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func (d DiggerController) UpdateRepoCache(c *gin.Context) {
6767

6868
// update the cache here, do it async for immediate response
6969
go func() {
70-
err = utils.CloneGitRepoAndDoAction(cloneUrl, branch, "", *token, func(dir string) error {
70+
err = utils.CloneGitRepoAndDoAction(cloneUrl, branch, "", *token, "", func(dir string) error {
7171
diggerYmlBytes, err := os.ReadFile(path.Join(dir, "digger.yml"))
7272
diggerYmlStr = string(diggerYmlBytes)
7373
config, _, _, err = dg_configuration.LoadDiggerConfig(dir, true, nil)

backend/controllers/connections.go

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
package controllers
2+
3+
import (
4+
"errors"
5+
"github.com/samber/lo"
6+
"log"
7+
"net/http"
8+
"os"
9+
10+
"github.com/diggerhq/digger/backend/utils"
11+
"gorm.io/gorm"
12+
13+
"github.com/diggerhq/digger/backend/middleware"
14+
"github.com/diggerhq/digger/backend/models"
15+
"github.com/gin-gonic/gin"
16+
)
17+
18+
func ListVCSConnectionsApi(c *gin.Context) {
19+
organisationId := c.GetString(middleware.ORGANISATION_ID_KEY)
20+
organisationSource := c.GetString(middleware.ORGANISATION_SOURCE_KEY)
21+
22+
var org models.Organisation
23+
err := models.DB.GormDB.Where("external_id = ? AND external_source = ?", organisationId, organisationSource).First(&org).Error
24+
if err != nil {
25+
log.Printf("could not fetch organisation: %v err: %v", organisationId, err)
26+
c.JSON(http.StatusNotFound, gin.H{"error": "Could not fetch organisation"})
27+
return
28+
}
29+
30+
var connections []models.VCSConnection
31+
err = models.DB.GormDB.Where("organisation_id = ?", org.ID).Find(&connections).Error
32+
if err != nil {
33+
log.Printf("could not fetch VCS connections: %v", err)
34+
c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not fetch VCS connections"})
35+
return
36+
}
37+
38+
connectionsSlim := lo.Map(connections, func(c models.VCSConnection, i int) gin.H {
39+
return gin.H{
40+
"connection_id": c.ID,
41+
"vcs": "bitbucket",
42+
"connection_name": c.Name,
43+
}
44+
})
45+
c.JSON(http.StatusOK, gin.H{
46+
"result": connectionsSlim,
47+
})
48+
}
49+
50+
func CreateVCSConnectionApi(c *gin.Context) {
51+
organisationId := c.GetString(middleware.ORGANISATION_ID_KEY)
52+
organisationSource := c.GetString(middleware.ORGANISATION_SOURCE_KEY)
53+
54+
var org models.Organisation
55+
err := models.DB.GormDB.Where("external_id = ? AND external_source = ?", organisationId, organisationSource).First(&org).Error
56+
if err != nil {
57+
log.Printf("could not fetch organisation: %v err: %v", organisationId, err)
58+
c.JSON(http.StatusNotFound, gin.H{"error": "Could not fetch organisation"})
59+
return
60+
}
61+
62+
type CreateVCSConnectionRequest struct {
63+
VCS string `json:"type" binding:"required"`
64+
Name string `json:"connection_name"`
65+
BitbucketAccessToken string `json:"bitbucket_access_token"`
66+
BitbucketWebhookSecret string `json:"bitbucket_webhook_secret"`
67+
}
68+
69+
var request CreateVCSConnectionRequest
70+
if err := c.BindJSON(&request); err != nil {
71+
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
72+
return
73+
}
74+
75+
if request.VCS != "bitbucket" {
76+
log.Printf("VCS type not supported: %v", request.VCS)
77+
c.JSON(http.StatusBadRequest, gin.H{"error": "VCS type not supported"})
78+
return
79+
}
80+
81+
secret := os.Getenv("DIGGER_ENCRYPTION_SECRET")
82+
if secret == "" {
83+
log.Printf("ERROR: no encryption secret specified")
84+
c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not encrypt access token"})
85+
return
86+
}
87+
88+
bitbucketAccessTokenEncrypted, err := utils.AESEncrypt([]byte(secret), request.BitbucketAccessToken)
89+
if err != nil {
90+
log.Printf("could not encrypt access token: %v", err)
91+
c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not encrypt access token"})
92+
return
93+
}
94+
95+
bitbucketWebhookSecretEncrypted, err := utils.AESEncrypt([]byte(secret), request.BitbucketWebhookSecret)
96+
if err != nil {
97+
log.Printf("could not encrypt webhook secret: %v", err)
98+
c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not encrypt webhook secret"})
99+
return
100+
}
101+
102+
connection, err := models.DB.CreateVCSConnection(
103+
request.Name,
104+
0,
105+
"",
106+
"",
107+
"",
108+
"",
109+
"",
110+
"",
111+
"",
112+
bitbucketAccessTokenEncrypted,
113+
bitbucketWebhookSecretEncrypted,
114+
org.ID,
115+
)
116+
if err != nil {
117+
log.Printf("")
118+
}
119+
120+
c.JSON(http.StatusCreated, gin.H{
121+
"connection": connection.ID,
122+
})
123+
}
124+
125+
func GetVCSConnection(c *gin.Context) {
126+
organisationId := c.GetString(middleware.ORGANISATION_ID_KEY)
127+
organisationSource := c.GetString(middleware.ORGANISATION_SOURCE_KEY)
128+
connectionId := c.Param("id")
129+
130+
var org models.Organisation
131+
err := models.DB.GormDB.Where("external_id = ? AND external_source = ?", organisationId, organisationSource).First(&org).Error
132+
if err != nil {
133+
if errors.Is(err, gorm.ErrRecordNotFound) {
134+
c.String(http.StatusNotFound, "Could not find organisation: "+organisationId)
135+
} else {
136+
log.Printf("could not fetch organisation: %v err: %v", organisationId, err)
137+
c.String(http.StatusNotFound, "Could not fetch organisation: "+organisationId)
138+
}
139+
return
140+
}
141+
142+
var connection models.VCSConnection
143+
err = models.DB.GormDB.Where("id = ? AND organisation_id = ?", connectionId, org.ID).First(&connection).Error
144+
if err != nil {
145+
if errors.Is(err, gorm.ErrRecordNotFound) {
146+
c.String(http.StatusNotFound, "Could not find connection: "+connectionId)
147+
} else {
148+
log.Printf("could not fetch connection: %v err: %v", connectionId, err)
149+
c.String(http.StatusInternalServerError, "Could not fetch connection")
150+
}
151+
return
152+
}
153+
154+
c.JSON(http.StatusOK, gin.H{
155+
"connection_name": connection.Name,
156+
"connection_id": connection.ID,
157+
})
158+
}
159+
160+
func DeleteVCSConnection(c *gin.Context) {
161+
organisationId := c.GetString(middleware.ORGANISATION_ID_KEY)
162+
organisationSource := c.GetString(middleware.ORGANISATION_SOURCE_KEY)
163+
connectionId := c.Param("id")
164+
165+
var org models.Organisation
166+
err := models.DB.GormDB.Where("external_id = ? AND external_source = ?", organisationId, organisationSource).First(&org).Error
167+
if err != nil {
168+
if errors.Is(err, gorm.ErrRecordNotFound) {
169+
c.String(http.StatusNotFound, "Could not find organisation: "+organisationId)
170+
} else {
171+
log.Printf("could not fetch organisation: %v err: %v", organisationId, err)
172+
c.String(http.StatusNotFound, "Could not fetch organisation: "+organisationId)
173+
}
174+
return
175+
}
176+
177+
var connection models.VCSConnection
178+
err = models.DB.GormDB.Where("id = ? AND organisation_id = ?", connectionId, org.ID).First(&connection).Error
179+
if err != nil {
180+
if errors.Is(err, gorm.ErrRecordNotFound) {
181+
c.String(http.StatusNotFound, "Could not find connection: "+connectionId)
182+
} else {
183+
log.Printf("could not fetch connection: %v err: %v", connectionId, err)
184+
c.String(http.StatusInternalServerError, "Could not fetch connection")
185+
}
186+
return
187+
}
188+
189+
err = models.DB.GormDB.Delete(&connection).Error
190+
if err != nil {
191+
log.Printf("could not delete connection: %v err: %v", connectionId, err)
192+
c.String(http.StatusInternalServerError, "Could not delete connection")
193+
return
194+
}
195+
196+
c.JSON(http.StatusOK, gin.H{
197+
"status": "success",
198+
})
199+
}

backend/controllers/github.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,7 @@ func handlePullRequestEvent(gh utils.GithubClientProvider, payload *github.PullR
522522
aiSummaryCommentId = aiSummaryComment.Id
523523
}
524524

525-
batchId, _, err := utils.ConvertJobsToDiggerJobs(*diggerCommand, models.DiggerVCSGithub, organisationId, impactedJobsMap, impactedProjectsMap, projectsGraph, installationId, branch, prNumber, repoOwner, repoName, repoFullName, commitSha, commentId, diggerYmlStr, 0, aiSummaryCommentId, config.ReportTerraformOutputs)
525+
batchId, _, err := utils.ConvertJobsToDiggerJobs(*diggerCommand, models.DiggerVCSGithub, organisationId, impactedJobsMap, impactedProjectsMap, projectsGraph, installationId, branch, prNumber, repoOwner, repoName, repoFullName, commitSha, commentId, diggerYmlStr, 0, aiSummaryCommentId, config.ReportTerraformOutputs, nil)
526526
if err != nil {
527527
log.Printf("ConvertJobsToDiggerJobs error: %v", err)
528528
commentReporterManager.UpdateComment(fmt.Sprintf(":x: ConvertJobsToDiggerJobs error: %v", err))
@@ -595,7 +595,7 @@ func GetDiggerConfigForBranch(gh utils.GithubClientProvider, installationId int6
595595
var diggerYmlStr string
596596
var dependencyGraph graph.Graph[string, dg_configuration.Project]
597597

598-
err = utils.CloneGitRepoAndDoAction(cloneUrl, branch, "", *token, func(dir string) error {
598+
err = utils.CloneGitRepoAndDoAction(cloneUrl, branch, "", *token, "", func(dir string) error {
599599
diggerYmlStr, err = dg_configuration.ReadDiggerYmlFileContents(dir)
600600
if err != nil {
601601
log.Printf("could not load digger config: %v", err)
@@ -930,7 +930,7 @@ func handleIssueCommentEvent(gh utils.GithubClientProvider, payload *github.Issu
930930
aiSummaryCommentId = aiSummaryComment.Id
931931
}
932932

933-
batchId, _, err := utils.ConvertJobsToDiggerJobs(*diggerCommand, "github", orgId, impactedProjectsJobMap, impactedProjectsMap, projectsGraph, installationId, *branch, issueNumber, repoOwner, repoName, repoFullName, *commitSha, reporterCommentId, diggerYmlStr, 0, aiSummaryCommentId, config.ReportTerraformOutputs)
933+
batchId, _, err := utils.ConvertJobsToDiggerJobs(*diggerCommand, "github", orgId, impactedProjectsJobMap, impactedProjectsMap, projectsGraph, installationId, *branch, issueNumber, repoOwner, repoName, repoFullName, *commitSha, reporterCommentId, diggerYmlStr, 0, aiSummaryCommentId, config.ReportTerraformOutputs, nil)
934934
if err != nil {
935935
log.Printf("ConvertJobsToDiggerJobs error: %v", err)
936936
commentReporterManager.UpdateComment(fmt.Sprintf(":x: ConvertJobsToDiggerJobs error: %v", err))

backend/controllers/github_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@ func setupSuite(tb testing.TB) (func(tb testing.TB), *models.Database) {
589589

590590
// migrate tables
591591
err = gdb.AutoMigrate(&models.Policy{}, &models.Organisation{}, &models.Repo{}, &models.Project{}, &models.Token{},
592-
&models.User{}, &models.ProjectRun{}, &models.GithubAppInstallation{}, &models.GithubAppConnection{}, &models.GithubAppInstallationLink{},
592+
&models.User{}, &models.ProjectRun{}, &models.GithubAppInstallation{}, &models.VCSConnection{}, &models.GithubAppInstallationLink{},
593593
&models.GithubDiggerJobLink{}, &models.DiggerJob{}, &models.DiggerJobParentLink{}, &models.JobToken{})
594594
if err != nil {
595595
log.Fatal(err)
@@ -731,7 +731,7 @@ func TestJobsTreeWithOneJobsAndTwoProjects(t *testing.T) {
731731
graph, err := configuration.CreateProjectDependencyGraph(projects)
732732
assert.NoError(t, err)
733733

734-
_, result, err := utils.ConvertJobsToDiggerJobs("", "github", 1, jobs, projectMap, graph, 41584295, "", 2, "diggerhq", "parallel_jobs_demo", "diggerhq/parallel_jobs_demo", "", 123, "test", 0, "", false)
734+
_, result, err := utils.ConvertJobsToDiggerJobs("", "github", 1, jobs, projectMap, graph, 41584295, "", 2, "diggerhq", "parallel_jobs_demo", "diggerhq/parallel_jobs_demo", "", 123, "test", 0, "", false, nil)
735735
assert.NoError(t, err)
736736
assert.Equal(t, 1, len(result))
737737
parentLinks, err := models.DB.GetDiggerJobParentLinksChildId(&result["dev"].DiggerJobID)
@@ -760,7 +760,7 @@ func TestJobsTreeWithTwoDependantJobs(t *testing.T) {
760760
projectMap["dev"] = project1
761761
projectMap["prod"] = project2
762762

763-
_, result, err := utils.ConvertJobsToDiggerJobs("", "github", 1, jobs, projectMap, graph, 123, "", 2, "", "", "test", "", 123, "test", 0, "", false)
763+
_, result, err := utils.ConvertJobsToDiggerJobs("", "github", 1, jobs, projectMap, graph, 123, "", 2, "", "", "test", "", 123, "test", 0, "", false, nil)
764764
assert.NoError(t, err)
765765
assert.Equal(t, 2, len(result))
766766

@@ -793,7 +793,7 @@ func TestJobsTreeWithTwoIndependentJobs(t *testing.T) {
793793
projectMap["dev"] = project1
794794
projectMap["prod"] = project2
795795

796-
_, result, err := utils.ConvertJobsToDiggerJobs("", "github", 1, jobs, projectMap, graph, 123, "", 2, "", "", "test", "", 123, "test", 0, "", false)
796+
_, result, err := utils.ConvertJobsToDiggerJobs("", "github", 1, jobs, projectMap, graph, 123, "", 2, "", "", "test", "", 123, "test", 0, "", false, nil)
797797
assert.NoError(t, err)
798798
assert.Equal(t, 2, len(result))
799799
parentLinks, err := models.DB.GetDiggerJobParentLinksChildId(&result["dev"].DiggerJobID)
@@ -838,7 +838,7 @@ func TestJobsTreeWithThreeLevels(t *testing.T) {
838838
projectMap["555"] = project5
839839
projectMap["666"] = project6
840840

841-
_, result, err := utils.ConvertJobsToDiggerJobs("", "github", 1, jobs, projectMap, graph, 123, "", 2, "", "", "test", "", 123, "test", 0, "", false)
841+
_, result, err := utils.ConvertJobsToDiggerJobs("", "github", 1, jobs, projectMap, graph, 123, "", 2, "", "", "test", "", 123, "test", 0, "", false, nil)
842842
assert.NoError(t, err)
843843
assert.Equal(t, 6, len(result))
844844
parentLinks, err := models.DB.GetDiggerJobParentLinksChildId(&result["111"].DiggerJobID)

backend/go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ replace github.com/ugorji/go => github.com/ugorji/go v1.2.12
1010
require (
1111
ariga.io/atlas-provider-gorm v0.5.0
1212
github.com/bradleyfalzon/ghinstallation/v2 v2.11.0
13+
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
1314
github.com/dchest/uniuri v1.2.0
1415
github.com/diggerhq/digger/libs v0.4.15
1516
github.com/dominikbraun/graph v0.23.0
@@ -20,6 +21,7 @@ require (
2021
github.com/golang-jwt/jwt v3.2.2+incompatible
2122
github.com/google/go-github/v61 v61.0.0
2223
github.com/google/uuid v1.6.0
24+
github.com/ktrysmt/go-bitbucket v0.9.81
2325
github.com/migueleliasweb/go-github-mock v0.0.23
2426
github.com/robfig/cron v1.2.0
2527
github.com/samber/lo v1.39.0
@@ -29,7 +31,6 @@ require (
2931
github.com/stretchr/testify v1.9.0
3032
github.com/xanzy/go-gitlab v0.106.0
3133
golang.org/x/oauth2 v0.24.0
32-
gorm.io/datatypes v1.2.4
3334
gorm.io/driver/postgres v1.5.7
3435
gorm.io/driver/sqlite v1.5.5
3536
gorm.io/gorm v1.25.11
@@ -119,7 +120,6 @@ require (
119120
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect
120121
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
121122
github.com/creack/pty v1.1.17 // indirect
122-
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
123123
github.com/dimchansky/utfbom v1.1.1 // indirect
124124
github.com/dineshba/tf-summarize v0.3.10 // indirect
125125
github.com/envoyproxy/go-control-plane v0.13.0 // indirect

0 commit comments

Comments
 (0)