1
0
mirror of https://github.com/helm/chart-releaser.git synced 2026-02-05 09:45:23 +01:00
Files
chart-releaser/pkg/releaser/releaser_test.go
Steven Barnes 378f5275a6 Add option to host the chart package files in the GitHub Pages branch (#123)
* add packages-with-index flag

Signed-off-by: Steven Barnes <Steven.Barnes@topgolf.com>

* Add unit tests

Signed-off-by: Steven Barnes <Steven.Barnes@topgolf.com>

* delete files created by test

Signed-off-by: Steven Barnes <Steven.Barnes@topgolf.com>

* Authenticate to get existing index.yaml

Signed-off-by: Steven Barnes <Steven.Barnes@topgolf.com>

* update docs

Signed-off-by: cpanato <ctadeu@gmail.com>

* fix lints

Signed-off-by: cpanato <ctadeu@gmail.com>

* add git pull function

Signed-off-by: cpanato <ctadeu@gmail.com>

* update help text for upload command

Signed-off-by: cpanato <ctadeu@gmail.com>

---------

Signed-off-by: Steven Barnes <Steven.Barnes@topgolf.com>
Signed-off-by: cpanato <ctadeu@gmail.com>
Co-authored-by: cpanato <ctadeu@gmail.com>
2023-06-20 11:26:09 +02:00

527 lines
14 KiB
Go

// Copyright The Helm Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package releaser
import (
"context"
"fmt"
"os"
"path/filepath"
"testing"
"github.com/helm/chart-releaser/pkg/github"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"helm.sh/helm/v3/pkg/provenance"
"helm.sh/helm/v3/pkg/repo"
"github.com/helm/chart-releaser/pkg/config"
)
type FakeGitHub struct {
mock.Mock
release *github.Release
}
type FakeGit struct {
indexFile string
mock.Mock
}
func (f *FakeGit) AddWorktree(workingDir string, committish string) (string, error) {
dir, err := os.MkdirTemp("", "chart-releaser-")
if err != nil {
return "", err
}
if len(f.indexFile) == 0 {
return dir, nil
}
return dir, copyFile(f.indexFile, filepath.Join(dir, "index.yaml"))
}
func (f *FakeGit) RemoveWorktree(workingDir string, path string) error {
f.Called(workingDir, path)
return os.RemoveAll(workingDir)
}
func (f *FakeGit) Add(workingDir string, args ...string) error {
f.Called(workingDir, args)
if len(args) == 0 {
return fmt.Errorf("no args specified")
}
return nil
}
func (f *FakeGit) Commit(workingDir string, message string) error {
f.Called(workingDir, message)
return nil
}
func (f *FakeGit) Pull(workingDir string, args ...string) error {
f.Called(workingDir, args)
return nil
}
func (f *FakeGit) Push(workingDir string, args ...string) error {
f.Called(workingDir, args)
return nil
}
func (f *FakeGit) GetPushURL(remote string, token string) (string, error) {
f.Called(remote, token)
pushURLWithToken := fmt.Sprintf("https://x-access-token:%s@github.com/owner/repo", token)
return pushURLWithToken, nil
}
func (f *FakeGitHub) CreateRelease(ctx context.Context, input *github.Release) error {
f.Called(ctx, input)
f.release = input
return nil
}
func (f *FakeGitHub) GetRelease(ctx context.Context, tag string) (*github.Release, error) {
release := &github.Release{
Name: "testdata/release-packages/test-chart-0.1.0",
Description: "A Helm chart for Kubernetes",
Assets: []*github.Asset{
{
Path: "testdata/release-packages/test-chart-0.1.0.tgz",
URL: "https://myrepo/charts/test-chart-0.1.0.tgz",
},
{
Path: "testdata/release-packages/third-party-file-0.1.0.txt",
URL: "https://myrepo/charts/third-party-file-0.1.0.txt",
},
},
}
return release, nil
}
func (f *FakeGitHub) CreatePullRequest(owner string, repo string, message string, head string, base string) (string, error) {
f.Called(owner, repo, message, head, base)
return "https://github.com/owner/repo/pull/42", nil
}
func TestReleaser_UpdateIndexFile(t *testing.T) {
indexDir, _ := os.MkdirTemp(".", "index")
defer os.RemoveAll(indexDir)
fakeGitHub := new(FakeGitHub)
tests := []struct {
name string
exists bool
releaser *Releaser
indexFile string
}{
{
name: "index-file-exists",
exists: true,
releaser: &Releaser{
config: &config.Options{
IndexPath: "testdata/index/index.yaml",
PackagePath: "testdata/release-packages",
},
github: fakeGitHub,
},
indexFile: "testdata/repo/index.yaml",
},
{
name: "index-file-exists-pages-index-path",
exists: true,
releaser: &Releaser{
config: &config.Options{
IndexPath: "testdata/index/index.yaml",
PackagePath: "testdata/release-packages",
PagesIndexPath: "./",
},
github: fakeGitHub,
},
indexFile: "testdata/repo/index.yaml",
},
{
name: "index-file-does-not-exist",
exists: false,
releaser: &Releaser{
config: &config.Options{
IndexPath: filepath.Join(indexDir, "index.yaml"),
PackagePath: "testdata/release-packages",
},
github: fakeGitHub,
},
indexFile: "",
},
{
name: "index-file-does-not-exist-pages-index-path",
exists: false,
releaser: &Releaser{
config: &config.Options{
IndexPath: filepath.Join(indexDir, "index.yaml"),
PackagePath: "testdata/release-packages",
PagesIndexPath: "./",
},
github: fakeGitHub,
},
indexFile: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var sha256 string
if tt.exists {
sha256, _ = provenance.DigestFile(tt.releaser.config.IndexPath)
}
fakeGit := new(FakeGit)
fakeGit.indexFile = tt.indexFile
fakeGit.On("RemoveWorktree", mock.Anything, mock.Anything).Return(nil)
tt.releaser.git = fakeGit
update, err := tt.releaser.UpdateIndexFile()
assert.NoError(t, err)
assert.Equal(t, update, !tt.exists)
if tt.exists {
newSha256, _ := provenance.DigestFile(tt.releaser.config.IndexPath)
assert.Equal(t, sha256, newSha256)
} else {
_, err := os.Stat(tt.releaser.config.IndexPath)
assert.NoError(t, err)
}
})
}
}
func TestReleaser_UpdateIndexFileGenerated(t *testing.T) {
indexDir, _ := os.MkdirTemp(".", "index")
defer os.RemoveAll(indexDir)
fakeGitHub := new(FakeGitHub)
tests := []struct {
name string
releaser *Releaser
indexFile string
}{
{
name: "index-file-exists",
releaser: &Releaser{
config: &config.Options{
IndexPath: filepath.Join(indexDir, "index.yaml"),
PackagePath: "testdata/release-packages",
},
github: fakeGitHub,
},
indexFile: "testdata/empty-repo/index.yaml",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fakeGit := new(FakeGit)
fakeGit.indexFile = tt.indexFile
fakeGit.On("RemoveWorktree", mock.Anything, mock.Anything).Return(nil)
tt.releaser.git = fakeGit
indexFile, _ := repo.LoadIndexFile("testdata/empty-repo/index.yaml")
generated := indexFile.Generated
update, err := tt.releaser.UpdateIndexFile()
assert.NoError(t, err)
assert.True(t, update)
newIndexFile, _ := repo.LoadIndexFile(tt.releaser.config.IndexPath)
newGenerated := newIndexFile.Generated
assert.True(t, newGenerated.After(generated))
assert.Equal(t, 2, len(newIndexFile.Entries))
})
}
}
func TestReleaser_splitPackageNameAndVersion(t *testing.T) {
tests := []struct {
name string
pkg string
expected []string
}{
{
"no-hyphen",
"foo",
nil,
},
{
"one-hyphen",
"foo-1.2.3",
[]string{"foo", "1.2.3"},
},
{
"two-hyphens",
"foo-bar-1.2.3",
[]string{"foo-bar", "1.2.3"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Releaser{}
if tt.expected == nil {
assert.Panics(t, func() {
r.splitPackageNameAndVersion(tt.pkg)
}, "slice bounds out of range")
} else {
actual := r.splitPackageNameAndVersion(tt.pkg)
assert.Equal(t, tt.expected, actual)
}
})
}
}
func TestReleaser_addToIndexFile(t *testing.T) {
tests := []struct {
name string
chart string
version string
releaser *Releaser
packagesWithIndex bool
error bool
}{
{
"invalid-package",
"does-not-exist",
"0.1.0",
&Releaser{
config: &config.Options{
PackagePath: "testdata/release-packages",
PackagesWithIndex: false,
},
},
false,
true,
},
{
"valid-package",
"test-chart",
"0.1.0",
&Releaser{
config: &config.Options{
PackagePath: "testdata/release-packages",
PackagesWithIndex: false,
},
},
false,
false,
},
{
"valid-package-with-index",
"test-chart",
"0.1.0",
&Releaser{
config: &config.Options{
PackagePath: "testdata/release-packages",
PackagesWithIndex: true,
},
},
true,
false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
indexFile := repo.NewIndexFile()
url := fmt.Sprintf("https://myrepo/charts/%s-%s.tgz", tt.chart, tt.version)
err := tt.releaser.addToIndexFile(indexFile, url)
if tt.error {
assert.Error(t, err)
assert.False(t, indexFile.Has(tt.chart, tt.version))
} else {
assert.True(t, indexFile.Has(tt.chart, tt.version))
indexEntry, _ := indexFile.Get(tt.chart, tt.version)
if tt.packagesWithIndex {
assert.Equal(t, filepath.Base(url), indexEntry.URLs[0])
} else {
assert.Equal(t, url, indexEntry.URLs[0])
}
}
})
}
}
func TestReleaser_CreateReleases(t *testing.T) {
tests := []struct {
name string
packagePath string
chart string
version string
commit string
latest string
Releaser *Releaser
error bool
}{
{
name: "invalid-package-path",
packagePath: "testdata/does-not-exist",
chart: "test-chart",
version: "0.1.0",
commit: "",
latest: "true",
Releaser: &Releaser{
config: &config.Options{
PackagePath: "testdata/does-not-exist",
Commit: "",
PackagesWithIndex: false,
MakeReleaseLatest: true,
},
},
error: true,
},
{
name: "valid-package-path",
packagePath: "testdata/release-packages",
chart: "test-chart",
version: "0.1.0",
commit: "",
latest: "true",
Releaser: &Releaser{
config: &config.Options{
PackagePath: "testdata/release-packages",
Commit: "",
PackagesWithIndex: false,
MakeReleaseLatest: true,
},
},
error: false,
},
{
name: "valid-package-path-with-commit",
packagePath: "testdata/release-packages",
chart: "test-chart",
version: "0.1.0",
commit: "5e239bd19fbefb9eb0181ecf0c7ef73b8fe2753c",
latest: "true",
Releaser: &Releaser{
config: &config.Options{
PackagePath: "testdata/release-packages",
Commit: "5e239bd19fbefb9eb0181ecf0c7ef73b8fe2753c",
PackagesWithIndex: false,
MakeReleaseLatest: true,
},
},
error: false,
},
{
name: "valid-package-with-index",
packagePath: "testdata/release-packages",
chart: "test-chart",
version: "0.1.0",
commit: "5e239bd19fbefb9eb0181ecf0c7ef73b8fe2753c",
latest: "true",
Releaser: &Releaser{
config: &config.Options{
PackagePath: "testdata/release-packages",
Commit: "5e239bd19fbefb9eb0181ecf0c7ef73b8fe2753c",
PackagesWithIndex: true,
Push: true,
MakeReleaseLatest: true,
},
},
error: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fakeGitHub := new(FakeGitHub)
fakeGitHub.On("CreateRelease", mock.Anything, mock.Anything).Return(nil)
tt.Releaser.github = fakeGitHub
fakeGit := new(FakeGit)
fakeGit.On("AddWorktree", mock.Anything, mock.Anything).Return("/tmp/chart-releaser-012345678", nil)
fakeGit.On("RemoveWorktree", mock.Anything, mock.Anything).Return(nil)
fakeGit.On("Add", mock.Anything, mock.Anything).Return(nil)
fakeGit.On("Commit", mock.Anything, mock.Anything).Return(nil)
fakeGit.On("Push", mock.Anything, mock.Anything).Return(nil)
pushURL := fmt.Sprintf("https://x-access-token:%s@github.com/owner/repo", tt.Releaser.config.Token)
fakeGit.On("GetPushURL", mock.Anything, mock.Anything).Return(pushURL, nil)
tt.Releaser.git = fakeGit
tt.Releaser.config.ReleaseNameTemplate = "{{ .Name }}-{{ .Version }}"
err := tt.Releaser.CreateReleases()
if tt.error {
assert.Error(t, err)
assert.Nil(t, fakeGitHub.release)
fakeGitHub.AssertNumberOfCalls(t, "CreateRelease", 0)
} else {
assert.NoError(t, err)
releaseName := fmt.Sprintf("%s-%s", tt.chart, tt.version)
assetPath := fmt.Sprintf("%s/%s-%s.tgz", tt.Releaser.config.PackagePath, tt.chart, tt.version)
releaseDescription := "A Helm chart for Kubernetes"
assert.Equal(t, releaseName, fakeGitHub.release.Name)
assert.Equal(t, releaseDescription, fakeGitHub.release.Description)
assert.Len(t, fakeGitHub.release.Assets, 1)
assert.Equal(t, assetPath, fakeGitHub.release.Assets[0].Path)
assert.Equal(t, tt.commit, fakeGitHub.release.Commit)
assert.Equal(t, tt.latest, fakeGitHub.release.MakeLatest)
assert.Equal(t, tt.Releaser.config.Commit, fakeGitHub.release.Commit)
fakeGitHub.AssertNumberOfCalls(t, "CreateRelease", 1)
}
})
}
}
func TestReleaser_ReleaseNotes(t *testing.T) {
tests := []struct {
name string
packagePath string
chart string
version string
releaseNotesFile string
expectedReleaseNotes string
}{
{
"chart-package-with-release-notes-file",
"testdata/release-packages",
"test-chart",
"0.1.0",
"release-notes.md",
"The release notes file content is used as release notes",
},
{
"chart-package-with-non-exists-release-notes-file",
"testdata/release-packages",
"test-chart",
"0.1.0",
"non-exists-release-notes.md",
"A Helm chart for Kubernetes",
},
{
"chart-package-with-empty-release-notes-file-config-value",
"testdata/release-packages",
"test-chart",
"0.1.0",
"",
"A Helm chart for Kubernetes",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fakeGitHub := new(FakeGitHub)
r := &Releaser{
config: &config.Options{
PackagePath: "testdata/release-packages",
ReleaseNotesFile: tt.releaseNotesFile,
},
github: fakeGitHub,
}
fakeGitHub.On("CreateRelease", mock.Anything, mock.Anything).Return(nil)
err := r.CreateReleases()
assert.NoError(t, err)
assert.Equal(t, tt.expectedReleaseNotes, fakeGitHub.release.Description)
})
}
}