diff --git a/docs/api-contract.md b/docs/api-contract.md index dce3f9a..6b04c5e 100644 --- a/docs/api-contract.md +++ b/docs/api-contract.md @@ -190,6 +190,7 @@ Coverage notes: rows list direct tests when a symbol is named in test names or r | method | ReleaseService.DeleteByTag | `func (s *ReleaseService) DeleteByTag(ctx context.Context, owner, repo, tag string) error` | DeleteByTag deletes a release by its tag name. | No direct tests. | | method | ReleaseService.GetAsset | `func (s *ReleaseService) GetAsset(ctx context.Context, owner, repo string, releaseID, assetID int64) (*types.Attachment, error)` | GetAsset returns a single asset for a release. | No direct tests. | | method | ReleaseService.GetByTag | `func (s *ReleaseService) GetByTag(ctx context.Context, owner, repo, tag string) (*types.Release, error)` | GetByTag returns a release by its tag name. | `TestReleaseService_Good_GetByTag` | +| method | ReleaseService.GetLatest | `func (s *ReleaseService) GetLatest(ctx context.Context, owner, repo string) (*types.Release, error)` | GetLatest returns the most recent non-prerelease, non-draft release. | `TestReleaseService_GetLatest_Good` | | method | ReleaseService.IterAssets | `func (s *ReleaseService) IterAssets(ctx context.Context, owner, repo string, releaseID int64) iter.Seq2[types.Attachment, error]` | IterAssets returns an iterator over all assets for a release. | No direct tests. | | method | ReleaseService.ListAssets | `func (s *ReleaseService) ListAssets(ctx context.Context, owner, repo string, releaseID int64) ([]types.Attachment, error)` | ListAssets returns all assets for a release. | No direct tests. | | method | RepoService.AcceptTransfer | `func (s *RepoService) AcceptTransfer(ctx context.Context, owner, repo string) error` | AcceptTransfer accepts a pending repository transfer. | No direct tests. | diff --git a/releases.go b/releases.go index ebd5480..4d833fd 100644 --- a/releases.go +++ b/releases.go @@ -53,6 +53,16 @@ func (s *ReleaseService) GetByTag(ctx context.Context, owner, repo, tag string) return &out, nil } +// GetLatest returns the most recent non-prerelease, non-draft release. +func (s *ReleaseService) GetLatest(ctx context.Context, owner, repo string) (*types.Release, error) { + path := ResolvePath("/api/v1/repos/{owner}/{repo}/releases/latest", pathParams("owner", owner, "repo", repo)) + var out types.Release + if err := s.client.Get(ctx, path, &out); err != nil { + return nil, err + } + return &out, nil +} + // DeleteByTag deletes a release by its tag name. func (s *ReleaseService) DeleteByTag(ctx context.Context, owner, repo, tag string) error { path := ResolvePath("/api/v1/repos/{owner}/{repo}/releases/tags/{tag}", pathParams("owner", owner, "repo", repo, "tag", tag)) diff --git a/releases_test.go b/releases_test.go index b0410d4..1779aa2 100644 --- a/releases_test.go +++ b/releases_test.go @@ -134,6 +134,31 @@ func TestReleaseService_GetByTag_Good(t *testing.T) { } } +func TestReleaseService_GetLatest_Good(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + t.Errorf("expected GET, got %s", r.Method) + } + if r.URL.Path != "/api/v1/repos/core/go-forge/releases/latest" { + t.Errorf("wrong path: %s", r.URL.Path) + } + json.NewEncoder(w).Encode(types.Release{ID: 3, TagName: "v2.1.0", Title: "Latest Release"}) + })) + defer srv.Close() + + f := NewForge(srv.URL, "tok") + release, err := f.Releases.GetLatest(context.Background(), "core", "go-forge") + if err != nil { + t.Fatal(err) + } + if release.TagName != "v2.1.0" { + t.Errorf("got tag=%q, want %q", release.TagName, "v2.1.0") + } + if release.Title != "Latest Release" { + t.Errorf("got title=%q, want %q", release.Title, "Latest Release") + } +} + func TestReleaseService_CreateAttachment_Good(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost {