Repository: scala-steward-org/scala-steward
Pull request #2074 (2021-04-08 18:07:56)

Bypass all Git hooks by setting hooksPath to /dev/null
Follow-up to #2073.


Patch:
diff --git a/modules/core/src/main/scala/org/scalasteward/core/git/FileGitAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/git/FileGitAlg.scala
index 448e6eee..7b1f00de 100644
--- a/modules/core/src/main/scala/org/scalasteward/core/git/FileGitAlg.scala
+++ b/modules/core/src/main/scala/org/scalasteward/core/git/FileGitAlg.scala
@@ -21,7 +21,7 @@ import cats.effect.MonadCancelThrow
 import cats.syntax.all._
 import org.http4s.Uri
 import org.scalasteward.core.application.Config.GitCfg
-import org.scalasteward.core.git.FileGitAlg.dotdot
+import org.scalasteward.core.git.FileGitAlg.{dotdot, gitCmd}
 import org.scalasteward.core.io.{FileAlg, ProcessAlg, WorkspaceAlg}
 import org.scalasteward.core.util.Nel
 
@@ -110,7 +110,7 @@ final class FileGitAlg[F[_]](config: GitCfg)(implicit
     } yield Option.when(before =!= after)(Commit())
 
   override def push(repo: File, branch: Branch): F[Unit] =
-    git("push", "--force", "--no-verify", "--set-upstream", "origin", branch.name)(repo).void
+    git("push", "--force", "--set-upstream", "origin", branch.name)(repo).void
 
   override def removeClone(repo: File): F[Unit] =
     fileAlg.deleteForce(repo)
@@ -138,14 +138,18 @@ final class FileGitAlg[F[_]](config: GitCfg)(implicit
   override def version: F[String] =
     workspaceAlg.rootDir.flatMap(git("--version")).map(_.mkString.trim)
 
-  private def git(args: String*)(repo: File): F[List[String]] =
-    processAlg.exec(Nel.of("git", args: _*), repo, "GIT_ASKPASS" -> config.gitAskPass.pathAsString)
+  private def git(args: String*)(repo: File): F[List[String]] = {
+    val extraEnv = "GIT_ASKPASS" -> config.gitAskPass.pathAsString
+    processAlg.exec(gitCmd ++ args.toList, repo, extraEnv)
+  }
 
   private val sign: String =
     if (config.signCommits) "--gpg-sign" else "--no-gpg-sign"
 }
 
 object FileGitAlg {
+  val gitCmd: Nel[String] = Nel.of("git", "-c", "core.hooksPath=/dev/null")
+
   // man 7 gitrevisions:
   // When you have two commits r1 and r2 you can ask for commits that are
   // reachable from r2 excluding those that are reachable from r1 by ^r1 r2


Test patch:
diff --git a/modules/core/src/test/scala/org/scalasteward/core/edit/EditAlgTest.scala b/modules/core/src/test/scala/org/scalasteward/core/edit/EditAlgTest.scala
index 5c13654d..de4d04a9 100644
--- a/modules/core/src/test/scala/org/scalasteward/core/edit/EditAlgTest.scala
+++ b/modules/core/src/test/scala/org/scalasteward/core/edit/EditAlgTest.scala
@@ -1,11 +1,12 @@
 package org.scalasteward.core.edit
 
+import better.files.File
 import cats.effect.unsafe.implicits.global
 import munit.FunSuite
 import org.scalasteward.core.TestInstances.dummyRepoCache
 import org.scalasteward.core.TestSyntax._
 import org.scalasteward.core.data.{GroupId, RepoData, Update}
-import org.scalasteward.core.mock.MockConfig.{config, envVars}
+import org.scalasteward.core.mock.MockConfig.{config, gitCmd}
 import org.scalasteward.core.mock.MockContext.context.editAlg
 import org.scalasteward.core.mock.MockState
 import org.scalasteward.core.mock.MockState.TraceEntry.{Cmd, Log}
@@ -15,8 +16,8 @@ import org.scalasteward.core.util.Nel
 import org.scalasteward.core.vcs.data.Repo
 
 class EditAlgTest extends FunSuite {
-  private val gitStatus =
-    List("git", "status", "--porcelain", "--untracked-files=no", "--ignore-submodules")
+  private def gitStatus(repoDir: File): List[String] =
+    gitCmd(repoDir) ++ List("status", "--porcelain", "--untracked-files=no", "--ignore-submodules")
 
   test("applyUpdate") {
     val repo = Repo("edit-alg", "test-1")
@@ -46,7 +47,7 @@ class EditAlgTest extends FunSuite {
         Log("Trying heuristic 'original'"),
         Cmd("read", file1.pathAsString),
         Cmd("write", file1.pathAsString),
-        Cmd(envVars ++ (repoDir.toString :: gitStatus))
+        Cmd(gitStatus(repoDir))
       ),
       files = Map(file1 -> """val catsVersion = "1.3.0"""", file2 -> "")
     )
@@ -94,10 +95,10 @@ class EditAlgTest extends FunSuite {
         Log("Trying heuristic 'specific'"),
         Cmd("read", scalafmtConf.pathAsString),
         Cmd("write", scalafmtConf.pathAsString),
-        Cmd(envVars ++ (repoDir.toString :: gitStatus)),
+        Cmd(gitStatus(repoDir)),
         Log("Executing post-update hook for org.scalameta:scalafmt-core"),
         Cmd("VAR1=val1", "VAR2=val2", repoDir.toString, scalafmtBinary, "--non-interactive"),
-        Cmd(envVars ++ (repoDir.toString :: gitStatus))
+        Cmd(gitStatus(repoDir))
       ),
       files = Map(
         scalafmtConf ->
@@ -140,7 +141,7 @@ class EditAlgTest extends FunSuite {
         Cmd("write", file2.pathAsString),
         Cmd("read", file1.pathAsString),
         Cmd("write", file1.pathAsString),
-        Cmd(envVars ++ (repoDir.toString :: gitStatus))
+        Cmd(gitStatus(repoDir))
       ),
       files = Map(
         file1 -> """import $ivy.`org.typelevel::cats-core:1.3.0`, cats.implicits._"""",
diff --git a/modules/core/src/test/scala/org/scalasteward/core/edit/hooks/HookExecutorTest.scala b/modules/core/src/test/scala/org/scalasteward/core/edit/hooks/HookExecutorTest.scala
index 9fbe4da1..37f20417 100644
--- a/modules/core/src/test/scala/org/scalasteward/core/edit/hooks/HookExecutorTest.scala
+++ b/modules/core/src/test/scala/org/scalasteward/core/edit/hooks/HookExecutorTest.scala
@@ -1,11 +1,11 @@
 package org.scalasteward.core.edit.hooks
 
-import cats.effect.unsafe.implicits.global
-import munit.FunSuite
+import munit.CatsEffectSuite
 import org.scalasteward.core.TestInstances.dummyRepoCache
 import org.scalasteward.core.TestSyntax._
 import org.scalasteward.core.data.{RepoData, Update}
-import org.scalasteward.core.mock.MockConfig.envVars
+import org.scalasteward.core.git.FileGitAlg
+import org.scalasteward.core.mock.MockConfig.gitCmd
 import org.scalasteward.core.mock.MockContext.context.{hookExecutor, workspaceAlg}
 import org.scalasteward.core.mock.MockState
 import org.scalasteward.core.mock.MockState.TraceEntry.{Cmd, Log}
@@ -14,17 +14,15 @@ import org.scalasteward.core.scalafmt.{scalafmtArtifactId, scalafmtBinary, scala
 import org.scalasteward.core.util.Nel
 import org.scalasteward.core.vcs.data.Repo
 
-class HookExecutorTest extends FunSuite {
+class HookExecutorTest extends CatsEffectSuite {
   private val repo = Repo("scala-steward-org", "scala-steward")
   private val data = RepoData(repo, dummyRepoCache, RepoConfig.empty)
   private val repoDir = workspaceAlg.repoDir(repo).runA(MockState.empty).unsafeRunSync()
 
   test("no hook") {
-
     val update = Update.Single("org.typelevel" % "cats-core" % "1.2.0", Nel.of("1.3.0"))
-    val state = hookExecutor.execPostUpdateHooks(data, update).runS(MockState.empty).unsafeRunSync()
-
-    assertEquals(state, MockState.empty)
+    val state = hookExecutor.execPostUpdateHooks(data, update).runS(MockState.empty)
+    state.map(assertEquals(_, MockState.empty))
   }
 
   test("scalafmt: enabled by config") {
@@ -32,29 +30,26 @@ class HookExecutorTest extends FunSuite {
       Update.Single(scalafmtGroupId.value % scalafmtArtifactId % "2.7.4", Nel.of("2.7.5"))
     val initial = MockState.empty.copy(commandOutputs =
       Map(
-        List("git", "status", "--porcelain", "--untracked-files=no", "--ignore-submodules") ->
+        FileGitAlg.gitCmd.toList ++
+          List("status", "--porcelain", "--untracked-files=no", "--ignore-submodules") ->
           List("build.sbt")
       )
     )
-    val state = hookExecutor.execPostUpdateHooks(data, update).runS(initial).unsafeRunSync()
+    val state = hookExecutor.execPostUpdateHooks(data, update).runS(initial)
 
     val expected = initial.copy(
       trace = Vector(
         Log("Executing post-update hook for org.scalameta:scalafmt-core"),
         Cmd("VAR1=val1", "VAR2=val2", repoDir.toString, scalafmtBinary, "--non-interactive"),
         Cmd(
-          envVars,
-          repoDir.toString,
-          "git",
+          gitCmd(repoDir),
           "status",
           "--porcelain",
           "--untracked-files=no",
           "--ignore-submodules"
         ),
         Cmd(
-          envVars,
-          repoDir.toString,
-          "git",
+          gitCmd(repoDir),
           "commit",
           "--all",
           "--no-gpg-sign",
@@ -64,7 +59,7 @@ class HookExecutorTest extends FunSuite {
       )
     )
 
-    assertEquals(state, expected)
+    state.map(assertEquals(_, expected))
   }
 
   test("scalafmt: disabled by config") {
@@ -73,14 +68,14 @@ class HookExecutorTest extends FunSuite {
     val data = RepoData(repo, dummyRepoCache, repoConfig)
     val update =
       Update.Single(scalafmtGroupId.value % scalafmtArtifactId % "2.7.4", Nel.of("2.7.5"))
-    val state = hookExecutor.execPostUpdateHooks(data, update).runS(MockState.empty).unsafeRunSync()
+    val state = hookExecutor.execPostUpdateHooks(data, update).runS(MockState.empty)
 
-    assertEquals(state, MockState.empty)
+    state.map(assertEquals(_, MockState.empty))
   }
 
   test("sbt-github-actions") {
     val update = Update.Single("com.codecommit" % "sbt-github-actions" % "0.9.4", Nel.of("0.9.5"))
-    val state = hookExecutor.execPostUpdateHooks(data, update).runS(MockState.empty).unsafeRunSync()
+    val state = hookExecutor.execPostUpdateHooks(data, update).runS(MockState.empty)
 
     val expected = MockState.empty.copy(
       trace = Vector(
@@ -95,18 +90,10 @@ class HookExecutorTest extends FunSuite {
           "sbt",
           "githubWorkflowGenerate"
         ),
-        Cmd(
-          envVars,
-          repoDir.toString,
-          "git",
-          "status",
-          "--porcelain",
-          "--untracked-files=no",
-          "--ignore-submodules"
-        )
+        Cmd(gitCmd(repoDir), "status", "--porcelain", "--untracked-files=no", "--ignore-submodules")
       )
     )
 
-    assertEquals(state, expected)
+    state.map(assertEquals(_, expected))
   }
 }
diff --git a/modules/core/src/test/scala/org/scalasteward/core/mock/MockConfig.scala b/modules/core/src/test/scala/org/scalasteward/core/mock/MockConfig.scala
index 5e1c228a..d3226bff 100644
--- a/modules/core/src/test/scala/org/scalasteward/core/mock/MockConfig.scala
+++ b/modules/core/src/test/scala/org/scalasteward/core/mock/MockConfig.scala
@@ -4,6 +4,7 @@ import better.files.File
 import org.http4s.Uri
 import org.scalasteward.core.application.Cli.EnvVar
 import org.scalasteward.core.application.{Cli, Config}
+import org.scalasteward.core.git.FileGitAlg
 import org.scalasteward.core.vcs.VCSType
 import org.scalasteward.core.vcs.data.AuthenticatedUser
 
@@ -27,5 +28,7 @@ object MockConfig {
   )
   val config: Config = Config.from(args)
   val envVars = List(s"GIT_ASKPASS=${config.gitCfg.gitAskPass}", "VAR1=val1", "VAR2=val2")
+  def gitCmd(repoDir: File): List[String] =
+    envVars ++ (repoDir.toString :: FileGitAlg.gitCmd.toList)
   val user: AuthenticatedUser = AuthenticatedUser("scala-steward", "token")
 }
diff --git a/modules/core/src/test/scala/org/scalasteward/core/vcs/VCSRepoAlgTest.scala b/modules/core/src/test/scala/org/scalasteward/core/vcs/VCSRepoAlgTest.scala
index 7c3f9d33..a5a1344c 100644
--- a/modules/core/src/test/scala/org/scalasteward/core/vcs/VCSRepoAlgTest.scala
+++ b/modules/core/src/test/scala/org/scalasteward/core/vcs/VCSRepoAlgTest.scala
@@ -1,24 +1,23 @@
 package org.scalasteward.core.vcs
 
-import cats.effect.unsafe.implicits.global
-import munit.FunSuite
+import munit.CatsEffectSuite
 import org.http4s.syntax.literals._
 import org.scalasteward.core.git.Branch
-import org.scalasteward.core.mock.MockConfig.{config, envVars}
+import org.scalasteward.core.mock.MockConfig.{config, gitCmd}
 import org.scalasteward.core.mock.MockContext.context.{gitAlg, logger, vcsRepoAlg}
 import org.scalasteward.core.mock.MockState.TraceEntry.{Cmd, Log}
 import org.scalasteward.core.mock.{MockConfig, MockEff, MockState}
 import org.scalasteward.core.vcs.data.{Repo, RepoOut, UserOut}
 
-class VCSRepoAlgTest extends FunSuite {
+class VCSRepoAlgTest extends CatsEffectSuite {
   private val repo = Repo("fthomas", "datapackage")
-  private val repoDir = (config.workspace / "fthomas/datapackage").toString
+  private val repoDir = config.workspace / repo.show
   private val parentRepoOut = RepoOut(
     "datapackage",
     UserOut("fthomas"),
     None,
     uri"https://github.com/fthomas/datapackage",
-    Branch("master")
+    Branch("main")
   )
 
   private val forkRepoOut = RepoOut(
@@ -26,40 +25,30 @@ class VCSRepoAlgTest extends FunSuite {
     UserOut("scala-steward"),
     Some(parentRepoOut),
     uri"https://github.com/scala-steward/datapackage",
-    Branch("master")
+    Branch("main")
   )
 
   private val parentUrl = s"https://${config.vcsLogin}@github.com/fthomas/datapackage"
   private val forkUrl = s"https://${config.vcsLogin}@github.com/scala-steward/datapackage"
 
   test("cloneAndSync: doNotFork = false") {
-    val state = vcsRepoAlg.cloneAndSync(repo, forkRepoOut).runS(MockState.empty).unsafeRunSync()
+    val state = vcsRepoAlg.cloneAndSync(repo, forkRepoOut).runS(MockState.empty)
     val expected = MockState.empty.copy(
       trace = Vector(
         Log("Clone scala-steward/datapackage"),
-        Cmd(envVars, config.workspace.toString, "git", "clone", forkUrl, repoDir),
-        Cmd(envVars, repoDir, "git", "config", "user.email", "bot@example.org"),
-        Cmd(envVars, repoDir, "git", "config", "user.name", "Bot Doe"),
+        Cmd(gitCmd(config.workspace), "clone", forkUrl, repoDir.toString),
+        Cmd(gitCmd(repoDir), "config", "user.email", "bot@example.org"),
+        Cmd(gitCmd(repoDir), "config", "user.name", "Bot Doe"),
         Log("Synchronize with fthomas/datapackage"),
-        Cmd(envVars, repoDir, "git", "remote", "add", "upstream", parentUrl),
-        Cmd(envVars, repoDir, "git", "fetch", "--force", "--tags", "upstream", "master"),
-        Cmd(envVars, repoDir, "git", "checkout", "-B", "master", "--track", "upstream/master"),
-        Cmd(envVars, repoDir, "git", "merge", "upstream/master"),
-        Cmd(
-          envVars,
-          repoDir,
-          "git",
-          "push",
-          "--force",
-          "--no-verify",
-          "--set-upstream",
-          "origin",
-          "master"
-        ),
-        Cmd(envVars, repoDir, "git", "submodule", "update", "--init", "--recursive")
+        Cmd(gitCmd(repoDir), "remote", "add", "upstream", parentUrl),
+        Cmd(gitCmd(repoDir), "fetch", "--force", "--tags", "upstream", "main"),
+        Cmd(gitCmd(repoDir), "checkout", "-B", "main", "--track", "upstream/main"),
+        Cmd(gitCmd(repoDir), "merge", "upstream/main"),
+        Cmd(gitCmd(repoDir), "push", "--force", "--set-upstream", "origin", "main"),
+        Cmd(gitCmd(repoDir), "submodule", "update", "--init", "--recursive")
       )
     )
-    assertEquals(state, expected)
+    state.map(assertEquals(_, expected))
   }
 
   test("cloneAndSync: doNotFork = true") {
@@ -67,25 +56,24 @@ class VCSRepoAlgTest extends FunSuite {
     val state = new VCSRepoAlg[MockEff](config)
       .cloneAndSync(repo, parentRepoOut)
       .runS(MockState.empty)
-      .unsafeRunSync()
+
     val expected = MockState.empty.copy(
       trace = Vector(
         Log("Clone fthomas/datapackage"),
-        Cmd(envVars, config.workspace.toString, "git", "clone", parentUrl, repoDir),
-        Cmd(envVars, repoDir, "git", "config", "user.email", "bot@example.org"),
-        Cmd(envVars, repoDir, "git", "config", "user.name", "Bot Doe"),
-        Cmd(envVars, repoDir, "git", "submodule", "update", "--init", "--recursive")
+        Cmd(gitCmd(config.workspace), "clone", parentUrl, repoDir.toString),
+        Cmd(gitCmd(repoDir), "config", "user.email", "bot@example.org"),
+        Cmd(gitCmd(repoDir), "config", "user.name", "Bot Doe"),
+        Cmd(gitCmd(repoDir), "submodule", "update", "--init", "--recursive")
       )
     )
-    assertEquals(state, expected)
+    state.map(assertEquals(_, expected))
   }
 
   test("cloneAndSync: doNotFork = false, no parent") {
-    val result = vcsRepoAlg
+    vcsRepoAlg
       .cloneAndSync(repo, parentRepoOut)
       .runS(MockState.empty)
       .attempt
-      .unsafeRunSync()
-    assert(clue(result).isLeft)
+      .map(result => assert(clue(result).isLeft))
   }
 }