Skip to content

Commit b0691a4

Browse files
authored
Merge pull request #40 from git-pkgs/add-commit-statuses
Add commit status support
2 parents 8c5c178 + 9c55540 commit b0691a4

13 files changed

Lines changed: 740 additions & 0 deletions

bitbucket/commit_statuses.go

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

bitbucket/commit_statuses_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package bitbucket
2+
3+
import (
4+
"context"
5+
forge "github.com/git-pkgs/forge"
6+
"testing"
7+
)
8+
9+
func TestBitbucketListCommitStatusesNotSupported(t *testing.T) {
10+
s := New("", nil).CommitStatuses()
11+
_, err := s.List(context.Background(), "owner", "repo", "abc123")
12+
if err != forge.ErrNotSupported {
13+
t.Errorf("List: expected forge.ErrNotSupported, got %v", err)
14+
}
15+
}
16+
17+
func TestBitbucketSetCommitStatusNotSupported(t *testing.T) {
18+
s := New("", nil).CommitStatuses()
19+
_, err := s.Set(context.Background(), "owner", "repo", "abc123", forge.SetCommitStatusOpts{})
20+
if err != forge.ErrNotSupported {
21+
t.Errorf("Set: expected forge.ErrNotSupported, got %v", err)
22+
}
23+
}

forge.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ type Forge interface {
4747
Reviews() ReviewService
4848
Files() FileService
4949
Collaborators() CollaboratorService
50+
CommitStatuses() CommitStatusService
5051
GetRateLimit(ctx context.Context) (*RateLimit, error)
5152
}
5253

forges_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,10 @@ func (m *mockForge) Collaborators() CollaboratorService {
485485
return &mockCollaboratorService{}
486486
}
487487

488+
func (m *mockForge) CommitStatuses() CommitStatusService {
489+
return &mockCommitStatusService{}
490+
}
491+
488492
func (m *mockForge) GetRateLimit(_ context.Context) (*RateLimit, error) {
489493
return nil, ErrNotSupported
490494
}
@@ -513,6 +517,16 @@ func (m *mockCollaboratorService) Remove(_ context.Context, _, _, _ string) erro
513517
return nil
514518
}
515519

520+
type mockCommitStatusService struct{}
521+
522+
func (m *mockCommitStatusService) List(_ context.Context, _, _, _ string) ([]CommitStatus, error) {
523+
return nil, nil
524+
}
525+
526+
func (m *mockCommitStatusService) Set(_ context.Context, _, _, _ string, _ SetCommitStatusOpts) (*CommitStatus, error) {
527+
return nil, nil
528+
}
529+
516530
type mockRepoService struct {
517531
repo *Repository
518532
repos []Repository

gitea/commit_statuses.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package gitea
2+
3+
import (
4+
"context"
5+
forge "github.com/git-pkgs/forge"
6+
"net/http"
7+
8+
"code.gitea.io/sdk/gitea"
9+
)
10+
11+
type giteaCommitStatusService struct {
12+
client *gitea.Client
13+
}
14+
15+
func (f *giteaForge) CommitStatuses() forge.CommitStatusService {
16+
return &giteaCommitStatusService{client: f.client}
17+
}
18+
19+
func (s *giteaCommitStatusService) List(ctx context.Context, owner, repo, sha string) ([]forge.CommitStatus, error) {
20+
var all []forge.CommitStatus
21+
page := 1
22+
for {
23+
statuses, resp, err := s.client.ListStatuses(owner, repo, sha, gitea.ListStatusesOption{
24+
ListOptions: gitea.ListOptions{Page: page, PageSize: 50},
25+
})
26+
if err != nil {
27+
if resp != nil && resp.StatusCode == http.StatusNotFound {
28+
return nil, forge.ErrNotFound
29+
}
30+
return nil, err
31+
}
32+
for _, st := range statuses {
33+
cs := forge.CommitStatus{
34+
State: string(st.State),
35+
Context: st.Context,
36+
Description: st.Description,
37+
TargetURL: st.TargetURL,
38+
CreatedAt: st.Created,
39+
}
40+
if st.Creator != nil {
41+
cs.Creator = st.Creator.UserName
42+
}
43+
all = append(all, cs)
44+
}
45+
if len(statuses) < 50 {
46+
break
47+
}
48+
page++
49+
}
50+
return all, nil
51+
}
52+
53+
func (s *giteaCommitStatusService) Set(ctx context.Context, owner, repo, sha string, opts forge.SetCommitStatusOpts) (*forge.CommitStatus, error) {
54+
result, resp, err := s.client.CreateStatus(owner, repo, sha, gitea.CreateStatusOption{
55+
State: gitea.StatusState(opts.State),
56+
TargetURL: opts.TargetURL,
57+
Description: opts.Description,
58+
Context: opts.Context,
59+
})
60+
if err != nil {
61+
if resp != nil && resp.StatusCode == http.StatusNotFound {
62+
return nil, forge.ErrNotFound
63+
}
64+
return nil, err
65+
}
66+
67+
cs := &forge.CommitStatus{
68+
State: string(result.State),
69+
Context: result.Context,
70+
Description: result.Description,
71+
TargetURL: result.TargetURL,
72+
CreatedAt: result.Created,
73+
}
74+
if result.Creator != nil {
75+
cs.Creator = result.Creator.UserName
76+
}
77+
return cs, nil
78+
}

gitea/commit_statuses_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package gitea
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
forge "github.com/git-pkgs/forge"
7+
"net/http"
8+
"net/http/httptest"
9+
"testing"
10+
)
11+
12+
func TestGiteaListCommitStatuses(t *testing.T) {
13+
mux := http.NewServeMux()
14+
mux.HandleFunc("GET /api/v1/version", giteaVersionHandler)
15+
mux.HandleFunc("GET /api/v1/repos/testorg/testrepo/commits/abc123/statuses", func(w http.ResponseWriter, r *http.Request) {
16+
_ = json.NewEncoder(w).Encode([]map[string]any{
17+
{
18+
"id": 1,
19+
"status": "success",
20+
"context": "ci/tests",
21+
"description": "All tests passed",
22+
"target_url": "https://ci.example.com/1",
23+
"creator": map[string]any{"login": "bot"},
24+
},
25+
})
26+
})
27+
28+
srv := httptest.NewServer(mux)
29+
defer srv.Close()
30+
31+
f := New(srv.URL, "test-token", nil)
32+
statuses, err := f.CommitStatuses().List(context.Background(), "testorg", "testrepo", "abc123")
33+
if err != nil {
34+
t.Fatalf("unexpected error: %v", err)
35+
}
36+
if len(statuses) != 1 {
37+
t.Fatalf("expected 1 status, got %d", len(statuses))
38+
}
39+
assertEqual(t, "statuses[0].State", "success", statuses[0].State)
40+
assertEqual(t, "statuses[0].Context", "ci/tests", statuses[0].Context)
41+
assertEqual(t, "statuses[0].Creator", "bot", statuses[0].Creator)
42+
}
43+
44+
func TestGiteaSetCommitStatus(t *testing.T) {
45+
mux := http.NewServeMux()
46+
mux.HandleFunc("GET /api/v1/version", giteaVersionHandler)
47+
mux.HandleFunc("POST /api/v1/repos/testorg/testrepo/statuses/abc123", func(w http.ResponseWriter, r *http.Request) {
48+
w.WriteHeader(http.StatusCreated)
49+
_ = json.NewEncoder(w).Encode(map[string]any{
50+
"id": 1,
51+
"status": "success",
52+
"context": "my-check",
53+
"description": "Everything passed",
54+
"target_url": "https://example.com",
55+
"creator": map[string]any{"login": "bot"},
56+
})
57+
})
58+
59+
srv := httptest.NewServer(mux)
60+
defer srv.Close()
61+
62+
f := New(srv.URL, "test-token", nil)
63+
status, err := f.CommitStatuses().Set(context.Background(), "testorg", "testrepo", "abc123", forge.SetCommitStatusOpts{
64+
State: "success",
65+
Context: "my-check",
66+
Description: "Everything passed",
67+
TargetURL: "https://example.com",
68+
})
69+
if err != nil {
70+
t.Fatalf("unexpected error: %v", err)
71+
}
72+
assertEqual(t, "State", "success", status.State)
73+
assertEqual(t, "Context", "my-check", status.Context)
74+
}
75+
76+
func TestGiteaListCommitStatusesNotFound(t *testing.T) {
77+
mux := http.NewServeMux()
78+
mux.HandleFunc("GET /api/v1/version", giteaVersionHandler)
79+
mux.HandleFunc("GET /api/v1/repos/testorg/nope/commits/abc123/statuses", func(w http.ResponseWriter, r *http.Request) {
80+
w.WriteHeader(http.StatusNotFound)
81+
})
82+
83+
srv := httptest.NewServer(mux)
84+
defer srv.Close()
85+
86+
f := New(srv.URL, "test-token", nil)
87+
_, err := f.CommitStatuses().List(context.Background(), "testorg", "nope", "abc123")
88+
if err != forge.ErrNotFound {
89+
t.Fatalf("expected forge.ErrNotFound, got %v", err)
90+
}
91+
}

github/commit_statuses.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package github
2+
3+
import (
4+
"context"
5+
forge "github.com/git-pkgs/forge"
6+
"net/http"
7+
8+
"github.com/google/go-github/v82/github"
9+
)
10+
11+
type gitHubCommitStatusService struct {
12+
client *github.Client
13+
}
14+
15+
func (f *gitHubForge) CommitStatuses() forge.CommitStatusService {
16+
return &gitHubCommitStatusService{client: f.client}
17+
}
18+
19+
func (s *gitHubCommitStatusService) List(ctx context.Context, owner, repo, sha string) ([]forge.CommitStatus, error) {
20+
var all []forge.CommitStatus
21+
opts := &github.ListOptions{PerPage: 100}
22+
for {
23+
statuses, resp, err := s.client.Repositories.ListStatuses(ctx, owner, repo, sha, opts)
24+
if err != nil {
25+
if resp != nil && resp.StatusCode == http.StatusNotFound {
26+
return nil, forge.ErrNotFound
27+
}
28+
return nil, err
29+
}
30+
for _, st := range statuses {
31+
cs := forge.CommitStatus{
32+
State: st.GetState(),
33+
Context: st.GetContext(),
34+
Description: st.GetDescription(),
35+
TargetURL: st.GetTargetURL(),
36+
}
37+
if st.Creator != nil {
38+
cs.Creator = st.Creator.GetLogin()
39+
}
40+
if st.CreatedAt != nil {
41+
cs.CreatedAt = st.CreatedAt.Time
42+
}
43+
all = append(all, cs)
44+
}
45+
if resp.NextPage == 0 {
46+
break
47+
}
48+
opts.Page = resp.NextPage
49+
}
50+
return all, nil
51+
}
52+
53+
func (s *gitHubCommitStatusService) Set(ctx context.Context, owner, repo, sha string, opts forge.SetCommitStatusOpts) (*forge.CommitStatus, error) {
54+
status := github.RepoStatus{
55+
State: &opts.State,
56+
Context: &opts.Context,
57+
Description: &opts.Description,
58+
TargetURL: &opts.TargetURL,
59+
}
60+
61+
result, resp, err := s.client.Repositories.CreateStatus(ctx, owner, repo, sha, status)
62+
if err != nil {
63+
if resp != nil && resp.StatusCode == http.StatusNotFound {
64+
return nil, forge.ErrNotFound
65+
}
66+
return nil, err
67+
}
68+
69+
cs := &forge.CommitStatus{
70+
State: result.GetState(),
71+
Context: result.GetContext(),
72+
Description: result.GetDescription(),
73+
TargetURL: result.GetTargetURL(),
74+
}
75+
if result.Creator != nil {
76+
cs.Creator = result.Creator.GetLogin()
77+
}
78+
if result.CreatedAt != nil {
79+
cs.CreatedAt = result.CreatedAt.Time
80+
}
81+
return cs, nil
82+
}

0 commit comments

Comments
 (0)