From 92c1d6c7f26039c4d590f6114b383ba221e4a413 Mon Sep 17 00:00:00 2001 From: Kenneth Bingham Date: Tue, 30 Jul 2024 18:34:30 -0400 Subject: [PATCH 1/4] tweak build to enable contributions from forks Signed-off-by: Kenneth Bingham --- .github/workflows/release.yaml | 1 + .goreleaser.yml | 10 +++++----- magefile.go | 9 ++++++++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 17029c28..5e3e0d30 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -54,6 +54,7 @@ jobs: - uses: chainguard-dev/actions/goimports@main - name: Login to registry + if: github.repository_owner == 'helm' uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: registry: quay.io diff --git a/.goreleaser.yml b/.goreleaser.yml index 148e778f..209c6d15 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -58,7 +58,7 @@ snapshot: dockers: - goos: linux goarch: amd64 - skip_push: "{{ ne .GitURL 'https://github.com/helm/chart-releaser' | toYaml }}" + skip_push: "{{ if ne .GitURL \"https://github.com/helm/chart-releaser\" }}true{{ else }}false{{ end }}" dockerfile: Dockerfile use: buildx image_templates: @@ -77,7 +77,7 @@ dockers: - goos: linux goarch: arm64 - skip_push: "{{ ne .GitURL 'https://github.com/helm/chart-releaser' | toYaml }}" + skip_push: "{{ if ne .GitURL \"https://github.com/helm/chart-releaser\" }}true{{ else }}false{{ end }}" dockerfile: Dockerfile use: buildx image_templates: @@ -97,7 +97,7 @@ dockers: - goos: linux goarch: arm goarm: 7 - skip_push: "{{ ne .GitURL 'https://github.com/helm/chart-releaser' | toYaml }}" + skip_push: "{{ if ne .GitURL \"https://github.com/helm/chart-releaser\" }}true{{ else }}false{{ end }}" dockerfile: Dockerfile use: buildx image_templates: @@ -116,7 +116,7 @@ dockers: - goos: linux goarch: s390x - skip_push: "{{ ne .GitURL 'https://github.com/helm/chart-releaser' | toYaml }}" + skip_push: "{{ if ne .GitURL \"https://github.com/helm/chart-releaser\" }}true{{ else }}false{{ end }}" dockerfile: Dockerfile use: buildx image_templates: @@ -135,7 +135,7 @@ dockers: - goos: linux goarch: ppc64le - skip_push: "{{ ne .GitURL 'https://github.com/helm/chart-releaser' | toYaml }}" + skip_push: "{{ if ne .GitURL \"https://github.com/helm/chart-releaser\" }}true{{ else }}false{{ end }}" dockerfile: Dockerfile use: buildx image_templates: diff --git a/magefile.go b/magefile.go index 721f8416..ce3a6abb 100644 --- a/magefile.go +++ b/magefile.go @@ -137,5 +137,12 @@ func Release() error { return err } - return sh.RunV("goreleaser", "release", "--clean") + var args []string + args = append(args, "release", "--clean") + + if os.Getenv("GITHUB_REPOSITORY_OWNER") != "helm" { + args = append(args, "--skip=docker,homebrew") + } + + return sh.RunV("goreleaser", args...) } From 0732e6879d1ba4176b3b7d673d4527288c65007d Mon Sep 17 00:00:00 2001 From: Kenneth Bingham Date: Tue, 23 Jul 2024 22:02:18 -0400 Subject: [PATCH 2/4] generate release notes for delta since latest release of each chart Signed-off-by: Kenneth Bingham --- go.mod | 1 + go.sum | 2 + pkg/github/github.go | 83 ++++++++++++++++++++++++++++++----- pkg/releaser/releaser.go | 37 +++++++++++++--- pkg/releaser/releaser_test.go | 26 +++++++++++ 5 files changed, 130 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index ea34f6f3..db8ba351 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ toolchain go1.22.4 require ( github.com/MakeNowJust/heredoc v1.0.0 github.com/Songmu/retry v0.1.0 + github.com/blang/semver v3.5.1+incompatible github.com/google/go-github/v56 v56.0.0 github.com/magefile/mage v1.15.0 github.com/mitchellh/go-homedir v1.1.0 diff --git a/go.sum b/go.sum index 01658ad2..cc07ba31 100644 --- a/go.sum +++ b/go.sum @@ -37,6 +37,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= diff --git a/pkg/github/github.go b/pkg/github/github.go index 4143c50d..ff8bbc33 100644 --- a/pkg/github/github.go +++ b/pkg/github/github.go @@ -25,17 +25,18 @@ import ( "github.com/Songmu/retry" "github.com/pkg/errors" + "github.com/blang/semver" "github.com/google/go-github/v56/github" "golang.org/x/oauth2" ) type Release struct { - Name string - Description string - Assets []*Asset - Commit string - GenerateReleaseNotes bool - MakeLatest string + Name string + Description string + Assets []*Asset + Commit string + MakeLatest string + SemVer semver.Version } type Asset struct { @@ -102,15 +103,73 @@ func (c *Client) GetRelease(_ context.Context, tag string) (*Release, error) { return result, nil } +// GetLatestChartRelease queries the GitHub API for the previous release of a chart +func (c *Client) GetLatestChartRelease(_ context.Context, prefix string) (*Release, error) { + // Append hyphen to prefix unless already present + prefix = strings.TrimSuffix(prefix, "-") + "-" + + // Find all versions with tags matching prefix + opt := &github.ListOptions{ + PerPage: 100, + } + var versions []semver.Version + for { + rels, resp, err := c.Repositories.ListReleases(context.TODO(), c.owner, c.repo, opt) + if err != nil { + return nil, err + } else if len(rels) == 0 { + return nil, errors.New("no releases found") + } + for _, rel := range rels { + if strings.HasPrefix(*rel.TagName, prefix) { + version := semver.MustParse(strings.TrimPrefix(*rel.TagName, prefix)) + versions = append(versions, version) + } + } + if resp.NextPage == 0 { + break + } + opt.Page = resp.NextPage + } + + // Sort versions ascending + semver.Sort(versions) + + // Find highest version + latestVersion := versions[len(versions)-1] + var release *github.RepositoryRelease + if rel, _, err := c.Repositories.GetReleaseByTag(context.TODO(), c.owner, c.repo, prefix+latestVersion.String()); err == nil { + release = rel + } + + result := &Release{ + Name: *release.TagName, + Commit: *release.TargetCommitish, + SemVer: latestVersion, + } + return result, nil +} + +// GenerateReleaseNotes generates the release notes for a release +func (c *Client) GenerateReleaseNotes(_ context.Context, latestRelease *Release, nextRelease string) (string, error) { + notes, _, err := c.Repositories.GenerateReleaseNotes(context.TODO(), c.owner, c.repo, &github.GenerateNotesOptions{ + TagName: nextRelease, + PreviousTagName: &latestRelease.Name, + }) + if err != nil { + return "", err + } + return notes.Body, err +} + // CreateRelease creates a new release object in the GitHub API func (c *Client) CreateRelease(_ context.Context, input *Release) error { req := &github.RepositoryRelease{ - Name: &input.Name, - Body: &input.Description, - TagName: &input.Name, - TargetCommitish: &input.Commit, - GenerateReleaseNotes: &input.GenerateReleaseNotes, - MakeLatest: &input.MakeLatest, + Name: &input.Name, + Body: &input.Description, + TagName: &input.Name, + TargetCommitish: &input.Commit, + MakeLatest: &input.MakeLatest, } release, _, err := c.Repositories.CreateRelease(context.TODO(), c.owner, c.repo, req) diff --git a/pkg/releaser/releaser.go b/pkg/releaser/releaser.go index 439ae818..6297c03c 100644 --- a/pkg/releaser/releaser.go +++ b/pkg/releaser/releaser.go @@ -28,6 +28,7 @@ import ( "time" "github.com/Songmu/retry" + "github.com/blang/semver" "text/template" @@ -50,6 +51,8 @@ type GitHub interface { CreateRelease(ctx context.Context, input *github.Release) error GetRelease(ctx context.Context, tag string) (*github.Release, error) CreatePullRequest(owner string, repo string, message string, head string, base string) (string, error) + GetLatestChartRelease(ctx context.Context, prefix string) (*github.Release, error) + GenerateReleaseNotes(ctx context.Context, latestRelease *github.Release, nextRelease string) (string, error) } type Git interface { @@ -238,16 +241,33 @@ func (r *Releaser) computeReleaseName(chart *chart.Chart) (string, error) { return releaseName, nil } -func (r *Releaser) getReleaseNotes(chart *chart.Chart) string { +func (r *Releaser) getReleaseNotes(chart *chart.Chart) (string, error) { if r.config.ReleaseNotesFile != "" { for _, f := range chart.Files { if f.Name == r.config.ReleaseNotesFile { - return string(f.Data) + return string(f.Data), nil } } fmt.Printf("The release note file %q, is not present in the chart package\n", r.config.ReleaseNotesFile) + } else if r.config.GenerateReleaseNotes { + latestRelease, err := r.github.GetLatestChartRelease(context.TODO(), chart.Metadata.Name) + if err != nil { + return "", errors.Wrapf(err, "failed to get latest release for chart %s", chart.Metadata.Name) + } + nextVersion := semver.MustParse(chart.Metadata.Version) + versions := []semver.Version{nextVersion, latestRelease.SemVer} + semver.Sort(versions) + highest := versions[len(versions)-1] + // skip generating notes if there's already a higher version in GitHub + if nextVersion.String() == highest.String() { + notes, err := r.github.GenerateReleaseNotes(context.TODO(), latestRelease, chart.Metadata.Version) + if err != nil { + return "", errors.Wrapf(err, "failed to generate release notes for chart %s", chart.Metadata.Name) + } + return notes, nil + } } - return chart.Metadata.Description + return chart.Metadata.Description, nil } func (r *Releaser) splitPackageNameAndVersion(pkg string) []string { @@ -307,16 +327,19 @@ func (r *Releaser) CreateReleases() error { if err != nil { return err } + notes, err := r.getReleaseNotes(ch) + if err != nil { + return err + } release := &github.Release{ Name: releaseName, - Description: r.getReleaseNotes(ch), + Description: notes, Assets: []*github.Asset{ {Path: p}, }, - Commit: r.config.Commit, - GenerateReleaseNotes: r.config.GenerateReleaseNotes, - MakeLatest: strconv.FormatBool(r.config.MakeReleaseLatest), + Commit: r.config.Commit, + MakeLatest: strconv.FormatBool(r.config.MakeReleaseLatest), } provFile := fmt.Sprintf("%s.prov", p) if _, err := os.Stat(provFile); err == nil { diff --git a/pkg/releaser/releaser_test.go b/pkg/releaser/releaser_test.go index 55c7841f..56b2cd77 100644 --- a/pkg/releaser/releaser_test.go +++ b/pkg/releaser/releaser_test.go @@ -21,6 +21,7 @@ import ( "path/filepath" "testing" + "github.com/blang/semver" "github.com/helm/chart-releaser/pkg/github" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -115,6 +116,31 @@ func (f *FakeGitHub) CreatePullRequest(owner string, repo string, message string return "https://github.com/owner/repo/pull/42", nil } +// GetLatestChartRelease queries the GitHub API for the previous release of a chart +func (f *FakeGitHub) GetLatestChartRelease(_ context.Context, prefix string) (*github.Release, error) { + f.Called(prefix) + + result := &github.Release{ + Name: prefix + "-1.2.3", + Commit: "c11eea26f51782a8063ded1085384acb2928fd91", + SemVer: semver.Version{ + Major: 1, + Minor: 2, + Patch: 3, + }, + } + return result, nil +} + +// GenerateReleaseNotes generates the release notes for a release +func (f *FakeGitHub) GenerateReleaseNotes(_ context.Context, latestRelease *github.Release, nextRelease string) (string, error) { + f.Called(latestRelease, nextRelease) + + notes := "# Noted." + + return notes, nil +} + func TestReleaser_UpdateIndexFile(t *testing.T) { indexDir, _ := os.MkdirTemp(".", "index") defer os.RemoveAll(indexDir) From ffc76298d28d8aa6a564fee422f5cd68c80cd575 Mon Sep 17 00:00:00 2001 From: Kenneth Bingham Date: Thu, 8 Aug 2024 17:24:28 -0400 Subject: [PATCH 3/4] compare with correct tag Signed-off-by: Kenneth Bingham --- pkg/github/github.go | 5 +++-- pkg/releaser/releaser.go | 4 ++-- pkg/releaser/releaser_test.go | 5 +++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pkg/github/github.go b/pkg/github/github.go index ff8bbc33..2e70e3ea 100644 --- a/pkg/github/github.go +++ b/pkg/github/github.go @@ -24,6 +24,7 @@ import ( "github.com/Songmu/retry" "github.com/pkg/errors" + "helm.sh/helm/v3/pkg/chart" "github.com/blang/semver" "github.com/google/go-github/v56/github" @@ -151,9 +152,9 @@ func (c *Client) GetLatestChartRelease(_ context.Context, prefix string) (*Relea } // GenerateReleaseNotes generates the release notes for a release -func (c *Client) GenerateReleaseNotes(_ context.Context, latestRelease *Release, nextRelease string) (string, error) { +func (c *Client) GenerateReleaseNotes(_ context.Context, latestRelease *Release, chart *chart.Chart) (string, error) { notes, _, err := c.Repositories.GenerateReleaseNotes(context.TODO(), c.owner, c.repo, &github.GenerateNotesOptions{ - TagName: nextRelease, + TagName: chart.Metadata.Name + "-" + chart.Metadata.Version, PreviousTagName: &latestRelease.Name, }) if err != nil { diff --git a/pkg/releaser/releaser.go b/pkg/releaser/releaser.go index 6297c03c..04c3a476 100644 --- a/pkg/releaser/releaser.go +++ b/pkg/releaser/releaser.go @@ -52,7 +52,7 @@ type GitHub interface { GetRelease(ctx context.Context, tag string) (*github.Release, error) CreatePullRequest(owner string, repo string, message string, head string, base string) (string, error) GetLatestChartRelease(ctx context.Context, prefix string) (*github.Release, error) - GenerateReleaseNotes(ctx context.Context, latestRelease *github.Release, nextRelease string) (string, error) + GenerateReleaseNotes(ctx context.Context, latestRelease *github.Release, chart *chart.Chart) (string, error) } type Git interface { @@ -260,7 +260,7 @@ func (r *Releaser) getReleaseNotes(chart *chart.Chart) (string, error) { highest := versions[len(versions)-1] // skip generating notes if there's already a higher version in GitHub if nextVersion.String() == highest.String() { - notes, err := r.github.GenerateReleaseNotes(context.TODO(), latestRelease, chart.Metadata.Version) + notes, err := r.github.GenerateReleaseNotes(context.TODO(), latestRelease, chart) if err != nil { return "", errors.Wrapf(err, "failed to generate release notes for chart %s", chart.Metadata.Name) } diff --git a/pkg/releaser/releaser_test.go b/pkg/releaser/releaser_test.go index 56b2cd77..b7682df6 100644 --- a/pkg/releaser/releaser_test.go +++ b/pkg/releaser/releaser_test.go @@ -29,6 +29,7 @@ import ( "helm.sh/helm/v3/pkg/repo" "github.com/helm/chart-releaser/pkg/config" + "helm.sh/helm/v3/pkg/chart" ) type FakeGitHub struct { @@ -133,8 +134,8 @@ func (f *FakeGitHub) GetLatestChartRelease(_ context.Context, prefix string) (*g } // GenerateReleaseNotes generates the release notes for a release -func (f *FakeGitHub) GenerateReleaseNotes(_ context.Context, latestRelease *github.Release, nextRelease string) (string, error) { - f.Called(latestRelease, nextRelease) +func (f *FakeGitHub) GenerateReleaseNotes(_ context.Context, latestRelease *github.Release, chart *chart.Chart) (string, error) { + f.Called(latestRelease, chart) notes := "# Noted." From 5b5dc1fee24c1ae19d57465c79a7137905cc71d8 Mon Sep 17 00:00:00 2001 From: Kenneth Bingham Date: Thu, 8 Aug 2024 17:24:48 -0400 Subject: [PATCH 4/4] stop skipping generate if notes file missing Signed-off-by: Kenneth Bingham --- pkg/releaser/releaser.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/releaser/releaser.go b/pkg/releaser/releaser.go index 04c3a476..6d80c913 100644 --- a/pkg/releaser/releaser.go +++ b/pkg/releaser/releaser.go @@ -249,7 +249,8 @@ func (r *Releaser) getReleaseNotes(chart *chart.Chart) (string, error) { } } fmt.Printf("The release note file %q, is not present in the chart package\n", r.config.ReleaseNotesFile) - } else if r.config.GenerateReleaseNotes { + } + if r.config.GenerateReleaseNotes { latestRelease, err := r.github.GetLatestChartRelease(context.TODO(), chart.Metadata.Name) if err != nil { return "", errors.Wrapf(err, "failed to get latest release for chart %s", chart.Metadata.Name)