Repository: JuliaRegistries/CompatHelper.jl (julia)

Issue:
The "second push" (`force_ci_trigger()`) incorrectly pushes to the HTTPS remote, and as a result, Github Actions CI is not started for CompatHelper PRs
We are using `CompatHelper.jl` for our flow solver project [Trixi.jl](https://github.com/trixi-framework/Trixi.jl). However, since some time, our CI workflow does not start anymore for PRs opened by github-actions bot triggered by CompatHelper. 

We are using the workflow file linked on https://github.com/JuliaRegistries/CompatHelper.jl. We also use Documenter.jl and have `DOCUMENTER_KEY` set up as suggested. Our current workaround is to just close and reopen the PR, which then triggers CI. We also tried with a new Documenter key, which however did not help.

Do have any idea what could be in the way?

Pull request:
CompatHelper checks for the existence of an SSH key to determine an appropriate git remote URL:

https://github.com/JuliaRegistries/CompatHelper.jl/blob/e72bd6b815a69d14ccdb0c85b3589bed2a2af704/src/utilities/new_versions.jl#L254-L260

However, the `repo_git_url` variable generated here is not used later on. This means that if an SSH key is provided (via `COMPATHELPER_PRIV`) it is not actually used, because the remote URL is set as https here

https://github.com/JuliaRegistries/CompatHelper.jl/blob/e72bd6b815a69d14ccdb0c85b3589bed2a2af704/src/dependencies.jl#L6-L9

and never updated.

This PR fixes this by creating a new remote (by default called `compathelper-remote`) and calling `git remote add` before performing the rest of the git commands. If the remote already exists, we call `git remote set-url`. (This doesn't happen in a real run, but it does happen in the test suite.)

I do feel like ideally we'd use the ssh key and the url to clone the repo at the very start, but that requires reworking a fair bit more plumbing and I didn't want to shake it up too much. (For what it's worth, Documenter.jl uses the same approach as in the PR, i.e. adding a remote with the right URL.)

Since the signature of `force_ci_trigger` is changed, I also bumped the major version.

Fixes #500; I've tested this with my own fork and can confirm that this version of CompatHelper will generate PRs that trigger CI runs.

Patch:
diff --git a/Project.toml b/Project.toml
index e3c325d2..181322a3 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
 name = "CompatHelper"
 uuid = "aa819f21-2bde-4658-8897-bab36330d9b7"
 authors = ["Dilum Aluthge", "Brown Center for Biomedical Informatics", "contributors"]
-version = "3.11.0"
+version = "3.12.0"
 
 [deps]
 Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
diff --git a/src/utilities/git.jl b/src/utilities/git.jl
index abd8ceec..4329e4a0 100644
--- a/src/utilities/git.jl
+++ b/src/utilities/git.jl
@@ -89,3 +89,14 @@ function git_get_master_branch(master_branch::DefaultBranch)
     return string(strip(read(`git rev-parse --abbrev-ref HEAD`, String)))
 end
 git_get_master_branch(master_branch::AbstractString) = master_branch
+
+function remote_exists(remote_name::AbstractString)
+    return remote_name in split(strip(read(`git remote`, String)), '\n')
+end
+
+function git_remote_add_or_seturl(remote_name::AbstractString, url::AbstractString)
+    git_remote_subcommand = remote_exists(remote_name) ? "set-url" : "add"
+    run(`git remote $git_remote_subcommand $remote_name $url`)
+
+    return nothing
+end
diff --git a/src/utilities/new_versions.jl b/src/utilities/new_versions.jl
index 664aa0af..270d7779 100644
--- a/src/utilities/new_versions.jl
+++ b/src/utilities/new_versions.jl
@@ -205,6 +205,8 @@ function continue_with_pr(dep::DepInfo, bump_compat_containing_equality_specifie
     return true
 end
 
+const COMPATHELPER_SSH_REMOTE_NAME = "compathelper-ssh-remote"
+
 function make_pr_for_new_version(
     forge::Forge,
     repo::Union{GitHub.Repo,GitLab.Project},
@@ -286,6 +288,8 @@ function make_pr_for_new_version(
         if commit_was_success
             @info("Commit was a success")
             api_retry() do
+                # For the first push, we intentionally push to the HTTPS remote (`origin`),
+                # because we want to avoid triggering duplicate CI runs.
                 @mock git_push(
                     "origin", new_branch_name, pkey_filename; force=true, env=env
                 )
@@ -297,6 +301,12 @@ function make_pr_for_new_version(
 
             options.cc_user && cc_mention_user(forge, repo, new_pr; env=env)
             options.unsub_from_prs && unsub_from_pr(forge, new_pr)
+
+            # If we have an SSH key, we need to create a new remote (or update
+            # it) with the appropriate URL (ssh/https) so that force_ci_trigger()
+            # can use it
+            git_remote_add_or_seturl(COMPATHELPER_SSH_REMOTE_NAME, repo_git_url)
+
             force_ci_trigger(forge, new_pr_title, new_branch_name, pkey_filename; env=env)
 
             # Return to the master branch
@@ -345,7 +355,7 @@ function force_ci_trigger(
         # Force push the changes to trigger the PR
         api_retry() do
             @debug "force_ci_trigger: force-pushing the changes to trigger CI on the PR"
-            @mock git_push("origin", branch_name, pkey_filename; force=true, env=env)
+            @mock git_push(COMPATHELPER_SSH_REMOTE_NAME, branch_name, pkey_filename; force=true, env=env)
         end
     end
 


Test patch:
diff --git a/test/utilities/git.jl b/test/utilities/git.jl
index 100f1167..a5acf8f5 100644
--- a/test/utilities/git.jl
+++ b/test/utilities/git.jl
@@ -362,3 +362,48 @@ end
         end
     end
 end
+
+@testset "remote_exists" begin
+    mktempdir() do f
+        cd(f) do
+            run(`git init`)
+            run(`git remote add origin foo-url`)
+            run(`git remote add upstream bar-url`)
+
+            @testset "remote exists" begin
+                @test CompatHelper.remote_exists("origin")
+                @test CompatHelper.remote_exists("upstream")
+            end
+            @testset "remote does not exist" begin
+                @test !CompatHelper.remote_exists("nonexistent")
+            end
+        end
+    end
+end
+
+@testset "git_remote_add_or_seturl" begin
+    mktempdir() do f
+        cd(f) do
+            run(`git init`)
+            run(`git remote add origin foo-url`)
+
+            @testset "set existing remote" begin
+                url = "bar-url"
+                CompatHelper.git_remote_add_or_seturl("origin", url)
+                output = strip(read(`git remote get-url origin`, String))
+                @test output == url
+
+                url2 = "bar-url2"
+                CompatHelper.git_remote_add_or_seturl("origin", url2)
+                output = strip(read(`git remote get-url origin`, String))
+                @test output == url2
+            end
+            @testset "add new remote" begin
+                url = "baz-url"
+                CompatHelper.git_remote_add_or_seturl("upstream", url)
+                output = strip(read(`git remote get-url upstream`, String))
+                @test output == url
+            end
+        end
+    end
+end