pull/31678/merge
Oleg Trenin 2 days ago committed by GitHub
commit 865e722000
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -21,6 +21,7 @@ import (
stdfs "io/fs"
"log/slog"
"os"
"os/exec"
"sort"
"github.com/Masterminds/semver/v3"
@ -95,12 +96,66 @@ func (i *VCSInstaller) Install() error {
return fs.CopyDir(i.Repo.LocalPath(), i.Path())
}
// resetPluginYaml discards local modifications to plugin.yaml file.
// This is used to clean the cached repository before updating.
// plugin.yaml is the only file that Helm modifies during installation.
func resetPluginYaml(repo vcs.Repo) error {
pluginYaml := "plugin.yaml"
// Check the VCS type to determine the appropriate reset command
switch repo.Vcs() {
case vcs.Git:
// For Git, use 'git checkout -- plugin.yaml' to discard changes to this file
cmd := exec.Command("git", "checkout", "--", pluginYaml)
cmd.Dir = repo.LocalPath()
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("git checkout failed: %w, output: %s", err, output)
}
return nil
case vcs.Hg:
// For Mercurial, use 'hg revert --no-backup plugin.yaml'
cmd := exec.Command("hg", "revert", "--no-backup", pluginYaml)
cmd.Dir = repo.LocalPath()
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("hg revert failed: %w, output: %s", err, output)
}
return nil
case vcs.Bzr:
// For Bazaar, use 'bzr revert plugin.yaml'
cmd := exec.Command("bzr", "revert", pluginYaml)
cmd.Dir = repo.LocalPath()
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("bzr revert failed: %w, output: %s", err, output)
}
return nil
case vcs.Svn:
// For SVN, use 'svn revert plugin.yaml'
cmd := exec.Command("svn", "revert", pluginYaml)
cmd.Dir = repo.LocalPath()
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("svn revert failed: %w, output: %s", err, output)
}
return nil
default:
return fmt.Errorf("unsupported VCS type: %v", repo.Vcs())
}
}
// Update updates a remote repository
func (i *VCSInstaller) Update() error {
slog.Debug("updating", "source", i.Repo.Remote())
// Reset plugin.yaml if it was modified by Helm during installation.
// The cached repository is managed by Helm and should not contain user modifications.
// plugin.yaml is the only file that Helm modifies during installation,
// so we only need to reset this specific file.
if i.Repo.IsDirty() {
return errors.New("plugin repo was modified")
slog.Debug("resetting plugin.yaml in cache", "path", i.Repo.LocalPath())
if err := resetPluginYaml(i.Repo); err != nil {
return fmt.Errorf("failed to reset plugin.yaml: %w", err)
}
}
if err := i.Repo.Update(); err != nil {
return err
}

@ -175,15 +175,88 @@ func TestVCSInstallerUpdate(t *testing.T) {
t.Fatal(err)
}
// Test update failure
if err := os.Remove(filepath.Join(vcsInstaller.Repo.LocalPath(), "plugin.yaml")); err != nil {
// Test that local modifications are automatically reset during update
// Remove plugin.yaml to simulate modifications
pluginYamlPath := filepath.Join(vcsInstaller.Repo.LocalPath(), "plugin.yaml")
if err := os.Remove(pluginYamlPath); err != nil {
t.Fatal(err)
}
// Testing update for error
if err := Update(vcsInstaller); err == nil {
t.Fatalf("expected error for plugin modified, got none")
} else if err.Error() != "plugin repo was modified" {
t.Fatalf("expected error for plugin modified, got (%v)", err)
// Verify the repo is dirty
if !vcsInstaller.Repo.IsDirty() {
t.Fatal("expected repo to be dirty after removing plugin.yaml")
}
// Update should succeed because local modifications are automatically reset
if err := Update(vcsInstaller); err != nil {
t.Fatalf("update should succeed after automatic reset, got error: %v", err)
}
// Verify plugin.yaml was restored after reset
if _, err := os.Stat(pluginYamlPath); err != nil {
t.Fatalf("plugin.yaml should be restored after update, got error: %v", err)
}
}
func TestResetPluginYaml(t *testing.T) {
// Use a real git repository by cloning a test repo
ensure.HelmHome(t)
source := "https://github.com/adamreese/helm-env"
i, err := NewForSource(source, "")
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
vcsInstaller, ok := i.(*VCSInstaller)
if !ok {
t.Fatal("expected a VCSInstaller")
}
// Clone the repository
if err := vcsInstaller.sync(vcsInstaller.Repo); err != nil {
if strings.Contains(err.Error(), "Could not resolve host: github.com") {
t.Skip("Unable to run test without Internet access")
}
t.Fatalf("Failed to sync repo: %v", err)
}
// Modify plugin.yaml to make the repo dirty
pluginYaml := filepath.Join(vcsInstaller.Repo.LocalPath(), "plugin.yaml")
originalContent, err := os.ReadFile(pluginYaml)
if err != nil {
t.Fatalf("Failed to read plugin.yaml: %v", err)
}
modifiedContent := append(originalContent, []byte("\n# Test modification\n")...)
if err := os.WriteFile(pluginYaml, modifiedContent, 0644); err != nil {
t.Fatalf("Failed to modify plugin.yaml: %v", err)
}
// Verify the repo is dirty
if !vcsInstaller.Repo.IsDirty() {
t.Fatal("Expected repo to be dirty after modifying plugin.yaml")
}
// Reset only plugin.yaml
if err := resetPluginYaml(vcsInstaller.Repo); err != nil {
t.Fatalf("resetPluginYaml failed: %v", err)
}
// Verify the repo is clean
if vcsInstaller.Repo.IsDirty() {
t.Fatal("Expected repo to be clean after resetting plugin.yaml")
}
// Verify the plugin.yaml was restored to original content
restoredContent, err := os.ReadFile(pluginYaml)
if err != nil {
t.Fatalf("Failed to read plugin.yaml after reset: %v", err)
}
if string(restoredContent) != string(originalContent) {
t.Fatal("Expected plugin.yaml to be restored to original content after reset")
}
}

Loading…
Cancel
Save