From 83e4244c4ba8c093bbd5acebf90eb795c1466580 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 10 Mar 2026 21:12:54 +0800 Subject: [PATCH 1/5] fix(remote): fall back to set-url when remote already exists - Fall back to git remote set-url when git remote add fails - Add RemoteSetURL function for updating existing remote URLs - Add test for RemoteSetURL command arguments Co-Authored-By: Claude Opus 4.6 --- plugin.go | 6 +++++- repo/remote.go | 13 +++++++++++++ repo/remote_test.go | 15 +++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/plugin.go b/plugin.go index 700af06..821e7ec 100644 --- a/plugin.go +++ b/plugin.go @@ -148,10 +148,14 @@ func (p Plugin) WriteToken() error { } // HandleRemote adds the git remote if required. +// If the remote already exists, it updates the URL instead. func (p Plugin) HandleRemote(ctx context.Context) error { if p.Config.Remote != "" { if err := execute(repo.RemoteAdd(ctx, p.Config.RemoteName, p.Config.Remote)); err != nil { - return err + // If remote already exists, update its URL instead. + if err := execute(repo.RemoteSetURL(ctx, p.Config.RemoteName, p.Config.Remote)); err != nil { + return err + } } } diff --git a/repo/remote.go b/repo/remote.go index caae8a6..5b14e11 100644 --- a/repo/remote.go +++ b/repo/remote.go @@ -31,6 +31,19 @@ func RemoteAdd(ctx context.Context, name, url string) *exec.Cmd { return cmd } +// RemoteSetURL updates the URL of an existing remote in a git repo. +func RemoteSetURL(ctx context.Context, name, url string) *exec.Cmd { + cmd := exec.CommandContext( + ctx, + "git", + "remote", + "set-url", + name, + url) + + return cmd +} + // RemotePush pushs the changes from the local head to a remote branch.. func RemotePush(ctx context.Context, remote, branch string, force, followtags bool) *exec.Cmd { return RemotePushNamedBranch(ctx, remote, "HEAD", branch, force, followtags) diff --git a/repo/remote_test.go b/repo/remote_test.go index 395de8f..dcc1c6b 100644 --- a/repo/remote_test.go +++ b/repo/remote_test.go @@ -1,6 +1,7 @@ package repo import ( + "context" "testing" ) @@ -45,3 +46,17 @@ func TestSanitizeInput(t *testing.T) { }) } } + +func TestRemoteSetURL(t *testing.T) { + cmd := RemoteSetURL(context.Background(), "origin", "git@github.com:user/repo.git") + args := cmd.Args + expected := []string{"git", "remote", "set-url", "origin", "git@github.com:user/repo.git"} + if len(args) != len(expected) { + t.Fatalf("expected %d args, got %d", len(expected), len(args)) + } + for i, arg := range args { + if arg != expected[i] { + t.Errorf("arg[%d] = %q, want %q", i, arg, expected[i]) + } + } +} From 51088c230a911c951207beb374024bbf838f30a2 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 10 Mar 2026 21:20:51 +0800 Subject: [PATCH 2/5] fix(remote): check remote existence before add or set-url - Add RemoteExists helper to check if a named remote already exists - Use RemoteExists in HandleRemote to choose between add and set-url - Add integration test for HandleRemote with pre-existing remote Co-Authored-By: Claude Opus 4.6 --- plugin.go | 7 +++++-- plugin_test.go | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ repo/remote.go | 11 ++++++++++ 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/plugin.go b/plugin.go index 821e7ec..96f54a9 100644 --- a/plugin.go +++ b/plugin.go @@ -151,11 +151,14 @@ func (p Plugin) WriteToken() error { // If the remote already exists, it updates the URL instead. func (p Plugin) HandleRemote(ctx context.Context) error { if p.Config.Remote != "" { - if err := execute(repo.RemoteAdd(ctx, p.Config.RemoteName, p.Config.Remote)); err != nil { - // If remote already exists, update its URL instead. + if repo.RemoteExists(ctx, p.Config.RemoteName) { if err := execute(repo.RemoteSetURL(ctx, p.Config.RemoteName, p.Config.Remote)); err != nil { return err } + } else { + if err := execute(repo.RemoteAdd(ctx, p.Config.RemoteName, p.Config.Remote)); err != nil { + return err + } } } diff --git a/plugin_test.go b/plugin_test.go index fe59c0c..955271f 100644 --- a/plugin_test.go +++ b/plugin_test.go @@ -2,6 +2,8 @@ package main import ( "context" + "os" + "os/exec" "testing" ) @@ -40,3 +42,57 @@ func TestPlugin_HandleRemote(t *testing.T) { }) } } + +func TestPlugin_HandleRemote_ExistingRemote(t *testing.T) { + // Create a temporary git repo + tmpDir, err := os.MkdirTemp("", "drone-git-push-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) + + // Save current dir and change to temp dir + origDir, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + defer os.Chdir(origDir) + + if err := os.Chdir(tmpDir); err != nil { + t.Fatal(err) + } + + // Initialize a git repo and add a remote + cmds := [][]string{ + {"git", "init"}, + {"git", "remote", "add", "origin", "git@github.com:old/repo.git"}, + } + for _, args := range cmds { + cmd := exec.Command(args[0], args[1:]...) + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("command %v failed: %s, %v", args, out, err) + } + } + + // HandleRemote should succeed even though "origin" already exists + p := Plugin{ + Config: Config{ + RemoteName: "origin", + Remote: "git@github.com:new/repo.git", + }, + } + if err := p.HandleRemote(context.Background()); err != nil { + t.Errorf("HandleRemote() with existing remote should not fail, got: %v", err) + } + + // Verify the remote URL was updated + out, err := exec.Command("git", "remote", "get-url", "origin").Output() + if err != nil { + t.Fatal(err) + } + got := string(out) + want := "git@github.com:new/repo.git\n" + if got != want { + t.Errorf("remote URL = %q, want %q", got, want) + } +} diff --git a/repo/remote.go b/repo/remote.go index 5b14e11..28c6a83 100644 --- a/repo/remote.go +++ b/repo/remote.go @@ -18,6 +18,17 @@ func RemoteRemove(ctx context.Context, name string) *exec.Cmd { return cmd } +// RemoteExists checks if a named remote already exists in a git repo. +func RemoteExists(ctx context.Context, name string) bool { + cmd := exec.CommandContext( + ctx, + "git", + "remote", + "get-url", + name) + return cmd.Run() == nil +} + // RemoteAdd adds an additional remote to a git repo. func RemoteAdd(ctx context.Context, name, url string) *exec.Cmd { cmd := exec.CommandContext( From bb6cbb135b13566876a4d609e3b8689b9be83205 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 10 Mar 2026 21:38:52 +0800 Subject: [PATCH 3/5] fix(test): resolve lint errors in HandleRemote test - Check os.Chdir error return in defer to satisfy errcheck - Use exec.CommandContext instead of exec.Command to satisfy noctx - Add nolint:gosec for test-only git commands with static args Co-Authored-By: Claude Opus 4.6 --- plugin_test.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/plugin_test.go b/plugin_test.go index 955271f..544cb22 100644 --- a/plugin_test.go +++ b/plugin_test.go @@ -56,22 +56,21 @@ func TestPlugin_HandleRemote_ExistingRemote(t *testing.T) { if err != nil { t.Fatal(err) } - defer os.Chdir(origDir) + defer func() { + _ = os.Chdir(origDir) + }() if err := os.Chdir(tmpDir); err != nil { t.Fatal(err) } // Initialize a git repo and add a remote - cmds := [][]string{ - {"git", "init"}, - {"git", "remote", "add", "origin", "git@github.com:old/repo.git"}, + ctx := context.Background() + if out, err := exec.CommandContext(ctx, "git", "init").CombinedOutput(); err != nil { //nolint:gosec + t.Fatalf("git init failed: %s, %v", out, err) } - for _, args := range cmds { - cmd := exec.Command(args[0], args[1:]...) - if out, err := cmd.CombinedOutput(); err != nil { - t.Fatalf("command %v failed: %s, %v", args, out, err) - } + if out, err := exec.CommandContext(ctx, "git", "remote", "add", "origin", "git@github.com:old/repo.git").CombinedOutput(); err != nil { //nolint:gosec + t.Fatalf("git remote add failed: %s, %v", out, err) } // HandleRemote should succeed even though "origin" already exists @@ -81,12 +80,12 @@ func TestPlugin_HandleRemote_ExistingRemote(t *testing.T) { Remote: "git@github.com:new/repo.git", }, } - if err := p.HandleRemote(context.Background()); err != nil { + if err := p.HandleRemote(ctx); err != nil { t.Errorf("HandleRemote() with existing remote should not fail, got: %v", err) } // Verify the remote URL was updated - out, err := exec.Command("git", "remote", "get-url", "origin").Output() + out, err := exec.CommandContext(ctx, "git", "remote", "get-url", "origin").Output() //nolint:gosec if err != nil { t.Fatal(err) } From 032caa8808cf8373bcab09b83c6cac00d6246e12 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 10 Mar 2026 21:40:09 +0800 Subject: [PATCH 4/5] style(lint): fix golines and nolintlint issues - Break long lines to satisfy golines linter - Remove unused nolint:gosec directives Co-Authored-By: Claude Opus 4.6 --- plugin.go | 6 ++++-- plugin_test.go | 13 ++++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/plugin.go b/plugin.go index 96f54a9..7a5774d 100644 --- a/plugin.go +++ b/plugin.go @@ -152,11 +152,13 @@ func (p Plugin) WriteToken() error { func (p Plugin) HandleRemote(ctx context.Context) error { if p.Config.Remote != "" { if repo.RemoteExists(ctx, p.Config.RemoteName) { - if err := execute(repo.RemoteSetURL(ctx, p.Config.RemoteName, p.Config.Remote)); err != nil { + cmd := repo.RemoteSetURL(ctx, p.Config.RemoteName, p.Config.Remote) + if err := execute(cmd); err != nil { return err } } else { - if err := execute(repo.RemoteAdd(ctx, p.Config.RemoteName, p.Config.Remote)); err != nil { + cmd := repo.RemoteAdd(ctx, p.Config.RemoteName, p.Config.Remote) + if err := execute(cmd); err != nil { return err } } diff --git a/plugin_test.go b/plugin_test.go index 544cb22..a6b35c9 100644 --- a/plugin_test.go +++ b/plugin_test.go @@ -66,10 +66,15 @@ func TestPlugin_HandleRemote_ExistingRemote(t *testing.T) { // Initialize a git repo and add a remote ctx := context.Background() - if out, err := exec.CommandContext(ctx, "git", "init").CombinedOutput(); err != nil { //nolint:gosec + if out, err := exec.CommandContext( + ctx, "git", "init", + ).CombinedOutput(); err != nil { t.Fatalf("git init failed: %s, %v", out, err) } - if out, err := exec.CommandContext(ctx, "git", "remote", "add", "origin", "git@github.com:old/repo.git").CombinedOutput(); err != nil { //nolint:gosec + if out, err := exec.CommandContext( + ctx, "git", "remote", "add", "origin", + "git@github.com:old/repo.git", + ).CombinedOutput(); err != nil { t.Fatalf("git remote add failed: %s, %v", out, err) } @@ -85,7 +90,9 @@ func TestPlugin_HandleRemote_ExistingRemote(t *testing.T) { } // Verify the remote URL was updated - out, err := exec.CommandContext(ctx, "git", "remote", "get-url", "origin").Output() //nolint:gosec + out, err := exec.CommandContext( + ctx, "git", "remote", "get-url", "origin", + ).Output() if err != nil { t.Fatal(err) } From b5134335927bae3f82f8a7b2bdc6e2bc8a937954 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 10 Mar 2026 21:51:05 +0800 Subject: [PATCH 5/5] ci(testing): upgrade Go container to 1.25 to match go.mod Co-Authored-By: Claude Opus 4.6 --- .github/workflows/testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index add97bb..ce219e9 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -28,7 +28,7 @@ jobs: testing: runs-on: ubuntu-latest - container: golang:1.24-alpine + container: golang:1.25-alpine steps: - name: Checkout repository uses: actions/checkout@v6