From 8f1f044aab00b095fa4a22742580b7818af8a630 Mon Sep 17 00:00:00 2001 From: RKS Date: Tue, 15 Sep 2026 22:26:00 -0400 Subject: [PATCH 1/3] feat: Support Changes and Enterprise fields on ReleaseEvent --- github/event_types.go | 34 +++++++++++++--- github/github-accessors.go | 64 ++++++++++++++++++++++++++++++ github/github-accessors_test.go | 70 +++++++++++++++++++++++++++++++++ github/messages_test.go | 65 ++++++++++++++++++++++++++++++ 4 files changed, 228 insertions(+), 5 deletions(-) diff --git a/github/event_types.go b/github/event_types.go index df45ffbac3b..950dcf63985 100644 --- a/github/event_types.go +++ b/github/event_types.go @@ -1468,24 +1468,48 @@ type RegistryPackageEvent struct { } // ReleaseEvent is triggered when a release is published, unpublished, created, -// edited, deleted, or prereleased. +// edited, deleted, prereleased, or released. // The Webhook event name is "release". // // GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#release type ReleaseEvent struct { // Action is the action that was performed. Possible values are: "published", "unpublished", - // "created", "edited", "deleted", or "prereleased". + // "created", "edited", "deleted", "prereleased", or "released". Action *string `json:"action,omitempty"` Release *RepositoryRelease `json:"release,omitempty"` // The following fields are only populated by Webhook events. - Repo *Repository `json:"repository,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` + // Changes is populated in "edited" event deliveries. + Changes *ReleaseChanges `json:"changes,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` // The following field is only present when the webhook is triggered on // a repository belonging to an organization. Org *Organization `json:"organization,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an enterprise. + Enterprise *Enterprise `json:"enterprise,omitempty"` +} + +// ReleaseChanges represents changes made to a release. +type ReleaseChanges struct { + Body *ReleaseChangeFrom `json:"body,omitempty"` + Name *ReleaseChangeFrom `json:"name,omitempty"` + TagName *ReleaseChangeFrom `json:"tag_name,omitempty"` + MakeLatest *ReleaseChangeToBool `json:"make_latest,omitempty"` +} + +// ReleaseChangeFrom represents a release string field change containing the previous value. +type ReleaseChangeFrom struct { + From *string `json:"from,omitempty"` +} + +// ReleaseChangeToBool represents a boolean release field change containing the new value. +type ReleaseChangeToBool struct { + To *bool `json:"to,omitempty"` } // RepositoryEvent is triggered when a repository is created, archived, unarchived, diff --git a/github/github-accessors.go b/github/github-accessors.go index f1759a7e1f2..1970061b268 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -35606,6 +35606,54 @@ func (r *ReleaseAsset) GetURL() string { return *r.URL } +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (r *ReleaseChangeFrom) GetFrom() string { + if r == nil || r.From == nil { + return "" + } + return *r.From +} + +// GetBody returns the Body field. +func (r *ReleaseChanges) GetBody() *ReleaseChangeFrom { + if r == nil { + return nil + } + return r.Body +} + +// GetMakeLatest returns the MakeLatest field. +func (r *ReleaseChanges) GetMakeLatest() *ReleaseChangeToBool { + if r == nil { + return nil + } + return r.MakeLatest +} + +// GetName returns the Name field. +func (r *ReleaseChanges) GetName() *ReleaseChangeFrom { + if r == nil { + return nil + } + return r.Name +} + +// GetTagName returns the TagName field. +func (r *ReleaseChanges) GetTagName() *ReleaseChangeFrom { + if r == nil { + return nil + } + return r.TagName +} + +// GetTo returns the To field if it's non-nil, zero value otherwise. +func (r *ReleaseChangeToBool) GetTo() bool { + if r == nil || r.To == nil { + return false + } + return *r.To +} + // GetAction returns the Action field if it's non-nil, zero value otherwise. func (r *ReleaseEvent) GetAction() string { if r == nil || r.Action == nil { @@ -35614,6 +35662,22 @@ func (r *ReleaseEvent) GetAction() string { return *r.Action } +// GetChanges returns the Changes field. +func (r *ReleaseEvent) GetChanges() *ReleaseChanges { + if r == nil { + return nil + } + return r.Changes +} + +// GetEnterprise returns the Enterprise field. +func (r *ReleaseEvent) GetEnterprise() *Enterprise { + if r == nil { + return nil + } + return r.Enterprise +} + // GetInstallation returns the Installation field. func (r *ReleaseEvent) GetInstallation() *Installation { if r == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 9a5069fcdf0..1dc5b4b9d0a 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -44484,6 +44484,60 @@ func TestReleaseAsset_GetURL(tt *testing.T) { r.GetURL() } +func TestReleaseChangeFrom_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReleaseChangeFrom{From: &zeroValue} + r.GetFrom() + r = &ReleaseChangeFrom{} + r.GetFrom() + r = nil + r.GetFrom() +} + +func TestReleaseChanges_GetBody(tt *testing.T) { + tt.Parallel() + r := &ReleaseChanges{} + r.GetBody() + r = nil + r.GetBody() +} + +func TestReleaseChanges_GetMakeLatest(tt *testing.T) { + tt.Parallel() + r := &ReleaseChanges{} + r.GetMakeLatest() + r = nil + r.GetMakeLatest() +} + +func TestReleaseChanges_GetName(tt *testing.T) { + tt.Parallel() + r := &ReleaseChanges{} + r.GetName() + r = nil + r.GetName() +} + +func TestReleaseChanges_GetTagName(tt *testing.T) { + tt.Parallel() + r := &ReleaseChanges{} + r.GetTagName() + r = nil + r.GetTagName() +} + +func TestReleaseChangeToBool_GetTo(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &ReleaseChangeToBool{To: &zeroValue} + r.GetTo() + r = &ReleaseChangeToBool{} + r.GetTo() + r = nil + r.GetTo() +} + func TestReleaseEvent_GetAction(tt *testing.T) { tt.Parallel() var zeroValue string @@ -44495,6 +44549,22 @@ func TestReleaseEvent_GetAction(tt *testing.T) { r.GetAction() } +func TestReleaseEvent_GetChanges(tt *testing.T) { + tt.Parallel() + r := &ReleaseEvent{} + r.GetChanges() + r = nil + r.GetChanges() +} + +func TestReleaseEvent_GetEnterprise(tt *testing.T) { + tt.Parallel() + r := &ReleaseEvent{} + r.GetEnterprise() + r = nil + r.GetEnterprise() +} + func TestReleaseEvent_GetInstallation(tt *testing.T) { tt.Parallel() r := &ReleaseEvent{} diff --git a/github/messages_test.go b/github/messages_test.go index 62bd47c6e1d..0bfb0bbdc72 100644 --- a/github/messages_test.go +++ b/github/messages_test.go @@ -664,3 +664,68 @@ func TestWebHookType(t *testing.T) { t.Errorf("WebHookType = %q, want %q", got, want) } } + +func TestParseWebHook_ReleaseEvent_Edited(t *testing.T) { + t.Parallel() + rawJSON := []byte(`{ + "action": "edited", + "changes": { + "body": { + "from": "Old body" + }, + "name": { + "from": "Old name" + }, + "tag_name": { + "from": "v1.0.0-rc1" + }, + "make_latest": { + "to": true + } + }, + "release": { + "id": 12345, + "tag_name": "v1.0.0", + "name": "Release 1.0.0", + "body": "New body" + }, + "enterprise": { + "id": 1, + "slug": "octocat-enterprise" + } + }`) + + got, err := ParseWebHook("release", rawJSON) + if err != nil { + t.Fatalf("ParseWebHook failed: %v", err) + } + + event, ok := got.(*ReleaseEvent) + if !ok { + t.Fatalf("ParseWebHook returned %T, want *ReleaseEvent", got) + } + + want := &ReleaseEvent{ + Action: new("edited"), + Changes: &ReleaseChanges{ + Body: &ReleaseChangeFrom{From: new("Old body")}, + Name: &ReleaseChangeFrom{From: new("Old name")}, + TagName: &ReleaseChangeFrom{From: new("v1.0.0-rc1")}, + MakeLatest: &ReleaseChangeToBool{To: new(true)}, + }, + Release: &RepositoryRelease{ + ID: 12345, + TagName: "v1.0.0", + Name: new("Release 1.0.0"), + Body: new("New body"), + }, + Enterprise: &Enterprise{ + ID: new(1), + Slug: new("octocat-enterprise"), + }, + } + + if !cmp.Equal(event, want) { + t.Errorf("ParseWebHook() diff (-got +want):\n%v", cmp.Diff(event, want)) + } +} From 75b7d21ddeb47de00910bd85ede94737cf7d0562 Mon Sep 17 00:00:00 2001 From: RKS Date: Wed, 16 Sep 2026 12:36:53 -0400 Subject: [PATCH 2/3] refactor: update ReleaseChangeToBool.To and test assertion format --- github/event_types.go | 2 +- github/github-accessors.go | 6 +++--- github/github-accessors_test.go | 5 +---- github/messages_test.go | 4 ++-- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/github/event_types.go b/github/event_types.go index 950dcf63985..1154c4194a2 100644 --- a/github/event_types.go +++ b/github/event_types.go @@ -1509,7 +1509,7 @@ type ReleaseChangeFrom struct { // ReleaseChangeToBool represents a boolean release field change containing the new value. type ReleaseChangeToBool struct { - To *bool `json:"to,omitempty"` + To bool `json:"to"` } // RepositoryEvent is triggered when a repository is created, archived, unarchived, diff --git a/github/github-accessors.go b/github/github-accessors.go index 1970061b268..eb8f7cc641e 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -35646,12 +35646,12 @@ func (r *ReleaseChanges) GetTagName() *ReleaseChangeFrom { return r.TagName } -// GetTo returns the To field if it's non-nil, zero value otherwise. +// GetTo returns the To field. func (r *ReleaseChangeToBool) GetTo() bool { - if r == nil || r.To == nil { + if r == nil { return false } - return *r.To + return r.To } // GetAction returns the Action field if it's non-nil, zero value otherwise. diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 1dc5b4b9d0a..3ed0c6bf6d3 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -44529,10 +44529,7 @@ func TestReleaseChanges_GetTagName(tt *testing.T) { func TestReleaseChangeToBool_GetTo(tt *testing.T) { tt.Parallel() - var zeroValue bool - r := &ReleaseChangeToBool{To: &zeroValue} - r.GetTo() - r = &ReleaseChangeToBool{} + r := &ReleaseChangeToBool{} r.GetTo() r = nil r.GetTo() diff --git a/github/messages_test.go b/github/messages_test.go index 0bfb0bbdc72..90faa62044f 100644 --- a/github/messages_test.go +++ b/github/messages_test.go @@ -702,7 +702,7 @@ func TestParseWebHook_ReleaseEvent_Edited(t *testing.T) { event, ok := got.(*ReleaseEvent) if !ok { - t.Fatalf("ParseWebHook returned %T, want *ReleaseEvent", got) + t.Fatalf("ParseWebHook returned %v, want *ReleaseEvent", got) } want := &ReleaseEvent{ @@ -711,7 +711,7 @@ func TestParseWebHook_ReleaseEvent_Edited(t *testing.T) { Body: &ReleaseChangeFrom{From: new("Old body")}, Name: &ReleaseChangeFrom{From: new("Old name")}, TagName: &ReleaseChangeFrom{From: new("v1.0.0-rc1")}, - MakeLatest: &ReleaseChangeToBool{To: new(true)}, + MakeLatest: &ReleaseChangeToBool{To: true}, }, Release: &RepositoryRelease{ ID: 12345, From 8efa573d08feb296480d738720c7bf65647dd01f Mon Sep 17 00:00:00 2001 From: RKS Date: Wed, 16 Sep 2026 16:19:26 -0400 Subject: [PATCH 3/3] refactor: update ReleaseChangeFrom.From to non-pointer string without omitempty --- github/event_types.go | 2 +- github/github-accessors.go | 6 +++--- github/github-accessors_test.go | 5 +---- github/messages_test.go | 6 +++--- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/github/event_types.go b/github/event_types.go index 1154c4194a2..bd55539c787 100644 --- a/github/event_types.go +++ b/github/event_types.go @@ -1504,7 +1504,7 @@ type ReleaseChanges struct { // ReleaseChangeFrom represents a release string field change containing the previous value. type ReleaseChangeFrom struct { - From *string `json:"from,omitempty"` + From string `json:"from"` } // ReleaseChangeToBool represents a boolean release field change containing the new value. diff --git a/github/github-accessors.go b/github/github-accessors.go index eb8f7cc641e..28f07e0b106 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -35606,12 +35606,12 @@ func (r *ReleaseAsset) GetURL() string { return *r.URL } -// GetFrom returns the From field if it's non-nil, zero value otherwise. +// GetFrom returns the From field. func (r *ReleaseChangeFrom) GetFrom() string { - if r == nil || r.From == nil { + if r == nil { return "" } - return *r.From + return r.From } // GetBody returns the Body field. diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 3ed0c6bf6d3..cb8b98099d4 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -44486,10 +44486,7 @@ func TestReleaseAsset_GetURL(tt *testing.T) { func TestReleaseChangeFrom_GetFrom(tt *testing.T) { tt.Parallel() - var zeroValue string - r := &ReleaseChangeFrom{From: &zeroValue} - r.GetFrom() - r = &ReleaseChangeFrom{} + r := &ReleaseChangeFrom{} r.GetFrom() r = nil r.GetFrom() diff --git a/github/messages_test.go b/github/messages_test.go index 90faa62044f..ee86142368d 100644 --- a/github/messages_test.go +++ b/github/messages_test.go @@ -708,9 +708,9 @@ func TestParseWebHook_ReleaseEvent_Edited(t *testing.T) { want := &ReleaseEvent{ Action: new("edited"), Changes: &ReleaseChanges{ - Body: &ReleaseChangeFrom{From: new("Old body")}, - Name: &ReleaseChangeFrom{From: new("Old name")}, - TagName: &ReleaseChangeFrom{From: new("v1.0.0-rc1")}, + Body: &ReleaseChangeFrom{From: "Old body"}, + Name: &ReleaseChangeFrom{From: "Old name"}, + TagName: &ReleaseChangeFrom{From: "v1.0.0-rc1"}, MakeLatest: &ReleaseChangeToBool{To: true}, }, Release: &RepositoryRelease{