Skip to content

Commit b5a90e6

Browse files
committed
Move forge backends into subpackages
Each forge (github, gitlab, gitea, bitbucket) now lives in its own subpackage with an exported New constructor. The root package keeps only interfaces, types, and shared helpers. Default forge registration moves to internal/resolve to avoid import cycles.
1 parent 5a220d5 commit b5a90e6

79 files changed

Lines changed: 1471 additions & 1245 deletions

Some content is hidden

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

bitbucket.go renamed to bitbucket/bitbucket.go

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
package forges
1+
package bitbucket
22

33
import (
44
"bytes"
55
"context"
66
"encoding/json"
77
"errors"
88
"fmt"
9+
forge "github.com/git-pkgs/forge"
910
"io"
1011
"net/http"
1112
"time"
@@ -21,7 +22,8 @@ type bitbucketForge struct {
2122
httpClient *http.Client
2223
}
2324

24-
func newBitbucketForge(token string, hc *http.Client) *bitbucketForge {
25+
// New creates a Bitbucket forge backend.
26+
func New(token string, hc *http.Client) forge.Forge {
2527
if hc == nil {
2628
hc = http.DefaultClient
2729
}
@@ -33,7 +35,7 @@ type bitbucketRepoService struct {
3335
httpClient *http.Client
3436
}
3537

36-
func (f *bitbucketForge) Repos() RepoService {
38+
func (f *bitbucketForge) Repos() forge.RepoService {
3739
return &bitbucketRepoService{token: f.token, httpClient: f.httpClient}
3840
}
3941

@@ -121,14 +123,14 @@ func (s *bitbucketRepoService) doJSON(ctx context.Context, method, url string, b
121123
defer func() { _ = resp.Body.Close() }()
122124

123125
if resp.StatusCode == http.StatusNotFound {
124-
return ErrNotFound
126+
return forge.ErrNotFound
125127
}
126128
if resp.StatusCode == http.StatusNoContent {
127129
return nil
128130
}
129131
if resp.StatusCode >= 400 {
130132
respBody, _ := io.ReadAll(resp.Body)
131-
return &HTTPError{StatusCode: resp.StatusCode, URL: url, Body: string(respBody)}
133+
return &forge.HTTPError{StatusCode: resp.StatusCode, URL: url, Body: string(respBody)}
132134
}
133135

134136
if v != nil {
@@ -141,8 +143,8 @@ func (s *bitbucketRepoService) getJSON(ctx context.Context, url string, v any) e
141143
return s.doJSON(ctx, http.MethodGet, url, nil, v)
142144
}
143145

144-
func convertBitbucketRepo(bb bbRepository) Repository {
145-
result := Repository{
146+
func convertBitbucketRepo(bb bbRepository) forge.Repository {
147+
result := forge.Repository{
146148
FullName: bb.FullName,
147149
Name: bb.Slug,
148150
Description: bb.Description,
@@ -187,7 +189,7 @@ func convertBitbucketRepo(bb bbRepository) Repository {
187189
return result
188190
}
189191

190-
func (s *bitbucketRepoService) Get(ctx context.Context, owner, repo string) (*Repository, error) {
192+
func (s *bitbucketRepoService) Get(ctx context.Context, owner, repo string) (*forge.Repository, error) {
191193
url := fmt.Sprintf("%s/repositories/%s/%s", bitbucketAPI, owner, repo)
192194
var bb bbRepository
193195
if err := s.getJSON(ctx, url, &bb); err != nil {
@@ -198,20 +200,20 @@ func (s *bitbucketRepoService) Get(ctx context.Context, owner, repo string) (*Re
198200
return &result, nil
199201
}
200202

201-
func (s *bitbucketRepoService) List(ctx context.Context, owner string, opts ListRepoOpts) ([]Repository, error) {
203+
func (s *bitbucketRepoService) List(ctx context.Context, owner string, opts forge.ListRepoOpts) ([]forge.Repository, error) {
202204
perPage := opts.PerPage
203205
if perPage <= 0 {
204206
perPage = 100
205207
}
206208

207-
var all []Repository
209+
var all []forge.Repository
208210
url := fmt.Sprintf("%s/repositories/%s?pagelen=%d", bitbucketAPI, owner, perPage)
209211

210212
for url != "" {
211213
var page bbReposResponse
212214
if err := s.getJSON(ctx, url, &page); err != nil {
213-
if errors.Is(err, ErrNotFound) {
214-
return nil, ErrOwnerNotFound
215+
if errors.Is(err, forge.ErrNotFound) {
216+
return nil, forge.ErrOwnerNotFound
215217
}
216218
return nil, err
217219
}
@@ -221,10 +223,10 @@ func (s *bitbucketRepoService) List(ctx context.Context, owner string, opts List
221223
url = page.Next
222224
}
223225

224-
return FilterRepos(all, opts), nil
226+
return forge.FilterRepos(all, opts), nil
225227
}
226228

227-
func (s *bitbucketRepoService) Create(ctx context.Context, opts CreateRepoOpts) (*Repository, error) {
229+
func (s *bitbucketRepoService) Create(ctx context.Context, opts forge.CreateRepoOpts) (*forge.Repository, error) {
228230
owner := opts.Owner
229231
if owner == "" {
230232
return nil, fmt.Errorf("bitbucket: owner is required for repo creation")
@@ -236,7 +238,7 @@ func (s *bitbucketRepoService) Create(ctx context.Context, opts CreateRepoOpts)
236238
if opts.Description != "" {
237239
body["description"] = opts.Description
238240
}
239-
if opts.Visibility == VisibilityPrivate {
241+
if opts.Visibility == forge.VisibilityPrivate {
240242
body["is_private"] = true
241243
} else {
242244
body["is_private"] = false
@@ -251,7 +253,7 @@ func (s *bitbucketRepoService) Create(ctx context.Context, opts CreateRepoOpts)
251253
return &result, nil
252254
}
253255

254-
func (s *bitbucketRepoService) Edit(ctx context.Context, owner, repo string, opts EditRepoOpts) (*Repository, error) {
256+
func (s *bitbucketRepoService) Edit(ctx context.Context, owner, repo string, opts forge.EditRepoOpts) (*forge.Repository, error) {
255257
body := map[string]any{}
256258

257259
if opts.Description != nil {
@@ -265,9 +267,9 @@ func (s *bitbucketRepoService) Edit(ctx context.Context, owner, repo string, opt
265267
}
266268

267269
switch opts.Visibility {
268-
case VisibilityPrivate:
270+
case forge.VisibilityPrivate:
269271
body["is_private"] = true
270-
case VisibilityPublic:
272+
case forge.VisibilityPublic:
271273
body["is_private"] = false
272274
}
273275

@@ -290,7 +292,7 @@ func (s *bitbucketRepoService) Delete(ctx context.Context, owner, repo string) e
290292
return s.doJSON(ctx, http.MethodDelete, url, nil, nil)
291293
}
292294

293-
func (s *bitbucketRepoService) Fork(ctx context.Context, owner, repo string, opts ForkRepoOpts) (*Repository, error) {
295+
func (s *bitbucketRepoService) Fork(ctx context.Context, owner, repo string, opts forge.ForkRepoOpts) (*forge.Repository, error) {
294296
body := map[string]any{}
295297
if opts.Name != "" {
296298
body["name"] = opts.Name
@@ -309,8 +311,8 @@ func (s *bitbucketRepoService) Fork(ctx context.Context, owner, repo string, opt
309311
return &result, nil
310312
}
311313

312-
func (s *bitbucketRepoService) ListTags(ctx context.Context, owner, repo string) ([]Tag, error) {
313-
var allTags []Tag
314+
func (s *bitbucketRepoService) ListTags(ctx context.Context, owner, repo string) ([]forge.Tag, error) {
315+
var allTags []forge.Tag
314316
url := fmt.Sprintf("%s/repositories/%s/%s/refs/tags?pagelen=100", bitbucketAPI, owner, repo)
315317

316318
for url != "" {
@@ -319,7 +321,7 @@ func (s *bitbucketRepoService) ListTags(ctx context.Context, owner, repo string)
319321
return nil, err
320322
}
321323
for _, t := range page.Values {
322-
allTags = append(allTags, Tag{
324+
allTags = append(allTags, forge.Tag{
323325
Name: t.Name,
324326
Commit: t.Target.Hash,
325327
})
@@ -329,8 +331,8 @@ func (s *bitbucketRepoService) ListTags(ctx context.Context, owner, repo string)
329331
return allTags, nil
330332
}
331333

332-
func (s *bitbucketRepoService) Search(ctx context.Context, opts SearchRepoOpts) ([]Repository, error) {
334+
func (s *bitbucketRepoService) Search(ctx context.Context, opts forge.SearchRepoOpts) ([]forge.Repository, error) {
333335
// Bitbucket doesn't have a global repo search API.
334336
// The closest is searching within a workspace, which requires an owner.
335-
return nil, ErrNotSupported
337+
return nil, forge.ErrNotSupported
336338
}
Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
package forges
1+
package bitbucket
22

33
import (
44
"context"
55
"encoding/json"
6+
forge "github.com/git-pkgs/forge"
67
"net/http"
78
"net/http/httptest"
89
"testing"
@@ -65,7 +66,7 @@ func TestBitbucketGetRepo(t *testing.T) {
6566
defer func() { setBitbucketAPI(origAPI) }()
6667
setBitbucketAPI(srv.URL + "/2.0")
6768

68-
f := newBitbucketForge("test-bb-token", nil)
69+
f := New("test-bb-token", nil)
6970

7071
repo, err := f.Repos().Get(context.Background(), "atlassian", "stash-example-plugin")
7172
if err != nil {
@@ -101,11 +102,11 @@ func TestBitbucketGetRepoNotFound(t *testing.T) {
101102
defer func() { setBitbucketAPI(origAPI) }()
102103
setBitbucketAPI(srv.URL + "/2.0")
103104

104-
f := newBitbucketForge("", nil)
105+
f := New("", nil)
105106

106107
_, err := f.Repos().Get(context.Background(), "atlassian", "nonexistent")
107-
if err != ErrNotFound {
108-
t.Fatalf("expected ErrNotFound, got %v", err)
108+
if err != forge.ErrNotFound {
109+
t.Fatalf("expected forge.ErrNotFound, got %v", err)
109110
}
110111
}
111112

@@ -143,9 +144,9 @@ func TestBitbucketListRepos(t *testing.T) {
143144
defer func() { setBitbucketAPI(origAPI) }()
144145
setBitbucketAPI(srv.URL + "/2.0")
145146

146-
f := newBitbucketForge("test-token", nil)
147+
f := New("test-token", nil)
147148

148-
repos, err := f.Repos().List(context.Background(), "atlassian", ListRepoOpts{})
149+
repos, err := f.Repos().List(context.Background(), "atlassian", forge.ListRepoOpts{})
149150
if err != nil {
150151
t.Fatalf("unexpected error: %v", err)
151152
}
@@ -169,11 +170,11 @@ func TestBitbucketListReposNotFound(t *testing.T) {
169170
defer func() { setBitbucketAPI(origAPI) }()
170171
setBitbucketAPI(srv.URL + "/2.0")
171172

172-
f := newBitbucketForge("", nil)
173+
f := New("", nil)
173174

174-
_, err := f.Repos().List(context.Background(), "nonexistent", ListRepoOpts{})
175-
if err != ErrOwnerNotFound {
176-
t.Fatalf("expected ErrOwnerNotFound, got %v", err)
175+
_, err := f.Repos().List(context.Background(), "nonexistent", forge.ListRepoOpts{})
176+
if err != forge.ErrOwnerNotFound {
177+
t.Fatalf("expected forge.ErrOwnerNotFound, got %v", err)
177178
}
178179
}
179180

@@ -199,7 +200,7 @@ func TestBitbucketListTags(t *testing.T) {
199200
defer func() { setBitbucketAPI(origAPI) }()
200201
setBitbucketAPI(srv.URL + "/2.0")
201202

202-
f := newBitbucketForge("", nil)
203+
f := New("", nil)
203204

204205
tags, err := f.Repos().ListTags(context.Background(), "atlassian", "myrepo")
205206
if err != nil {

bitbucket/branches.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package bitbucket
2+
3+
import (
4+
"context"
5+
6+
forge "github.com/git-pkgs/forge"
7+
)
8+
9+
type bitbucketBranchService struct{}
10+
11+
func (f *bitbucketForge) Branches() forge.BranchService {
12+
return &bitbucketBranchService{}
13+
}
14+
15+
func (s *bitbucketBranchService) List(_ context.Context, _, _ string, _ forge.ListBranchOpts) ([]forge.Branch, error) {
16+
return nil, forge.ErrNotSupported
17+
}
18+
19+
func (s *bitbucketBranchService) Create(_ context.Context, _, _, _, _ string) (*forge.Branch, error) {
20+
return nil, forge.ErrNotSupported
21+
}
22+
23+
func (s *bitbucketBranchService) Delete(_ context.Context, _, _, _ string) error {
24+
return forge.ErrNotSupported
25+
}

bitbucket/branches_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package bitbucket
2+
3+
import (
4+
"context"
5+
"errors"
6+
forge "github.com/git-pkgs/forge"
7+
"testing"
8+
)
9+
10+
func TestBitbucketBranchNotSupported(t *testing.T) {
11+
svc := &bitbucketBranchService{}
12+
13+
_, err := svc.List(context.Background(), "owner", "repo", forge.ListBranchOpts{})
14+
if !errors.Is(err, forge.ErrNotSupported) {
15+
t.Errorf("List: expected forge.ErrNotSupported, got %v", err)
16+
}
17+
18+
_, err = svc.Create(context.Background(), "owner", "repo", "new", "main")
19+
if !errors.Is(err, forge.ErrNotSupported) {
20+
t.Errorf("Create: expected forge.ErrNotSupported, got %v", err)
21+
}
22+
23+
err = svc.Delete(context.Background(), "owner", "repo", "old")
24+
if !errors.Is(err, forge.ErrNotSupported) {
25+
t.Errorf("Delete: expected forge.ErrNotSupported, got %v", err)
26+
}
27+
}
Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,37 @@
1-
package forges
1+
package bitbucket
22

33
import (
44
"context"
5+
forge "github.com/git-pkgs/forge"
56
"io"
67
)
78

89
type bitbucketCIService struct{}
910

10-
func (f *bitbucketForge) CI() CIService {
11+
func (f *bitbucketForge) CI() forge.CIService {
1112
return &bitbucketCIService{}
1213
}
1314

14-
func (s *bitbucketCIService) ListRuns(_ context.Context, _, _ string, _ ListCIRunOpts) ([]CIRun, error) {
15-
return nil, ErrNotSupported
15+
func (s *bitbucketCIService) ListRuns(_ context.Context, _, _ string, _ forge.ListCIRunOpts) ([]forge.CIRun, error) {
16+
return nil, forge.ErrNotSupported
1617
}
1718

18-
func (s *bitbucketCIService) GetRun(_ context.Context, _, _ string, _ int64) (*CIRun, error) {
19-
return nil, ErrNotSupported
19+
func (s *bitbucketCIService) GetRun(_ context.Context, _, _ string, _ int64) (*forge.CIRun, error) {
20+
return nil, forge.ErrNotSupported
2021
}
2122

22-
func (s *bitbucketCIService) TriggerRun(_ context.Context, _, _ string, _ TriggerCIRunOpts) error {
23-
return ErrNotSupported
23+
func (s *bitbucketCIService) TriggerRun(_ context.Context, _, _ string, _ forge.TriggerCIRunOpts) error {
24+
return forge.ErrNotSupported
2425
}
2526

2627
func (s *bitbucketCIService) CancelRun(_ context.Context, _, _ string, _ int64) error {
27-
return ErrNotSupported
28+
return forge.ErrNotSupported
2829
}
2930

3031
func (s *bitbucketCIService) RetryRun(_ context.Context, _, _ string, _ int64) error {
31-
return ErrNotSupported
32+
return forge.ErrNotSupported
3233
}
3334

3435
func (s *bitbucketCIService) GetJobLog(_ context.Context, _, _ string, _ int64) (io.ReadCloser, error) {
35-
return nil, ErrNotSupported
36+
return nil, forge.ErrNotSupported
3637
}

bitbucket/ci_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package bitbucket
2+
3+
import (
4+
"context"
5+
"errors"
6+
forge "github.com/git-pkgs/forge"
7+
"testing"
8+
)
9+
10+
func TestBitbucketCINotSupported(t *testing.T) {
11+
svc := &bitbucketCIService{}
12+
13+
_, err := svc.ListRuns(context.Background(), "owner", "repo", forge.ListCIRunOpts{})
14+
if !errors.Is(err, forge.ErrNotSupported) {
15+
t.Errorf("ListRuns: expected forge.ErrNotSupported, got %v", err)
16+
}
17+
18+
_, err = svc.GetRun(context.Background(), "owner", "repo", 1)
19+
if !errors.Is(err, forge.ErrNotSupported) {
20+
t.Errorf("GetRun: expected forge.ErrNotSupported, got %v", err)
21+
}
22+
23+
err = svc.TriggerRun(context.Background(), "owner", "repo", forge.TriggerCIRunOpts{})
24+
if !errors.Is(err, forge.ErrNotSupported) {
25+
t.Errorf("TriggerRun: expected forge.ErrNotSupported, got %v", err)
26+
}
27+
28+
err = svc.CancelRun(context.Background(), "owner", "repo", 1)
29+
if !errors.Is(err, forge.ErrNotSupported) {
30+
t.Errorf("CancelRun: expected forge.ErrNotSupported, got %v", err)
31+
}
32+
33+
err = svc.RetryRun(context.Background(), "owner", "repo", 1)
34+
if !errors.Is(err, forge.ErrNotSupported) {
35+
t.Errorf("RetryRun: expected forge.ErrNotSupported, got %v", err)
36+
}
37+
38+
_, err = svc.GetJobLog(context.Background(), "owner", "repo", 1)
39+
if !errors.Is(err, forge.ErrNotSupported) {
40+
t.Errorf("GetJobLog: expected forge.ErrNotSupported, got %v", err)
41+
}
42+
}

0 commit comments

Comments
 (0)