Repository: scala-steward-org/scala-steward
Pull request #2951 (2023-01-30 14:17:50)

Simplify ProcessAlg
This simplifies `ProcessAlg` by removing the `NoSandbox` and `WithFirejail` classes in favor of the new `ProcessAlg#toArgs` and `ProcessAlg#toSandboxArgs` methods. These two show that the difference between normal and sandboxed execution is just how `Args` is constructed.


Patch:
diff --git a/modules/core/src/main/resources/default.scala-steward.conf b/modules/core/src/main/resources/default.scala-steward.conf
index ca266d87..1ca054ba 100644
--- a/modules/core/src/main/resources/default.scala-steward.conf
+++ b/modules/core/src/main/resources/default.scala-steward.conf
@@ -29,9 +29,9 @@ postUpdateHooks = [
 
 updates.ignore = [
   // Ignore the next Scala 3 version until it is announced.
-  { groupId = "org.scala-lang", artifactId = "scala3-compiler",     version = { exact = "3.3.0" } },
-  { groupId = "org.scala-lang", artifactId = "scala3-library",      version = { exact = "3.3.0" } },
-  { groupId = "org.scala-lang", artifactId = "scala3-library_sjs1", version = { exact = "3.3.0" } },
+  { groupId = "org.scala-lang", artifactId = "scala3-compiler",     version = { exact = "3.2.2" } },
+  { groupId = "org.scala-lang", artifactId = "scala3-library",      version = { exact = "3.2.2" } },
+  { groupId = "org.scala-lang", artifactId = "scala3-library_sjs1", version = { exact = "3.2.2" } },
 
   // Ignore the next Scala 2.13 version until it is announced.
   { groupId = "org.scala-lang", artifactId = "scala-compiler", version = { exact = "2.13.11" } },
diff --git a/modules/core/src/main/scala/org/scalasteward/core/io/ProcessAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/io/ProcessAlg.scala
index 5fdd7606..1d7ac2f8 100644
--- a/modules/core/src/main/scala/org/scalasteward/core/io/ProcessAlg.scala
+++ b/modules/core/src/main/scala/org/scalasteward/core/io/ProcessAlg.scala
@@ -24,13 +24,14 @@ import org.scalasteward.core.io.process.{Args, SlurpOption, SlurpOptions}
 import org.scalasteward.core.util.Nel
 import org.typelevel.log4cats.Logger
 
-trait ProcessAlg[F[_]] {
+final class ProcessAlg[F[_]](config: ProcessCfg)(execImpl: Args => F[List[String]]) {
   def exec(
       command: Nel[String],
       workingDirectory: File,
       extraEnv: List[(String, String)] = Nil,
       slurpOptions: SlurpOptions = Set.empty
-  ): F[List[String]]
+  ): F[List[String]] =
+    execImpl(toArgs(command, workingDirectory, extraEnv, slurpOptions))
 
   def execSandboxed(
       command: Nel[String],
@@ -38,9 +39,9 @@ trait ProcessAlg[F[_]] {
       extraEnv: List[(String, String)] = Nil,
       slurpOptions: SlurpOptions = Set.empty
   ): F[List[String]] =
-    exec(command, workingDirectory, extraEnv, slurpOptions)
+    execImpl(toSandboxArgs(command, workingDirectory, extraEnv, slurpOptions))
 
-  final def execMaybeSandboxed(sandboxed: Boolean)(
+  def execMaybeSandboxed(sandboxed: Boolean)(
       command: Nel[String],
       workingDirectory: File,
       extraEnv: List[(String, String)] = Nil,
@@ -48,30 +49,24 @@ trait ProcessAlg[F[_]] {
   ): F[List[String]] =
     if (sandboxed) execSandboxed(command, workingDirectory, extraEnv, slurpOptions)
     else exec(command, workingDirectory, extraEnv, slurpOptions)
-}
 
-object ProcessAlg {
-  private class NoSandbox[F[_]](config: ProcessCfg)(execImpl: Args => F[List[String]])
-      extends ProcessAlg[F] {
-    val configEnv: List[(String, String)] = config.envVars.map(v => (v.name, v.value))
+  private val configEnv: List[(String, String)] = config.envVars.map(v => (v.name, v.value))
 
-    override def exec(
-        command: Nel[String],
-        workingDirectory: File,
-        extraEnv: List[(String, String)],
-        slurpOptions: SlurpOptions
-    ): F[List[String]] =
-      execImpl(Args(command, Some(workingDirectory), extraEnv ++ configEnv, slurpOptions))
-  }
+  private def toArgs(
+      command: Nel[String],
+      workingDirectory: File,
+      extraEnv: List[(String, String)],
+      slurpOptions: SlurpOptions
+  ): Args =
+    Args(command, Some(workingDirectory), extraEnv ++ configEnv, slurpOptions)
 
-  private class WithFirejail[F[_]](config: ProcessCfg)(execImpl: Args => F[List[String]])
-      extends NoSandbox[F](config)(execImpl) {
-    override def execSandboxed(
-        command: Nel[String],
-        workingDirectory: File,
-        extraEnv: List[(String, String)],
-        slurpOptions: SlurpOptions
-    ): F[List[String]] = {
+  private def toSandboxArgs(
+      command: Nel[String],
+      workingDirectory: File,
+      extraEnv: List[(String, String)],
+      slurpOptions: SlurpOptions
+  ): Args =
+    if (config.sandboxCfg.enableSandbox) {
       val whitelisted = (workingDirectory.toString :: config.sandboxCfg.whitelistedDirectories)
         .map(dir => s"--whitelist=$dir")
       val readOnly = config.sandboxCfg.readOnlyDirectories
@@ -79,27 +74,20 @@ object ProcessAlg {
       val envVars = (extraEnv ++ configEnv)
         .map { case (k, v) => s"--env=$k=$v" }
       val firejail = Nel("firejail", "--quiet" :: whitelisted ++ readOnly ++ envVars) ::: command
-      val args = Args(
+      Args(
         command = firejail,
         workingDirectory = Some(workingDirectory),
         slurpOptions = slurpOptions ++ Set(SlurpOption.ClearEnvironment)
       )
-      execImpl(args)
+    } else {
+      toArgs(command, workingDirectory, extraEnv, slurpOptions)
     }
-  }
-
-  def fromExecImpl[F[_]](config: ProcessCfg)(execImpl: Args => F[List[String]]): ProcessAlg[F] =
-    if (config.sandboxCfg.enableSandbox)
-      new WithFirejail[F](config)(execImpl)
-    else
-      new NoSandbox[F](config)(execImpl)
+}
 
-  def create[F[_]](config: ProcessCfg)(implicit
-      logger: Logger[F],
-      F: Async[F]
-  ): ProcessAlg[F] =
-    fromExecImpl(config) { args =>
+object ProcessAlg {
+  def create[F[_]](config: ProcessCfg)(implicit logger: Logger[F], F: Async[F]): ProcessAlg[F] =
+    new ProcessAlg(config)({ args =>
       logger.debug(s"Execute ${process.showCmd(args)}") >>
         process.slurp(args, config.processTimeout, config.maxBufferSize, logger.trace(_))
-    }
+    })
 }


Test patch:
diff --git a/modules/core/src/test/scala/org/scalasteward/core/io/MockProcessAlg.scala b/modules/core/src/test/scala/org/scalasteward/core/io/MockProcessAlg.scala
index b066a22f..13af6533 100644
--- a/modules/core/src/test/scala/org/scalasteward/core/io/MockProcessAlg.scala
+++ b/modules/core/src/test/scala/org/scalasteward/core/io/MockProcessAlg.scala
@@ -6,7 +6,7 @@ import org.scalasteward.core.mock.MockEff
 
 object MockProcessAlg {
   def create(config: ProcessCfg): ProcessAlg[MockEff] =
-    ProcessAlg.fromExecImpl(config) { args =>
+    new ProcessAlg(config)({ args =>
       Kleisli {
         _.modify { s =>
           val cmd = args.workingDirectory.map(_.toString).toList ++ args.command.toList
@@ -15,5 +15,5 @@ object MockProcessAlg {
           (s1, a)
         }
       }
-    }
+    })
 }