diff --git a/github/github-accessors.go b/github/github-accessors.go index ec77299f2fe..29d092af03f 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -23510,6 +23510,22 @@ func (r *RepoDependencies) GetVersionInfo() string { return *r.VersionInfo } +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (r *RepoImmutableReleasesStatus) GetEnabled() bool { + if r == nil || r.Enabled == nil { + return false + } + return *r.Enabled +} + +// GetEnforcedByOwner returns the EnforcedByOwner field if it's non-nil, zero value otherwise. +func (r *RepoImmutableReleasesStatus) GetEnforcedByOwner() bool { + if r == nil || r.EnforcedByOwner == nil { + return false + } + return *r.EnforcedByOwner +} + // GetBranch returns the Branch field if it's non-nil, zero value otherwise. func (r *RepoMergeUpstreamRequest) GetBranch() string { if r == nil || r.Branch == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 08332e3776d..00c647c8db5 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -30393,6 +30393,28 @@ func TestRepoDependencies_GetVersionInfo(tt *testing.T) { r.GetVersionInfo() } +func TestRepoImmutableReleasesStatus_GetEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RepoImmutableReleasesStatus{Enabled: &zeroValue} + r.GetEnabled() + r = &RepoImmutableReleasesStatus{} + r.GetEnabled() + r = nil + r.GetEnabled() +} + +func TestRepoImmutableReleasesStatus_GetEnforcedByOwner(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RepoImmutableReleasesStatus{EnforcedByOwner: &zeroValue} + r.GetEnforcedByOwner() + r = &RepoImmutableReleasesStatus{} + r.GetEnforcedByOwner() + r = nil + r.GetEnforcedByOwner() +} + func TestRepoMergeUpstreamRequest_GetBranch(tt *testing.T) { tt.Parallel() var zeroValue string diff --git a/github/repos_immutable_releases.go b/github/repos_immutable_releases.go new file mode 100644 index 00000000000..1717ed1268b --- /dev/null +++ b/github/repos_immutable_releases.go @@ -0,0 +1,82 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// RepoImmutableReleasesStatus represents the immutable releases status for a repository. +type RepoImmutableReleasesStatus struct { + Enabled *bool `json:"enabled,omitempty"` + EnforcedByOwner *bool `json:"enforced_by_owner,omitempty"` +} + +// EnableImmutableReleases enables immutable releases for a repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/repos#enable-immutable-releases +// +//meta:operation PUT /repos/{owner}/{repo}/immutable-releases +func (s *RepositoriesService) EnableImmutableReleases(ctx context.Context, owner, repo string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/immutable-releases", owner, repo) + + req, err := s.client.NewRequest("PUT", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(ctx, req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// DisableImmutableReleases disables immutable releases for a repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/repos#disable-immutable-releases +// +//meta:operation DELETE /repos/{owner}/{repo}/immutable-releases +func (s *RepositoriesService) DisableImmutableReleases(ctx context.Context, owner, repo string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/immutable-releases", owner, repo) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(ctx, req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// IsImmutableReleasesEnabled checks if immutable releases are enabled for +// the repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/repos#check-if-immutable-releases-are-enabled-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/immutable-releases +func (s *RepositoriesService) IsImmutableReleasesEnabled(ctx context.Context, owner, repo string) (*RepoImmutableReleasesStatus, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/immutable-releases", owner, repo) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + status := new(RepoImmutableReleasesStatus) + resp, err := s.client.Do(ctx, req, status) + if err != nil { + return nil, resp, err + } + + return status, resp, nil +} diff --git a/github/repos_immutable_releases_test.go b/github/repos_immutable_releases_test.go new file mode 100644 index 00000000000..9a1a04f9f06 --- /dev/null +++ b/github/repos_immutable_releases_test.go @@ -0,0 +1,100 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRepositoriesService_EnableImmutableReleases(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/owner/repo/immutable-releases", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Repositories.EnableImmutableReleases(ctx, "owner", "repo") + if err != nil { + t.Errorf("Repositories.EnableImmutableReleases returned error: %v", err) + } + + const methodName = "EnableImmutableReleases" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.EnableImmutableReleases(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.EnableImmutableReleases(ctx, "owner", "repo") + }) +} + +func TestRepositoriesService_DisableImmutableReleases(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/owner/repo/immutable-releases", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Repositories.DisableImmutableReleases(ctx, "owner", "repo") + if err != nil { + t.Errorf("Repositories.DisableImmutableReleases returned error: %v", err) + } + + const methodName = "DisableImmutableReleases" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.DisableImmutableReleases(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DisableImmutableReleases(ctx, "owner", "repo") + }) +} + +func TestRepositoriesService_IsImmutableReleasesEnabled(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/owner/repo/immutable-releases", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"enabled": true, "enforced_by_owner": false}`) + }) + + ctx := t.Context() + status, _, err := client.Repositories.IsImmutableReleasesEnabled(ctx, "owner", "repo") + if err != nil { + t.Errorf("Repositories.IsImmutableReleasesEnabled returned error: %v", err) + } + want := &RepoImmutableReleasesStatus{Enabled: Ptr(true), EnforcedByOwner: Ptr(false)} + if !cmp.Equal(status, want) { + t.Errorf("Repositories.IsImmutableReleasesEnabled returned %+v, want %+v", status, want) + } + + const methodName = "IsImmutableReleasesEnabled" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.IsImmutableReleasesEnabled(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.IsImmutableReleasesEnabled(ctx, "owner", "repo") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +}