diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 028b25f9..8abdfd80 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,9 +8,9 @@ on: # The `test` job runs the unit suites only. The integration suites gated on # `ORCA_INTEGRATION=1` need an authenticated `claude`/`gh` and stay skipped — -# except `BuiltInFlowsCompileTest`, which needs only scala-cli and gets its own -# `flow-scripts` job, with the env var scoped to that one `testOnly`. Run the -# rest locally with `ORCA_INTEGRATION=1 sbt testFull`. +# except `BuiltInFlowsCompileTest` and `CompileProbeTest`, which need only +# scala-cli and get their own `flow-scripts` job, with the env var scoped to +# that one `testOnly`. Run the rest locally with `ORCA_INTEGRATION=1 sbt testFull`. # # Publish runs inline (rather than reusing # softwaremill/github-actions-workflows) because `secrets: inherit` only @@ -60,7 +60,7 @@ jobs: - uses: sbt/setup-sbt@v1 - uses: coursier/cache-action@v6 - run: | - sbt "publishLocal ; shell/testOnly *BuiltInFlowsCompileTest" | tee sbt.log + sbt "publishLocal ; shell/testOnly *BuiltInFlowsCompileTest *CompileProbeTest" | tee sbt.log if grep "Couldn't resolve a member" sbt.log; then echo "::error::Unresolved scaladoc link(s), see above" exit 1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 14aaee75..c4433b56 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -53,15 +53,16 @@ the tests won't see `ORCA_INTEGRATION=1`. | `OsGitHubIntegrationTest` | `gh` authenticated | | `BuiltInFlowsCompileTest`, `FlowAuthoringSmokeTest` | `scala-cli`, and a `publishLocal` in the same sbt invocation | | `ScalaCliSmokeTest` | the above, plus `claude` authenticated — it starts a real flow | +| `CompileProbeTest` | `scala-cli` | -The three scala-cli suites link a script against the local Ivy cache, so they +The other three scala-cli suites link a script against the local Ivy cache, so they need `publishLocal` in the *same* sbt invocation: the scripts pin that run's dynver version (runner's suite reads it from `-Dorca.build.version`, shell's from the version resource the build generates). -`BuiltInFlowsCompileTest` is the only one CI runs (its own `flow-scripts` job), -being the only one needing no credentials. It compiles every built-in flow as -`BuiltInFlows` stages them for a snapshot build — pin rewritten to the +CI runs `BuiltInFlowsCompileTest` and `CompileProbeTest` in its own +`flow-scripts` job. The former compiles +every built-in flow as `BuiltInFlows` stages them for a snapshot build — pin rewritten to the just-published version, `//> using repository ivy2Local` inserted — so an API change that breaks the flows fails CI instead of shipping. On a release version there is no rewrite (the flows resolve from Maven Central), so it skips itself. diff --git a/shell/src/main/scala/orca/shell/run/FlowLauncher.scala b/shell/src/main/scala/orca/shell/run/FlowLauncher.scala index bc05394f..e38fe1d0 100644 --- a/shell/src/main/scala/orca/shell/run/FlowLauncher.scala +++ b/shell/src/main/scala/orca/shell/run/FlowLauncher.scala @@ -70,10 +70,7 @@ private[shell] object FlowLauncher: private def depArgs(forced: Option[OrcaBuild]): Seq[String] = forced.toSeq.flatMap(_.forceArgs) - /** Tells the flow child its [[FlowSource]] ([[FlowSourceProperty]]). On the - * compile probe too, since scala-cli rebuilds when a `--java-prop` value - * changes. - */ + /** Tells the flow child its [[FlowSource]] ([[FlowSourceProperty]]). */ private def flowSourceArgs(source: FlowSource): Seq[String] = Seq("--java-prop", FlowSourceProperty.assignment(source)) @@ -110,7 +107,8 @@ private[shell] object FlowLauncher: /** The compile probe's argv — same `--workspace` treatment as [[argv]], and * for the same reason: without it, the probe (run whenever the forced - * version fails) would write its own `.scala-build` next to `flow` too. + * version fails) would write its own `.scala-build` next to `flow` too. No + * [[flowSourceArgs]]: `scala-cli compile` rejects `--java-prop`. */ private[run] def compileArgv( flow: LaunchedFlow, @@ -121,8 +119,7 @@ private[shell] object FlowLauncher: "scala-cli", "compile", flow.path.toString - ) ++ forced.forceArgs ++ flowSourceArgs(flow.source) ++ - Seq("--workspace", workspaceDir.toString) + ) ++ forced.forceArgs ++ Seq("--workspace", workspaceDir.toString) /** What to do once the forced run has finished. */ enum NextAction: diff --git a/shell/src/test/scala/orca/shell/run/CompileProbeTest.scala b/shell/src/test/scala/orca/shell/run/CompileProbeTest.scala new file mode 100644 index 00000000..0db25c9a --- /dev/null +++ b/shell/src/test/scala/orca/shell/run/CompileProbeTest.scala @@ -0,0 +1,32 @@ +package orca.shell.run + +import orca.progress.FlowSource +import orca.shell.OrcaBuild +import orca.testkit.TempDirs + +/** Runs [[FlowLauncher.compileArgv]] through a real `scala-cli`, so a flag + * `scala-cli compile` rejects fails here instead of turning every flow failure + * into a version-pin fallback offer. Pins a published release, so no + * `publishLocal` is needed. Gated on `ORCA_INTEGRATION` (network + a real + * scala-cli on PATH); skipped otherwise. + */ +class CompileProbeTest extends munit.FunSuite: + + override def munitTests(): Seq[Test] = + if sys.env.contains("ORCA_INTEGRATION") then super.munitTests() + else Nil + + override def munitTimeout: scala.concurrent.duration.Duration = + import scala.concurrent.duration.DurationInt + 5.minutes + + test("compileArgv compiles a valid flow"): + val dir = TempDirs.dir() + val script = dir / "flow.sc" + os.write(script, "println(\"ok\")\n") + val flow = LaunchedFlow(script, FlowSource.Catalog("flow.sc")) + val argv = + FlowLauncher.compileArgv(flow, OrcaBuild.Release("0.2.1"), dir) + val result = + os.proc(argv).call(cwd = dir, check = false, mergeErrIntoOut = true) + assertEquals(result.exitCode, 0, result.out.text()) diff --git a/shell/src/test/scala/orca/shell/run/FlowLauncherTest.scala b/shell/src/test/scala/orca/shell/run/FlowLauncherTest.scala index c5d8be90..5de3ffe3 100644 --- a/shell/src/test/scala/orca/shell/run/FlowLauncherTest.scala +++ b/shell/src/test/scala/orca/shell/run/FlowLauncherTest.scala @@ -119,12 +119,6 @@ class FlowLauncherTest extends munit.FunSuite: ) ) - test("compileArgv passes the same flow source as argv"): - // scala-cli rebuilds when a --java-prop value changes. - val compile = - FlowLauncher.compileArgv(flow, OrcaBuild.Release("0.0.18"), workspaceDir) - assert(compile.containsSlice(Seq("--java-prop", sourceProp)), compile) - test("resolveNextAction: a zero exit is Succeed, without the compile probe"): val probeCalls = new java.util.concurrent.atomic.AtomicInteger(0) val result =