Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions pkg/releaser/releaser.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,11 @@ func (r *Releaser) UpdateIndexFile() (bool, error) {
for _, asset := range release.Assets {
downloadURL, _ := url.Parse(asset.URL)
name := filepath.Base(downloadURL.Path)
// Decode URL-encoded characters in the filename (e.g., %2B -> +)
name, err = url.PathUnescape(name)
if err != nil {
return false, errors.Wrapf(err, "error decoding filename from URL: %s", asset.URL)
}
// Ignore any other files added in the release by the users.
if filepath.Ext(name) != chartAssetFileExtension {
continue
Expand Down Expand Up @@ -255,8 +260,14 @@ func (r *Releaser) splitPackageNameAndVersion(pkg string) []string {
return []string{pkg[0:delimIndex], pkg[delimIndex+1:]}
}

func (r *Releaser) addToIndexFile(indexFile *repo.IndexFile, url string) error {
arch := filepath.Join(r.config.PackagePath, filepath.Base(url))
func (r *Releaser) addToIndexFile(indexFile *repo.IndexFile, urlStr string) error {
// Decode URL-encoded characters in the filename (e.g., %2B -> +)
filename := filepath.Base(urlStr)
decodedFilename, err := url.PathUnescape(filename)
if err != nil {
return errors.Wrapf(err, "error decoding filename from URL: %s", urlStr)
}
arch := filepath.Join(r.config.PackagePath, decodedFilename)

// extract chart metadata
fmt.Printf("Extracting chart metadata from %s\n", arch)
Expand All @@ -274,7 +285,7 @@ func (r *Releaser) addToIndexFile(indexFile *repo.IndexFile, url string) error {
// remove url name from url as helm's index library
// adds it in during .Add
// there should be a better way to handle this :(
s := strings.Split(url, "/")
s := strings.Split(urlStr, "/")
s = s[:len(s)-1]

if r.config.PackagesWithIndex {
Expand All @@ -283,8 +294,8 @@ func (r *Releaser) addToIndexFile(indexFile *repo.IndexFile, url string) error {
s = s[:0]
}

// Add to index
return indexFile.MustAdd(c.Metadata, filepath.Base(arch), strings.Join(s, "/"), hash)
// Add to index (using the decoded filename)
return indexFile.MustAdd(c.Metadata, decodedFilename, strings.Join(s, "/"), hash)
}

// CreateReleases finds and uploads Helm chart packages to GitHub
Expand Down
68 changes: 67 additions & 1 deletion pkg/releaser/releaser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,33 @@ func (f *FakeGitHub) CreatePullRequest(owner string, repo string, message string
}

func TestReleaser_UpdateIndexFile(t *testing.T) {
indexDir, _ := os.MkdirTemp(".", "index")
indexDir, err := os.MkdirTemp(".", "index")
if err != nil {
t.Fatalf("Failed to create temp directory: %v", err)
}
defer os.RemoveAll(indexDir)

// Create a separate index directory for the URL encoding test
indexDirWithPlus, err := os.MkdirTemp(".", "index-plus")
if err != nil {
t.Fatalf("Failed to create temp directory: %v", err)
}
defer os.RemoveAll(indexDirWithPlus)

// FakeGitHub that returns URL-encoded asset URLs (e.g., + encoded as %2B)
fakeGitHubURLEncoded := &FakeGitHubWithURLEncoding{
release: &github.Release{
Name: "test-chart-0.1.0+build.1",
Description: "A Helm chart with build metadata",
Assets: []*github.Asset{
{
Path: "testdata/release-packages-plus/test-chart-0.1.0+build.1.tgz",
URL: "https://myrepo/charts/test-chart-0.1.0%2Bbuild.1.tgz",
},
},
},
}

fakeGitHub := new(FakeGitHub)

tests := []struct {
Expand Down Expand Up @@ -177,6 +201,18 @@ func TestReleaser_UpdateIndexFile(t *testing.T) {
},
indexFile: "",
},
{
name: "index-file-with-url-encoded-plus-sign",
exists: false,
releaser: &Releaser{
config: &config.Options{
IndexPath: filepath.Join(indexDirWithPlus, "index.yaml"),
PackagePath: "testdata/release-packages-plus",
},
github: fakeGitHubURLEncoded,
},
indexFile: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -198,6 +234,20 @@ func TestReleaser_UpdateIndexFile(t *testing.T) {
} else {
_, err := os.Stat(tt.releaser.config.IndexPath)
assert.NoError(t, err)

// Additional verification for URL-encoded case
if tt.name == "index-file-with-url-encoded-plus-sign" {
indexFile, err := repo.LoadIndexFile(tt.releaser.config.IndexPath)
assert.NoError(t, err, "should load index file")
assert.True(t, indexFile.Has("test-chart", "0.1.0+build.1"),
"index should contain chart version with + sign (decoded from %2B)")

// Verify the chart entry has the correct version with build metadata
chartVersion, err := indexFile.Get("test-chart", "0.1.0+build.1")
assert.NoError(t, err, "should retrieve chart version")
assert.NotNil(t, chartVersion, "chart version should not be nil")
assert.Equal(t, "0.1.0+build.1", chartVersion.Version, "version should preserve + sign")
}
}
})
}
Expand Down Expand Up @@ -535,3 +585,19 @@ func TestReleaser_ReleaseNotes(t *testing.T) {
})
}
}

type FakeGitHubWithURLEncoding struct {
release *github.Release
}

func (f *FakeGitHubWithURLEncoding) CreateRelease(ctx context.Context, input *github.Release) error {
return nil
}

func (f *FakeGitHubWithURLEncoding) GetRelease(ctx context.Context, tag string) (*github.Release, error) {
return f.release, nil
}

func (f *FakeGitHubWithURLEncoding) CreatePullRequest(owner string, repo string, message string, head string, base string) (string, error) {
return "https://github.com/owner/repo/pull/42", nil
}
Binary file not shown.