diff --git a/.scalafmt.conf b/.scalafmt.conf index 40cebc8..86d4209 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -4,6 +4,6 @@ maxColumn = 120 align.preset = more rewrite.trailingCommas.style = multiple rewrite.scala3.removeOptionalBraces = yes -rewrite.scala3.insertEndMarkerMinLines = 10 -rewrite.scala3.removeEndMarkerMaxLines = 5 +rewrite.scala3.endMarker.insert.blankGaps.min = 1 +rewrite.scala3.endMarker.remove.blankGaps.max = 0 rewrite.scala3.convertToNewSyntax = yes diff --git a/docs/src/test/scala/zipx/docs/BuildSite.scala b/docs/src/test/scala/zipx/docs/BuildSite.scala index 74ac429..ced226a 100644 --- a/docs/src/test/scala/zipx/docs/BuildSite.scala +++ b/docs/src/test/scala/zipx/docs/BuildSite.scala @@ -110,7 +110,6 @@ sbt zipxWorkflowGenerate""", ) ), ) - end site override def layers: ZLayer[Any, Nothing, SiteBuilder] = EarlyEffectTheme.layers @@ -162,8 +161,6 @@ sbt zipxWorkflowGenerate""", .findFirst() if found.isPresent then Some(found.get.nn) else None finally stream.close() - end if - end walkTargetOut private def repoRoot: Path = Iterator diff --git a/docs/src/test/scala/zipx/docs/DocDiff.scala b/docs/src/test/scala/zipx/docs/DocDiff.scala index 433542e..1024aa5 100644 --- a/docs/src/test/scala/zipx/docs/DocDiff.scala +++ b/docs/src/test/scala/zipx/docs/DocDiff.scala @@ -28,7 +28,6 @@ object DocDiff: val kids: Seq[Arg[Any]] = (prefix ++ body.toList).map(ui => Arg.ChildArg(ui)) E.div(Styles.Panel, kids) - end panel /** Several file hunks as one pull request. */ def stack(panels: UI[Any]*): UI[Any] = diff --git a/modules/agent/src/main/scala/zipx/agent/AgentDaemon.scala b/modules/agent/src/main/scala/zipx/agent/AgentDaemon.scala index e95a516..3373776 100644 --- a/modules/agent/src/main/scala/zipx/agent/AgentDaemon.scala +++ b/modules/agent/src/main/scala/zipx/agent/AgentDaemon.scala @@ -67,7 +67,6 @@ object AgentDaemon: iptables <- one(pins, Feed, IptablesId).flatMap(validateApt) suite <- one(pins, Feed, SuiteId).flatMap(validateSuite) yield DaemonPins(cli, fuse, iptables, suite) - end resolve def versions(body: String, packageName: String): List[String] = body.split("\n\n", -1).toList.flatMap { stanza => diff --git a/modules/agent/src/main/scala/zipx/agent/AgentFeeds.scala b/modules/agent/src/main/scala/zipx/agent/AgentFeeds.scala index 113ea93..db556d1 100644 --- a/modules/agent/src/main/scala/zipx/agent/AgentFeeds.scala +++ b/modules/agent/src/main/scala/zipx/agent/AgentFeeds.scala @@ -35,7 +35,6 @@ object AgentFeeds: val wantDaemon = sbtImage && (docker || pins.exists(_.feed == AgentDaemon.Feed)) if !wantDaemon || ubuntu.exists(_.name == AgentDaemon.Feed) then ubuntu else ubuntu :+ AgentDaemon.feed(pins) - end withUser val base: PinFeed = PinFeed( name = AgentPins.BaseFeed, diff --git a/modules/agent/src/main/scala/zipx/agent/AgentPins.scala b/modules/agent/src/main/scala/zipx/agent/AgentPins.scala index a097392..596ed1f 100644 --- a/modules/agent/src/main/scala/zipx/agent/AgentPins.scala +++ b/modules/agent/src/main/scala/zipx/agent/AgentPins.scala @@ -54,7 +54,6 @@ object AgentPins: docker <- one(pins, DockerFeed, DockerId).flatMap(validateDocker) runner <- one(pins, SbtFeed, SbtId).flatMap(validateRunner) yield AgentPins(base, docker, runner) - end resolve private def one(pins: Seq[Pin], feed: PinFeedName, id: String): Either[String, String] = pins.collectFirst { case pin if pin.feed == feed && pin.id == id => pin.current } match diff --git a/modules/agent/src/main/scala/zipx/agent/CursorEnv.scala b/modules/agent/src/main/scala/zipx/agent/CursorEnv.scala index d4df849..dd739f7 100644 --- a/modules/agent/src/main/scala/zipx/agent/CursorEnv.scala +++ b/modules/agent/src/main/scala/zipx/agent/CursorEnv.scala @@ -86,7 +86,6 @@ object CursorEnv: case None => Some(""" "start": "sudo service docker start"""") val fields = (image.toList ++ build.toList) ++ List(s""" "install": "bash $InstallRel"""") ++ start.toList "{\n" + fields.mkString(",\n") + "\n}\n" - end environmentJson def dockerfile(env: AgentEnv): Either[String, Option[String]] = env.toolchain match @@ -120,7 +119,6 @@ object CursorEnv: |USER ubuntu |WORKDIR /home/ubuntu |""".stripMargin - end ubuntuDockerfile private def sbtDockerfile(image: AgentImage, docker: Boolean): Either[String, Option[String]] = if !docker then @@ -220,7 +218,6 @@ object CursorEnv: | > /usr/local/bin/zipx-dockerd; \\ | chmod 0755 /usr/local/bin/zipx-dockerd |""".stripMargin - end daemonRun private def dockerSetup(version: String): String = s"""RUN set -eux; \\ diff --git a/modules/agent/src/main/scala/zipx/agent/DebianVersion.scala b/modules/agent/src/main/scala/zipx/agent/DebianVersion.scala index 155a457..1bd416e 100644 --- a/modules/agent/src/main/scala/zipx/agent/DebianVersion.scala +++ b/modules/agent/src/main/scala/zipx/agent/DebianVersion.scala @@ -38,7 +38,6 @@ object DebianVersion: if dig != 0 then dig else if lNext >= left.length && rNext >= right.length then 0 else compareAt(left, right, lNext, rNext) - end compareAt /** Non-digit run. `~` sorts before the end of the part. Letters sort before other characters. */ private def nonDigits(left: String, right: String, li: Int, ri: Int): (Int, Int, Int) = @@ -51,7 +50,6 @@ object DebianVersion: val cmp = Integer.compare(weight(lc), weight(rc)) if cmp != 0 then (li, ri, cmp) else nonDigits(left, right, li + step(lc), ri + step(rc)) - end nonDigits private def digits(raw: String, from: Int): (String, Int) = def end(i: Int): Int = diff --git a/modules/agent/src/main/scala/zipx/agent/SbtImageFeeds.scala b/modules/agent/src/main/scala/zipx/agent/SbtImageFeeds.scala index 900f195..40cbc91 100644 --- a/modules/agent/src/main/scala/zipx/agent/SbtImageFeeds.scala +++ b/modules/agent/src/main/scala/zipx/agent/SbtImageFeeds.scala @@ -107,7 +107,6 @@ object SbtImageFeeds: case Some(more) => go(more.group(1), left - 1, seen + url, acc ++ names) } go(s"$Tags" + s"eclipse-temurin-$major", 10, Set.empty, Nil) - end tagNames private def manifestDigest(tag: String): Either[String, String] = for diff --git a/modules/agent/src/main/scala/zipx/agent/ZipxAgentEnv.scala b/modules/agent/src/main/scala/zipx/agent/ZipxAgentEnv.scala index 7105b56..f88f668 100644 --- a/modules/agent/src/main/scala/zipx/agent/ZipxAgentEnv.scala +++ b/modules/agent/src/main/scala/zipx/agent/ZipxAgentEnv.scala @@ -80,5 +80,4 @@ object ZipxAgentEnv: else Left(s"zipx: secret-backed env name '$name' is not a shell identifier") } } - end secretNames end ZipxAgentEnv diff --git a/modules/aws/src/main/scala/zipx/aws/ZipxAws.scala b/modules/aws/src/main/scala/zipx/aws/ZipxAws.scala index fa4a2c8..b03f30d 100644 --- a/modules/aws/src/main/scala/zipx/aws/ZipxAws.scala +++ b/modules/aws/src/main/scala/zipx/aws/ZipxAws.scala @@ -128,7 +128,6 @@ object ZipxAws: extraSteps = ecrLoginSteps, condition = condition, ) - end dockerPublish /** [[dockerPublish]] over several registries in one job. One build is pushed, so the registries hold identical bytes. * Build `dockerAliases` with [[EcrImage.taggedAll]] from the same [[EcrRegistry]] values so the two sides cannot @@ -152,6 +151,5 @@ object ZipxAws: condition = condition, ) .withSharedTargets(registryTargets(registries)) - end dockerPublishAll end ZipxAws diff --git a/modules/aws/src/main/scala/zipx/aws/ecr.scala b/modules/aws/src/main/scala/zipx/aws/ecr.scala index e6c9d08..006704c 100644 --- a/modules/aws/src/main/scala/zipx/aws/ecr.scala +++ b/modules/aws/src/main/scala/zipx/aws/ecr.scala @@ -93,6 +93,7 @@ final case class EcrRegistry(accountId: AwsAccountId, region: AwsRegion): def host: String = s"$accountId.dkr.ecr.$region.amazonaws.com" def image(repository: EcrRepository): EcrImage = EcrImage(this, repository) +end EcrRegistry final case class EcrImage(registry: EcrRegistry, repository: EcrRepository): diff --git a/modules/aws/src/test/scala/zipx/aws/ZipxAwsSpec.scala b/modules/aws/src/test/scala/zipx/aws/ZipxAwsSpec.scala index fb513ca..1499535 100644 --- a/modules/aws/src/test/scala/zipx/aws/ZipxAwsSpec.scala +++ b/modules/aws/src/test/scala/zipx/aws/ZipxAwsSpec.scala @@ -223,6 +223,5 @@ object ZipxAwsSpec extends ZIOSpecDefault: (TargetName("dev"), EcrRegistry(AwsAccountId("555566667777"), AwsRegion("us-east-2")), secret"DEV_ROLE"), (TargetName("mirror"), EcrRegistry(AwsAccountId("888899990000"), AwsRegion("eu-central-1")), secret"MIRROR_ROLE"), ) - end sixRegistries end ZipxAwsSpec diff --git a/modules/central/src/main/scala/zipx/github/ZipxGitHubPackages.scala b/modules/central/src/main/scala/zipx/github/ZipxGitHubPackages.scala index c0464e8..053a6ca 100644 --- a/modules/central/src/main/scala/zipx/github/ZipxGitHubPackages.scala +++ b/modules/central/src/main/scala/zipx/github/ZipxGitHubPackages.scala @@ -66,7 +66,6 @@ object ZipxGitHubPackages: condition = condition, extraEnv = extras, ) - end sharedRegistry private def publishCap( name: CapabilityName, @@ -95,6 +94,5 @@ object ZipxGitHubPackages: ) ++ extraEnv, condition = condition, ) - end publishCap end ZipxGitHubPackages diff --git a/modules/central/src/main/scala/zipx/specular/ZipxDocs.scala b/modules/central/src/main/scala/zipx/specular/ZipxDocs.scala index ad11fc8..fb607eb 100644 --- a/modules/central/src/main/scala/zipx/specular/ZipxDocs.scala +++ b/modules/central/src/main/scala/zipx/specular/ZipxDocs.scala @@ -46,6 +46,5 @@ object ZipxDocs: workflowCall = Some(WorkflowCall(uses = ReusableWorkflow, withInputs = inputs)), condition = Some(deployWhen), ) - end pages end ZipxDocs diff --git a/modules/cli/src/main/scala/zipx/cli/Args.scala b/modules/cli/src/main/scala/zipx/cli/Args.scala index bf93970..ec9be1d 100644 --- a/modules/cli/src/main/scala/zipx/cli/Args.scala +++ b/modules/cli/src/main/scala/zipx/cli/Args.scala @@ -39,7 +39,6 @@ object Args: case "--file" :: Nil => Left("zipx: --file needs a path") case unknown :: _ => Left(s"zipx: unknown flag '$unknown'.\n$usage") loop(args, Flags(yes = false, dryRun = false, verifyLoad = false, file = DefaultFile)) - end flags val usage: String = """usage: diff --git a/modules/cli/src/main/scala/zipx/cli/CatalogOps.scala b/modules/cli/src/main/scala/zipx/cli/CatalogOps.scala index bf535be..5e16697 100644 --- a/modules/cli/src/main/scala/zipx/cli/CatalogOps.scala +++ b/modules/cli/src/main/scala/zipx/cli/CatalogOps.scala @@ -41,8 +41,6 @@ object CatalogOps: rendered = ZipxCatalog.renderPlugins(merged) props = after.sbt.map(ZipxCatalog.renderBuildProperties) yield UpdatePlan(deps, acts, holds ++ updates.held.map(_.message), next2, rendered, props) - end for - end planUpdate private def crossingOf(parsed: CatalogConstructors): Either[String, CatalogCrossing] = (parsed.scala, parsed.sbt) match @@ -76,7 +74,6 @@ object CatalogOps: expected = ZipxCatalog.mergePlugins(parsed.plugins, plugins) _ <- ZipxCatalog.checkPlugins(path.toString, Right(got), expected) yield () - end check def formatPlan(plan: UpdatePlan): String = val deps = ZipxCatalog.formatBumps(plan.depBumps) diff --git a/modules/cli/src/main/scala/zipx/cli/LoadVerify.scala b/modules/cli/src/main/scala/zipx/cli/LoadVerify.scala index 4bf00a2..12e26ca 100644 --- a/modules/cli/src/main/scala/zipx/cli/LoadVerify.scala +++ b/modules/cli/src/main/scala/zipx/cli/LoadVerify.scala @@ -25,7 +25,6 @@ object LoadVerify: snap.props match case Some(body) => write(props, body) case None => Files.deleteIfExists(props) - end restore def probe(root: Path, run: Path => Either[String, Unit] = defaultProbe): Either[String, Unit] = run(root) @@ -47,7 +46,6 @@ object LoadVerify: restore(versionsFile, snap) Right(Some(err)) } - end applyWrite private def defaultProbe(root: Path): Either[String, Unit] = try diff --git a/modules/core/src/main/scala/zipx/ZipxVersions.scala b/modules/core/src/main/scala/zipx/ZipxVersions.scala index 0d86c30..d012c10 100644 --- a/modules/core/src/main/scala/zipx/ZipxVersions.scala +++ b/modules/core/src/main/scala/zipx/ZipxVersions.scala @@ -10,6 +10,7 @@ object CatalogContents: final case class Rows(coords: Seq[ZipxCoord], pins: Seq[Pin], actions: Seq[Action], ships: Seq[PublishedRow]) inline given CatalogContents = ${ zipx.core.ZipxCatalog.contentsImpl } +end CatalogContents /** No sbt types, so the CLI compiles a catalog without the target build. Catalogs extend the plugin's `ZipxVersions`. */ diff --git a/modules/core/src/main/scala/zipx/core/ActionPinFile.scala b/modules/core/src/main/scala/zipx/core/ActionPinFile.scala index 9c338e1..5232faa 100644 --- a/modules/core/src/main/scala/zipx/core/ActionPinFile.scala +++ b/modules/core/src/main/scala/zipx/core/ActionPinFile.scala @@ -85,7 +85,6 @@ object ActionPinFile: case Right(_) if !namesAction(ref, field) => refuse(s"pin '$key' must name ${field.prefix}, but this ref is '$ref'") case Right(actionRef) => Right(Entry.FieldPin(field, actionRef, Option(ver).filter(_.nonEmpty))) - end fieldPin if isIgnorable(line) then Right(Entry.Nothing) else @@ -97,7 +96,6 @@ object ActionPinFile: refuse(s"indented, but no '${ActionPins.ExtraPrefix}:' block is open above it") case _ => refuse("not a pin, a # comment, or a blank line; expected 'key: owner/action@ref # vX.Y.Z'") - end if end parseLine private final case class Parsed( diff --git a/modules/core/src/main/scala/zipx/core/ActionPins.scala b/modules/core/src/main/scala/zipx/core/ActionPins.scala index 62a3771..48c1b03 100644 --- a/modules/core/src/main/scala/zipx/core/ActionPins.scala +++ b/modules/core/src/main/scala/zipx/core/ActionPins.scala @@ -111,7 +111,6 @@ object ActionPins: rows.foldLeft[Either[String, ActionPins]](Right(base)) { (accE, action) => accE.flatMap(pins => overlayOne(pins, action)) } - end overlay private def overlayOne(pins: ActionPins, action: Action): Either[String, ActionPins] = action.toRef.flatMap { ref => diff --git a/modules/core/src/main/scala/zipx/core/Advisory.scala b/modules/core/src/main/scala/zipx/core/Advisory.scala index 3081fa7..617e212 100644 --- a/modules/core/src/main/scala/zipx/core/Advisory.scala +++ b/modules/core/src/main/scala/zipx/core/Advisory.scala @@ -8,6 +8,7 @@ enum AdvisorySeverity: case Low, Moderate, High, Critical def rank: Int = ordinal +end AdvisorySeverity object AdvisorySeverity: def parse(raw: String): AdvisorySeverity = @@ -37,7 +38,6 @@ final class OsvAdvisorySource( case Left(err) => Left(s"osv: $err") case Right(res) if res.status == 200 => OsvAdvisorySource.parseResponse(res.body) case Right(res) => Left(s"osv: HTTP ${res.status}") - end advisories end OsvAdvisorySource object OsvAdvisorySource: diff --git a/modules/core/src/main/scala/zipx/core/AdvisoryCheck.scala b/modules/core/src/main/scala/zipx/core/AdvisoryCheck.scala index 7ab5650..e61b56e 100644 --- a/modules/core/src/main/scala/zipx/core/AdvisoryCheck.scala +++ b/modules/core/src/main/scala/zipx/core/AdvisoryCheck.scala @@ -31,7 +31,6 @@ object AdvisoryCheck: githubPurl(name, ver).map(p => (name, ver, p)) } fields ++ extras - end actionQueries def findingMessage( kind: String, diff --git a/modules/core/src/main/scala/zipx/core/Affected.scala b/modules/core/src/main/scala/zipx/core/Affected.scala index c9cf54e..ab000c9 100644 --- a/modules/core/src/main/scala/zipx/core/Affected.scala +++ b/modules/core/src/main/scala/zipx/core/Affected.scala @@ -28,7 +28,6 @@ object Affected: val readSeeds = readFiles.map(byPath) if others.exists(isBuildFile) || readSeeds.exists(_.isEmpty) then graph.ids.toSet else graph.affectedClosure(readSeeds.flatten.flatten.toSet ++ others.flatMap(owningModules(graph, _))) - end affectedModules /** `None` means the diff failed (bad base ref, no git), which fails open to [[AllSentinel]]: an empty list would skip * every Verify job and report green untested. `Some(Nil)` is a successful empty diff and stays empty. @@ -54,7 +53,6 @@ object Affected: else val best = ranked.map(_._2).max ranked.collect { case (id, len) if len == best => id }.toSet - end owningModules private def underBase(path: String, base: String): Boolean = val normalized = if base.endsWith("/") then base.dropRight(1) else base diff --git a/modules/core/src/main/scala/zipx/core/Capability.scala b/modules/core/src/main/scala/zipx/core/Capability.scala index 2f6c468..a327e27 100644 --- a/modules/core/src/main/scala/zipx/core/Capability.scala +++ b/modules/core/src/main/scala/zipx/core/Capability.scala @@ -59,7 +59,6 @@ object TargetGroup extends Subtype[String]: else s"invalid target group '$input': it is a dispatch choice beside target names, so it must start with an ASCII " + "letter or _ and contain only ASCII letters, digits, - or _" -end TargetGroup /** What a capability's `extraSteps` / `postSteps` see. `target` is populated only when the capability fans out * job-per-target. diff --git a/modules/core/src/main/scala/zipx/core/CatalogAuthority.scala b/modules/core/src/main/scala/zipx/core/CatalogAuthority.scala index b1e1b81..c4a0c2b 100644 --- a/modules/core/src/main/scala/zipx/core/CatalogAuthority.scala +++ b/modules/core/src/main/scala/zipx/core/CatalogAuthority.scala @@ -102,6 +102,4 @@ object CatalogConflict: s"Update the catalog: sbt zipxDepUpdate, or pin ${wanted.render}" case _ => s"Pin ${wanted.render} in the catalog." - end match - end fix end CatalogConflict diff --git a/modules/core/src/main/scala/zipx/core/CatalogReleases.scala b/modules/core/src/main/scala/zipx/core/CatalogReleases.scala index 6850db1..487028a 100644 --- a/modules/core/src/main/scala/zipx/core/CatalogReleases.scala +++ b/modules/core/src/main/scala/zipx/core/CatalogReleases.scala @@ -29,7 +29,6 @@ object PublishedNames: ScalaPlatform.values.toList.map(p => s"${lib.artifact}_${p.infix}${crossing.scalaBinary}") case Cross.Full => ScalaPlatform.values.toList.map(p => s"${lib.artifact}_${p.infix}${crossing.scala}") case plugin: Plugin => List(s"${plugin.artifact}_sbt${crossing.sbtBinary}_${crossing.scalaBinary}") -end PublishedNames final case class CatalogUpdates(bumps: List[DepBump], held: List[FamilyHold]) diff --git a/modules/core/src/main/scala/zipx/core/CompanionPr.scala b/modules/core/src/main/scala/zipx/core/CompanionPr.scala index 4dab9db..8feb3a3 100644 --- a/modules/core/src/main/scala/zipx/core/CompanionPr.scala +++ b/modules/core/src/main/scala/zipx/core/CompanionPr.scala @@ -89,7 +89,6 @@ object CompanionPr: ), trailingNewline = true, ) - end open /** The heredoc is unquoted so `GITHUB_RUN_ID` expands. No backticks: they would run as command substitution. */ private inline def workflowRegenLines(inline prefix: String): List[ScriptLine] = @@ -116,7 +115,6 @@ object CompanionPr: ScriptLine.empty, ScriptLine("The PR branch is " + prefix + "-${GITHUB_RUN_ID}."), ) - end workflowRegenLines private inline def runBranch(inline prefix: String): Word.Dquote = Word.dquote(Word.lit(prefix + "-"), Word.vBraced("GITHUB_RUN_ID")) diff --git a/modules/core/src/main/scala/zipx/core/CoverageWorkflow.scala b/modules/core/src/main/scala/zipx/core/CoverageWorkflow.scala index af4a2e5..42b8b77 100644 --- a/modules/core/src/main/scala/zipx/core/CoverageWorkflow.scala +++ b/modules/core/src/main/scala/zipx/core/CoverageWorkflow.scala @@ -13,12 +13,14 @@ enum CoverageTrigger: /** A PR carrying `label`: when that label is added, and on each push or reopen while it stays. */ case PrLabel(label: ExprLiteral) +end CoverageTrigger object CoverageTrigger: inline def prLabel(inline label: String): CoverageTrigger = PrLabel(ExprLiteral(label.trim)) def prLabelMake(label: String): Either[String, CoverageTrigger] = ExprLiteral.make(label.trim).map(PrLabel(_)) +end CoverageTrigger /** scoverage in `zipx-coverage.yml`, so `ci.yml`'s required checks never compile instrumented classes. * @@ -77,7 +79,6 @@ object CoverageWorkflow: config.actions.checkout, config.runnerOs, ) - end plan def render(coverage: CoverageWorkflow, config: PlanConfig): Either[String, String] = Render.render(plan(coverage, config)).map(ActionPinFile.annotateUses(_, config.actions)) diff --git a/modules/core/src/main/scala/zipx/core/DepCleanup.scala b/modules/core/src/main/scala/zipx/core/DepCleanup.scala index b3ad7e4..f796a6b 100644 --- a/modules/core/src/main/scala/zipx/core/DepCleanup.scala +++ b/modules/core/src/main/scala/zipx/core/DepCleanup.scala @@ -48,8 +48,6 @@ final case class DepCleanupReport( s" ${a.selected.valName} (${a.selected.group}:${a.selected.artifact}; family ${a.familyArtifact} ${a.familyRevision})" ) (List(header) ++ drop ++ align).mkString("\n") - end if - end render end DepCleanupReport object DepCleanup: @@ -76,7 +74,6 @@ object DepCleanup: if seen.add(dep) then q.enqueue(dep) } seen.toSet - end closure val tentative = selected.filter { s => closure(selected.filterNot(o => k(o) == k(s))).contains(k(s)) @@ -123,5 +120,4 @@ object DepCleanup: ) } .flatten - end familyCandidate end DepCleanup diff --git a/modules/core/src/main/scala/zipx/core/DeployPlan.scala b/modules/core/src/main/scala/zipx/core/DeployPlan.scala index d98d319..31486d3 100644 --- a/modules/core/src/main/scala/zipx/core/DeployPlan.scala +++ b/modules/core/src/main/scala/zipx/core/DeployPlan.scala @@ -55,6 +55,7 @@ final case class DeployPlan(sha: GitSha, images: List[ModuleId], targets: ListMa val nonEmpty: Map[String, List[String]] = ListMap.from(targets.collect { case (t, ms) if ms.nonEmpty => (t: String) -> ms.map(id => id: String) }) nonEmpty.toJson +end DeployPlan object DeployPlan: diff --git a/modules/core/src/main/scala/zipx/core/DeployRequest.scala b/modules/core/src/main/scala/zipx/core/DeployRequest.scala index 767bfc1..23e4cc7 100644 --- a/modules/core/src/main/scala/zipx/core/DeployRequest.scala +++ b/modules/core/src/main/scala/zipx/core/DeployRequest.scala @@ -28,6 +28,7 @@ enum DeploySelection: /** Nothing, for a reason the resolve job logs. Not a failure: a `no-deploy` merge is supposed to deploy nothing. */ case Skip(reason: String) +end DeploySelection object DeployRequest: @@ -87,8 +88,6 @@ object DeployRequest: ) case (DeployTrigger.Manual(_), _) => Left("zipx: DeployTrigger.Manual deploys only from a dispatch; a push or PR event never runs zipx-deploy.yml") - end match - end select /** The PR's labels, as the resolve job receives them from `toJSON(github.event.pull_request.labels.*.name)`. */ def labelsFromJson(json: String): Either[String, Set[String]] = diff --git a/modules/core/src/main/scala/zipx/core/DeployWorkflow.scala b/modules/core/src/main/scala/zipx/core/DeployWorkflow.scala index 639cc98..6c25900 100644 --- a/modules/core/src/main/scala/zipx/core/DeployWorkflow.scala +++ b/modules/core/src/main/scala/zipx/core/DeployWorkflow.scala @@ -16,6 +16,7 @@ enum DeployStage: /** Also every merge under [[DeployTrigger.Staged]], and every push to a PR carrying its deploy label. */ case PreProduction +end DeployStage /** Where image pushes and deploys run. */ enum DeployTrigger: @@ -180,7 +181,6 @@ object DeployWorkflow: case _ => Nil (perCapability ++ unrecorded ++ clashes ++ reserved ++ mixedStages ++ staged) .map(s"zipx: ${triggerName(trigger)}: " + _) - end problems private def triggerName(trigger: DeployTrigger): String = trigger match case DeployTrigger.OnMerge => "DeployTrigger.OnMerge" @@ -212,7 +212,6 @@ object DeployWorkflow: case ((t, env), rows) => TargetModules(t, env, rows.map(_._3).distinct.sorted) } DeployScope(imagesEnvironment, images, targets) - end scope def targetChoices(graph: ModuleGraph, deploy: List[Capability]): List[String] = val targets = deploy.flatMap(c => graph.nodes.filter(c.participates).flatMap(c.targets)) @@ -292,7 +291,6 @@ object DeployWorkflow: config.actions.checkout, config.runnerOs, ) - end plan def render( graph: ModuleGraph, @@ -382,7 +380,6 @@ object DeployWorkflow: outputs = ListMap(fromPlan(Sha), fromPlan(Images), fromPlan(Targets)), steps = Planner.checkoutThenSbtSetup(config, ResolveJobId, nodeVersion = None, cacheMode(config)) :+ resolve, ) - end resolveJob /** `unsafeMake` because every caller passes one of the relative-path constants above. */ private def path(file: String): Word = Word.Lit(ShText.unsafeMake(file)) diff --git a/modules/core/src/main/scala/zipx/core/FromGraph.scala b/modules/core/src/main/scala/zipx/core/FromGraph.scala index 9ffeac8..8227add 100644 --- a/modules/core/src/main/scala/zipx/core/FromGraph.scala +++ b/modules/core/src/main/scala/zipx/core/FromGraph.scala @@ -40,11 +40,8 @@ object FromGraph: Left( s"zipx: fromGraph '${req.lib.artifact}' found multiple revisions of ${req.lib.group}:$srcName (${many.mkString(", ")})" ) - end match } } - end if - end revisions /** Strips the Scala cross suffix (`_` or `_sjs_`). */ def artifactFamily(resolvedName: String): String = diff --git a/modules/core/src/main/scala/zipx/core/GitHubActionLookup.scala b/modules/core/src/main/scala/zipx/core/GitHubActionLookup.scala index bb1df51..b47e40c 100644 --- a/modules/core/src/main/scala/zipx/core/GitHubActionLookup.scala +++ b/modules/core/src/main/scala/zipx/core/GitHubActionLookup.scala @@ -21,7 +21,6 @@ final class GitHubActionLookup( } } } - end latest private def peel(repo: String, rel: GitHubActionMeta.Release): Either[String, Option[GitHubActionMeta.Release]] = rel.sha.filter(s => GitSha.make(s).isRight) match @@ -53,5 +52,4 @@ object GitHubActionLookup: case Right(res) if res.status == 200 => Right(Option(res.body).getOrElse("")) case Right(res) if res.status == 404 => Right("[]") case Right(res) => Left(s"GitHub $url: HTTP ${res.status}") - end httpGet end GitHubActionLookup diff --git a/modules/core/src/main/scala/zipx/core/GitHubActionMeta.scala b/modules/core/src/main/scala/zipx/core/GitHubActionMeta.scala index 9329c29..9f2d3e5 100644 --- a/modules/core/src/main/scala/zipx/core/GitHubActionMeta.scala +++ b/modules/core/src/main/scala/zipx/core/GitHubActionMeta.scala @@ -25,7 +25,6 @@ object GitHubActionMeta: .map(_.tag_name) .filterNot(isPrereleaseTag) Right(VersionStrategy.npm.latestStable(tags).orElse(tags.headOption).map(tag => Release(tag, None))) - end pickLatestRelease def pickLatestTag(json: String): Either[String, Option[Release]] = val wrapped = if json.trim.startsWith("[") then json else s"[$json]" @@ -41,8 +40,6 @@ object GitHubActionMeta: .orElse(tags.headOption.map(_._1)) .flatMap(latest => tags.find(_._1 == latest).map { case (tag, sha) => Release(tag, sha) }) ) - end match - end pickLatestTag def peelSha(refJson: String, tagObjectJson: Option[String] = None): Either[String, String] = gitObject(refJson).flatMap { obj => @@ -62,7 +59,6 @@ object GitHubActionMeta: case (Some(s), _) => Left(s"git ref is not a 40-hex SHA: $s") case _ => Left("git ref JSON has no object.sha") } - end peelSha private def gitObject(json: String): Either[String, GhGitObject] = json.fromJson[GhGitRef] match diff --git a/modules/core/src/main/scala/zipx/core/HttpLookup.scala b/modules/core/src/main/scala/zipx/core/HttpLookup.scala index 3604168..ec73d8b 100644 --- a/modules/core/src/main/scala/zipx/core/HttpLookup.scala +++ b/modules/core/src/main/scala/zipx/core/HttpLookup.scala @@ -85,8 +85,6 @@ object HttpLookup: Map.empty, ) else HttpLookupResult(404, "", Map.empty) - end if - end readFile /** sbt writes no `maven-metadata.xml` into a `file:` repository, so a version is a directory holding a POM. */ private def publishedVersions(artifactDir: java.nio.file.Path): List[String] = @@ -130,7 +128,6 @@ object HttpLookup: val bare = headers.filter((name, _) => !name.equalsIgnoreCase("Authorization")) fetchGet(next, bare, timeout, None, send, retry, Duration.Zero, None, passRedirects = false) } - end getZio private def fetchGet( url: String, @@ -147,7 +144,6 @@ object HttpLookup: val inm = ifNoneMatch.orElse(revalidating.flatMap(_.etag)).filter(_.nonEmpty) val req = request(url, headers, timeout, body = None, ifNoneMatch = inm) execute(url, req, send, retry, firstJitter, cache, revalidating, passRedirects) - end fetchGet private[core] def postZio( url: String, @@ -160,7 +156,6 @@ object HttpLookup: ): IO[String, HttpLookupResult] = val req = request(url, headers, timeout, body = Some(body), ifNoneMatch = None) execute(url, req, send, retry, firstJitter, cache = None, revalidating = None, passRedirects = false) - end postZio private[core] def jdkSend(req: HttpRequest): Task[HttpLookupResult] = ZIO.attemptBlocking { @@ -197,7 +192,6 @@ object HttpLookup: case LookupFailure.Retryable(msg, _) => msg case LookupFailure.Fatal(msg) => msg } - end execute private def classify( result: HttpLookupResult, @@ -276,7 +270,6 @@ object HttpLookup: body match case Some(bytes) => builder.POST(HttpRequest.BodyPublishers.ofString(bytes)).build() case None => builder.GET().build() - end request private def runEither[A](effect: IO[String, A]): Either[String, A] = try diff --git a/modules/core/src/main/scala/zipx/core/JdkFloor.scala b/modules/core/src/main/scala/zipx/core/JdkFloor.scala index e4f5e1a..80e4d47 100644 --- a/modules/core/src/main/scala/zipx/core/JdkFloor.scala +++ b/modules/core/src/main/scala/zipx/core/JdkFloor.scala @@ -45,6 +45,7 @@ object JdkFloor: def render: String = val body = if numbers.isEmpty then "?" else numbers.mkString(".") build.fold(body)(n => s"$body+$n") + end Release /** `vulnerableUpdate` is the affected update. A later update is safe. The same update is safe with OpenJDK extra * component `openJdkExtra`, or, for Zulu only, a `+build` at least `zuluBuild`. diff --git a/modules/core/src/main/scala/zipx/core/MatrixCollapse.scala b/modules/core/src/main/scala/zipx/core/MatrixCollapse.scala index 09f09c5..789522b 100644 --- a/modules/core/src/main/scala/zipx/core/MatrixCollapse.scala +++ b/modules/core/src/main/scala/zipx/core/MatrixCollapse.scala @@ -60,7 +60,6 @@ object MatrixCollapse: else val targets = nodes.headOption.toList.flatMap(capability.targets) targetsCompatible(targets).isRight && isomorphicMatrixCommands(capability, nodes).isRight - end graphCollapseFeasible private def nearestParticipatingAncestors( node: ModuleNode, @@ -80,7 +79,6 @@ object MatrixCollapse: seen ++ deps, ) go(List(node.id), Nil, Set.empty) - end nearestParticipatingAncestors def underMatrixModule(node: ModuleNode, command: SbtCommand): Either[String, SbtCommand] = val id = node.id @@ -97,7 +95,6 @@ object MatrixCollapse: s"command '${command.text}' is not parametric in module '$id' " + s"(expected a Task scoped to that module)" ) - end underMatrixModule def isomorphicMatrixCommands(capability: Capability, nodes: List[ModuleNode]): Either[String, SbtCommand] = nodes @@ -190,7 +187,5 @@ object MatrixCollapse: t.environment.foreach(e => base += "environment" -> e) t.env.foreach { (k, v) => base += k -> v.render } base.result() - end for - end includeRows end MatrixCollapse diff --git a/modules/core/src/main/scala/zipx/core/ModuleGraph.scala b/modules/core/src/main/scala/zipx/core/ModuleGraph.scala index 61c95f8..d79e36a 100644 --- a/modules/core/src/main/scala/zipx/core/ModuleGraph.scala +++ b/modules/core/src/main/scala/zipx/core/ModuleGraph.scala @@ -152,7 +152,6 @@ final case class ModuleGraph private (nodes: List[ModuleNode], topologicalLayers acc + (id -> (if ancestors.isEmpty then 0 else ancestors.map(acc).max + 1)) } depths.groupBy(_._2).toList.sortBy(_._1).map((_, group) => group.keys.toList.sorted) - end subsetLayers private def nearestAncestors(id: String, included: Set[String]): Set[String] = def go(frontier: List[String], found: Set[String], seen: Set[String]): Set[String] = @@ -204,6 +203,5 @@ object ModuleGraph: ready.foreach(remainingDeps.remove) remainingDeps.mapValuesInPlace((_, deps) => deps -- ready.toSet) Right(layers.toList) - end layersOrCycle end ModuleGraph diff --git a/modules/core/src/main/scala/zipx/core/Modver.scala b/modules/core/src/main/scala/zipx/core/Modver.scala index 221b23c..1817cbd 100644 --- a/modules/core/src/main/scala/zipx/core/Modver.scala +++ b/modules/core/src/main/scala/zipx/core/Modver.scala @@ -146,7 +146,6 @@ object Modver: case None => m } } - end contractedDependents private[core] def propagate( bumps: BumpSet, @@ -158,7 +157,6 @@ object Modver: val seed = bumps.asMap val start = seed.iterator.collect { case (ref, kind) if isMinBump(kind) => ref }.toList BumpSet(walk(seed, start, dependents, inheritTrigger)) - end propagate private def isMinBump(kind: BumpKind): Boolean = kind == BumpKind.Patch || kind == BumpKind.Minor || kind == BumpKind.Major @@ -182,7 +180,6 @@ object Modver: case _ => (m.updated(dep, proposed), dep :: acc) } walk(next, qs ++ extra, dependents, inheritTrigger) - end walk /** An empty file list is an empty set, not all, and `.sbt` / `project/` changes do not expand. */ def dirtyRoots(graph: ModuleGraph, changedFiles: List[String]): Set[ModuleId] = @@ -269,7 +266,6 @@ object Modver: } .headOption missing.toLeft(()) - end uncoveredError def isJsOnly(graph: ModuleGraph, root: ModuleId): Boolean = val rows = graph.nodes.filter(n => n.matrixRoot == root && n.publishes) @@ -335,7 +331,6 @@ object Modver: .map(ks => kinds + (ref -> maxKind(ks))) } } - end minBumps def report( current: ShipIndex, @@ -374,5 +369,4 @@ object Modver: } } .map(ModverReport(_)) - end report end Modver diff --git a/modules/core/src/main/scala/zipx/core/ModverComment.scala b/modules/core/src/main/scala/zipx/core/ModverComment.scala index d978f5e..b792197 100644 --- a/modules/core/src/main/scala/zipx/core/ModverComment.scala +++ b/modules/core/src/main/scala/zipx/core/ModverComment.scala @@ -30,5 +30,4 @@ object ModverComment: |$table |$fence |""".stripMargin - end body end ModverComment diff --git a/modules/core/src/main/scala/zipx/core/PinCheckWorkflow.scala b/modules/core/src/main/scala/zipx/core/PinCheckWorkflow.scala index aae4b73..3cd6a6e 100644 --- a/modules/core/src/main/scala/zipx/core/PinCheckWorkflow.scala +++ b/modules/core/src/main/scala/zipx/core/PinCheckWorkflow.scala @@ -73,7 +73,6 @@ object PinCheckWorkflow: pins.checkout, runnerOs, ) - end plan def render( pins: ActionPins, diff --git a/modules/core/src/main/scala/zipx/core/PinEngine.scala b/modules/core/src/main/scala/zipx/core/PinEngine.scala index bfa2d10..92097f0 100644 --- a/modules/core/src/main/scala/zipx/core/PinEngine.scala +++ b/modules/core/src/main/scala/zipx/core/PinEngine.scala @@ -17,6 +17,7 @@ final case class PinBump(pin: Pin, bump: BumpKind, candidate: PinCandidate): def to: String = candidate.version def applied: AppliedPin = AppliedPin(pin, candidate) +end PinBump final case class PinReport( scanned: Int, @@ -79,7 +80,6 @@ object PinEngine: feed.materialize(item.pin, item.candidate).map(_ => acc :+ item) } } - end materialize def formatBumps(bumps: List[PinBump]): String = if bumps.isEmpty then "no outdated pins" @@ -110,7 +110,6 @@ applied: ${report.applied.size} $findingLines """ - end summary private final case class Accum( scanned: Int, @@ -251,6 +250,5 @@ $findingLines Right(Some(AppliedPin(pin, to))) case _ => Right(None) yield applied.fold(scanned)(a => scanned.copy(applied = scanned.applied :+ a)) - end if } end PinEngine diff --git a/modules/core/src/main/scala/zipx/core/PinFeed.scala b/modules/core/src/main/scala/zipx/core/PinFeed.scala index 29a0ab3..66feedd 100644 --- a/modules/core/src/main/scala/zipx/core/PinFeed.scala +++ b/modules/core/src/main/scala/zipx/core/PinFeed.scala @@ -12,7 +12,6 @@ object PinFeedName extends Subtype[String]: else if input.matches(Names.ActionsId) then true else s"invalid pin feed name '$input': it becomes a snapshot manifest key, so it must start with an ASCII letter or _ and contain only ASCII letters, digits, - or _" -end PinFeedName /** What zipx does when a signal fires for a pin. */ enum PinAction: @@ -29,6 +28,7 @@ enum PreRelease: case Skip, Include def allows(kind: BumpKind): Boolean = this != Skip || kind != BumpKind.PreRelease +end PreRelease /** A Package URL. Pins without one skip OSV and snapshot submit. */ type Purl = Purl.Type diff --git a/modules/core/src/main/scala/zipx/core/PinRewrite.scala b/modules/core/src/main/scala/zipx/core/PinRewrite.scala index 83c46f6..0f4ea63 100644 --- a/modules/core/src/main/scala/zipx/core/PinRewrite.scala +++ b/modules/core/src/main/scala/zipx/core/PinRewrite.scala @@ -23,7 +23,6 @@ object PinRewrite: Left(s"$current is the snapshot pointer, not a pin. Run sbt zipxSnapshotStatus.") case DepRevision.Release(_) | DepRevision.Changing(_) | DepRevision.Other(_) => Left(SnapshotRevisionError.NotCommitPin(current).message) - end advance /** The only way a pin changes lines ([[advance]] stays on its own). A release row becomes a commit pin of `line`. */ def moveTo(current: String, line: ReleaseVersion, pointerSha: GitSha): Either[String, Option[String]] = diff --git a/modules/core/src/main/scala/zipx/core/PinSnapshot.scala b/modules/core/src/main/scala/zipx/core/PinSnapshot.scala index fefb5a9..45d3620 100644 --- a/modules/core/src/main/scala/zipx/core/PinSnapshot.scala +++ b/modules/core/src/main/scala/zipx/core/PinSnapshot.scala @@ -53,7 +53,6 @@ object PinSnapshot: scanned = scanned, manifests = manifests, ).toJson - end render def submit( token: String, diff --git a/modules/core/src/main/scala/zipx/core/PinSnapshotWorkflow.scala b/modules/core/src/main/scala/zipx/core/PinSnapshotWorkflow.scala index 453a195..4efdacd 100644 --- a/modules/core/src/main/scala/zipx/core/PinSnapshotWorkflow.scala +++ b/modules/core/src/main/scala/zipx/core/PinSnapshotWorkflow.scala @@ -35,7 +35,6 @@ object PinSnapshotWorkflow: pins.checkout, runnerOs, ) - end plan def render( pins: ActionPins, diff --git a/modules/core/src/main/scala/zipx/core/Planner.scala b/modules/core/src/main/scala/zipx/core/Planner.scala index 99942c5..de86e11 100644 --- a/modules/core/src/main/scala/zipx/core/Planner.scala +++ b/modules/core/src/main/scala/zipx/core/Planner.scala @@ -59,8 +59,6 @@ object Planner: .sorted if collapsesGraph(mode, capability, graph) then List(capability.name.asJobId) else expanded - end match - end allJobIds /** Same predicate the Graph emitter uses before calling [[graphMatrixJobs]]. */ private def collapsesGraph(mode: MatrixCollapse, capability: Capability, graph: ModuleGraph): Boolean = @@ -133,7 +131,6 @@ object Planner: "Gate.Always (Verify capabilities are affected-gated automatically, Publish and Deploy ones under " + "zipxAffectedPublish / zipxAffectedDeploy) or Gate.OnReleaseTag." ) - end match // `ModuleGraph.cycle` rather than `make`: these are capabilities, so the error has to name them as such. ModuleGraph .cycle(capabilities.map(c => c.name -> c.needsCapabilities).toMap) @@ -182,7 +179,6 @@ object Planner: s"zipx: capability '${capability.name}' withAffectedBy matches no module, so it would run only when the diff " + "cannot narrow anything; name the modules whose changes it tests" ) - end validateAffectedBy private def validateCoverage(capability: Capability): Unit = if capability.name == Capability.TestName then @@ -198,7 +194,6 @@ object Planner: "that test and image jobs restore would hold instrumented classes. Coverage restores the snapshot and " + "never saves it: drop .withLocalCache(LocalCacheMode.Save)." ) - end validateCoverage /** Two owners race each other's entries, and an owner spanning several jobs writes one entry per job: the eviction * [[LocalCacheMode]] exists to stop. @@ -225,7 +220,6 @@ object Planner: ) ) } - end validateLocalCacheOwner private def validateSessionTail(capability: Capability): Unit = capability.sessionTail.foreach { tail => @@ -252,9 +246,7 @@ object Planner: "Use Aggregate / Once", ) case CapabilityScope.Aggregate | CapabilityScope.Once => () - end match } - end validateSessionTail /** Rejects an Aggregate or Layer capability that needs an affected-gated Graph one: `tolerateSkips` would run it * beside a skipped producer, and a joined session cannot drop one module's command. A Graph consumer skips with its @@ -289,7 +281,6 @@ object Planner: s"'${consumer.name}' CapabilityScope.Graph so it skips with its own '$producer' job; make its command " + s"resolve a moving tag that a skipped '$producer' cannot invalidate; or turn $flag off." ) - end for end validateSkipConsumers private def validateWorkflowCall(capability: Capability): Unit = @@ -322,8 +313,6 @@ object Planner: if target.condition.isDefined then refuse(target, "a condition") if target.environment.isDefined then refuse(target, "an environment") } - end if - end validateSharedTargets /** Checked per (capability, target): the gate, capability condition and target condition come from different files, * and only their conjunction is never-true. @@ -439,7 +428,6 @@ object Planner: affectedGatedNames, Pipeline.Ci, ) - end match } val leading = @@ -585,7 +573,6 @@ object Planner: elseDo = Some(Block(setOutput("run", Word.lit("true")))), ), ) - end verifyGateScript private inline def setOutput(inline name: String, value: Word.Quotable): Command = Exec("echo", Word.dquote(Word.lit(name + "="), value)).appendTo(Word.vq("GITHUB_OUTPUT")) @@ -618,7 +605,6 @@ object Planner: Step.run(Script(config.cacheRehydrateTask.render)).named(cacheRehydrateJobId).build ), ) - end cacheRehydrateJob /** One check a ruleset can require. GitHub counts a skipped required check as passing, so this runs under * `!cancelled()` and fails only when a need failed or was cancelled. @@ -675,7 +661,6 @@ object Planner: Expr.group(verifyGateRuns === Expr.quoted("true")) ) (gatedNeeds, andConditions(Some(gateCond.unwrapped), cond)) - end if private def affectedSetupJob(config: PlanConfig, usesVerifyGate: Boolean, runsWhenVerifySkips: Boolean): Job = val (needs, cond) = @@ -697,7 +682,6 @@ object Planner: (if config.affectedOnPush then compute.withEnv("GH_TOKEN", Expr.githubToken) else compute).build ), ) - end affectedSetupJob private def affectedScript(affectedOnPush: Boolean): Script = // Read from a file, not stdout: sbt prints server banners that `modules=$(sbt …)` would put in GITHUB_OUTPUT. @@ -794,7 +778,6 @@ object Planner: pullRequest = Some(PullRequestTrigger()), workflowDispatch = Option.when(config.workflowDispatch)(WorkflowDispatch()), ) - end triggersFor private def mentionsTagRef(condition: JobCondition): Boolean = condition match case JobCondition.RefStartsWith(prefix) => prefix.unwrap.startsWith("refs/tags/") @@ -881,8 +864,6 @@ object Planner: jobSuffix = capability.name.asJobId, ), ) - end match - end onceJob private val syntheticNode = ModuleNode(id = ModuleId("_build")) @@ -1075,8 +1056,6 @@ object Planner: ), ) } - end match - end match end if end aggregateJobs @@ -1165,7 +1144,6 @@ object Planner: matrixAxes = matrixAxes, ), ) - end waveJob fanned match case Nil => @@ -1236,7 +1214,6 @@ object Planner: prev, ) } - end match } end if end layerJobs @@ -1287,8 +1264,6 @@ object Planner: collapse, pipeline, ) - end match - end graphCapabilityJobs private def graphMatrixJobs( capability: Capability, @@ -1520,7 +1495,6 @@ object Planner: // Deploy reads it. val gated = applyVerifyGate(rawNeeds, base, capability.phase, usesVerifyGate)._2 andConditions(gated, JobCondition.renderOpt(capability.condition)) - end condFor val runner = capability.runsOn.getOrElse(List(config.runnerOs)) val module = lit(node.id) @@ -1559,7 +1533,6 @@ object Planner: module = module, ), ) - end baseJob fannedTargets(capability, node) match case Nil => @@ -1584,7 +1557,6 @@ object Planner: target.env, ) } - end match end graphJobsFor /** Prefixed per destination so two registries' `AWS_ROLE_TO_ASSUME` coexist instead of `++` keeping the last. Steps @@ -1699,6 +1671,7 @@ object Planner: /** `resolve` selects the images and each target's modules, and jobs check out and record its `sha`. */ case Deploy(imagesEnvironment: String) + end Pipeline private def selectionJobId(pipeline: Pipeline): JobId = pipeline match case Pipeline.Ci => affectedJobId @@ -1777,7 +1750,6 @@ object Planner: deps.partition(d => graph.get(d).exists(capability.participates)) go(passthrough ++ t, found ++ participating, seen ++ deps) go(List(node.id), Set.empty, Set.empty).toList.sorted - end nearestParticipatingAncestors /** `nodeVersion` is the capability's, not the config's: a Node toolchain is per-suite, so a Scala.js test capability * can ask for one without putting it on every publish job in the build. @@ -1826,8 +1798,6 @@ object Planner: // Local composites need the workspace on disk before `uses: ./.github/actions/…` can resolve. checkout :: ZipxComposites.sbtSetupStep(config, jobSuffix, capability.nodeVersion, cacheMode) :: cache.steps ++ capability.extraSteps(ctx) ++ publish ++ capability.postSteps(ctx) - end match - end stepsFor /** Full-history checkout, then [[ZipxComposites.sbtSetupStep]]. Order is load-bearing for local composites. */ private[core] def checkoutThenSbtSetup( @@ -1878,8 +1848,6 @@ object Planner: Step.run(Script(sbtStep(command))).named(name).build case mode => Step.run(Script(sbtStep(mode.prefixCommand(command)))).named(name).build - end match - end verifyCommandStep /** `test` → `++${{ matrix.scala }}; test`, so a matrixed job's one leg runs under its own Scala version. */ private def underMatrixScala(command: SbtCommand): SbtCommand = diff --git a/modules/core/src/main/scala/zipx/core/PomExclusions.scala b/modules/core/src/main/scala/zipx/core/PomExclusions.scala index ac5d47e..f6e2c67 100644 --- a/modules/core/src/main/scala/zipx/core/PomExclusions.scala +++ b/modules/core/src/main/scala/zipx/core/PomExclusions.scala @@ -8,6 +8,7 @@ enum PomScope: def inherited: Boolean = this match case Compile | Runtime => true case Provided | Optional | Test | Tool => false +end PomScope /** A dependency of one in-repo project on another. */ final case class PomEdge(scope: PomScope, to: ResolvedModule) @@ -39,7 +40,6 @@ object PomExclusions: case Some(_) => Left(DuplicateModule(module)) case None => loop(tail, acc.updated(module, next)) loop(edges, Map.empty) - end graph def of( project: ResolvedModule, @@ -65,5 +65,4 @@ object PomExclusions: .distinct walk(fresh ++ rest, seen ++ fresh) walk(List(project), Set.empty).toList - end reached end PomExclusions diff --git a/modules/core/src/main/scala/zipx/core/Release.scala b/modules/core/src/main/scala/zipx/core/Release.scala index b18be45..92c7df4 100644 --- a/modules/core/src/main/scala/zipx/core/Release.scala +++ b/modules/core/src/main/scala/zipx/core/Release.scala @@ -97,6 +97,7 @@ enum ReleaseRequest: /** Catalog identities, in the order the dispatch named them. Unreleased in-repo upstreams still ride along. */ case Ships(identities: ::[String]) +end ReleaseRequest object ReleaseRequest: @@ -162,6 +163,7 @@ final case class ReleasePlan(entries: ::[ReleaseEntry]): graph.topologicalSort.flatMap(graph.get).collect { case node if node.publishes && catalog.rowFor(node.matrixRoot).exists(rows.contains) => node.id } +end ReleasePlan object ReleasePlan: @@ -194,8 +196,6 @@ object ReleasePlan: case head :: tail => Right(::(head, tail)) case Nil => Left(ReleaseError.NothingToRelease) yield ReleasePlan(entries) - end for - end plan private def inBuildOrder(catalog: ShipIndex, graph: ModuleGraph): List[PublishedRow] = val byOrder = graph.topologicalSort.flatMap(graph.get).flatMap(n => catalog.rowFor(n.matrixRoot)).distinct @@ -246,8 +246,6 @@ object ReleasePlan: case RowStatus.Partial(missing) => Left(ReleaseError.PartiallyReleased(row, missing)) } } - end match - end selected private def unreleased( rows: List[PublishedRow], diff --git a/modules/core/src/main/scala/zipx/core/ReleaseReadiness.scala b/modules/core/src/main/scala/zipx/core/ReleaseReadiness.scala index b22cc4f..8022e61 100644 --- a/modules/core/src/main/scala/zipx/core/ReleaseReadiness.scala +++ b/modules/core/src/main/scala/zipx/core/ReleaseReadiness.scala @@ -46,8 +46,6 @@ object ReleaseReadiness: val refuses = if selected.isEmpty then s"\n\nall refuses, because ${included(blocked.map(_.identity))}." else "" s"Not ready.\n\n$blocks$rest$refuses" - end if - end render /** The paragraph the release job writes after a deployment. */ def shadowSentences(lines: List[ReleaseVersion]): String = @@ -79,7 +77,6 @@ object ReleaseReadiness: if everything then Nil else unreleased.filterNot(ship => ready.exists(_.identity == ship.identity)).map(_.identity) s"$titles\n${rideLines(companions)} gh workflow run zipx-release.yml --ref $branch -f ships=$target\n That is one registry deployment.${stays(leftOut)}" - end clearBody private def readyWhileBlocked(ready: List[ShipGate], chosen: List[ShipGate], branch: String): String = if ready.isEmpty then "" diff --git a/modules/core/src/main/scala/zipx/core/ReleaseWorkflow.scala b/modules/core/src/main/scala/zipx/core/ReleaseWorkflow.scala index 38b3602..b08c363 100644 --- a/modules/core/src/main/scala/zipx/core/ReleaseWorkflow.scala +++ b/modules/core/src/main/scala/zipx/core/ReleaseWorkflow.scala @@ -76,7 +76,6 @@ object ReleaseWorkflow: config.actions.checkout, config.runnerOs, ) - end plan def render( release: ReleaseWorkflow, @@ -112,7 +111,6 @@ object ReleaseWorkflow: .when(tagPushed) .withEnv("ZIPX_DEFAULT_BRANCH", defaultBranch) .build - end onDefaultBranchStep /** Shell, not an expression: an empty string is falsy, so `inputs.ships || github.ref` would turn a cleared field * into the branch ref and release everything. `zipxRelease` refuses the empty string instead. @@ -134,7 +132,6 @@ object ReleaseWorkflow: .named("Release request") .withEnv("ZIPX_SHIPS", Expr.input("ships")) .build - end bindRefStep private val shipsDispatch: WorkflowDispatch = WorkflowDispatch( @@ -196,7 +193,6 @@ object ReleaseWorkflow: ) .named("Open the next snapshot") .build - end openCycleStep private val bumpPrStep: Step = val opened = CompanionPr.open( @@ -216,7 +212,6 @@ object ReleaseWorkflow: .named("Open the bump pull request") .withEnv("GH_TOKEN", Expr.github("token")) .build - end bumpPrStep private def docsJob(docs: Capability, tags: TagScheme): Option[(String, Job)] = val unseenByCi = tags match @@ -233,5 +228,4 @@ object ReleaseWorkflow: `with` = ListMap.from(call.withInputs), ) } - end docsJob end ReleaseWorkflow diff --git a/modules/core/src/main/scala/zipx/core/Satisfiable.scala b/modules/core/src/main/scala/zipx/core/Satisfiable.scala index eb751b2..c61b9e5 100644 --- a/modules/core/src/main/scala/zipx/core/Satisfiable.scala +++ b/modules/core/src/main/scala/zipx/core/Satisfiable.scala @@ -35,6 +35,7 @@ private[core] object Satisfiable: /** `startsWith(github.ref, '')`. */ case RefPrefix(prefix: String) + end Claim /** `!(a || b)` is a conjunction by De Morgan; `!(a && b)` is a disjunction, so it stops here and contributes nothing. */ diff --git a/modules/core/src/main/scala/zipx/core/SbtCommandName.scala b/modules/core/src/main/scala/zipx/core/SbtCommandName.scala index 0b6d9df..f0769d5 100644 --- a/modules/core/src/main/scala/zipx/core/SbtCommandName.scala +++ b/modules/core/src/main/scala/zipx/core/SbtCommandName.scala @@ -14,4 +14,3 @@ object SbtCommandName extends Subtype[String]: else "invalid sbt command name: must start with a letter and contain only letters, digits, - or _ " + "(operator commands use SbtCommand.unsafeBuilt)" -end SbtCommandName diff --git a/modules/core/src/main/scala/zipx/core/SbtCommandText.scala b/modules/core/src/main/scala/zipx/core/SbtCommandText.scala index d2846db..5a347be 100644 --- a/modules/core/src/main/scala/zipx/core/SbtCommandText.scala +++ b/modules/core/src/main/scala/zipx/core/SbtCommandText.scala @@ -17,4 +17,3 @@ object SbtCommandText extends Subtype[String]: else if !input.matches(Patterns.NoControlChars) then "an sbt command must not contain control characters: YAML would quote-escape the whole script" else true -end SbtCommandText diff --git a/modules/core/src/main/scala/zipx/core/SbtImage.scala b/modules/core/src/main/scala/zipx/core/SbtImage.scala index 2929612..b48c287 100644 --- a/modules/core/src/main/scala/zipx/core/SbtImage.scala +++ b/modules/core/src/main/scala/zipx/core/SbtImage.scala @@ -162,7 +162,6 @@ object SbtImage: "GIT_CONFIG_KEY_0" -> "safe.directory", "GIT_CONFIG_VALUE_0" -> "*", ) - end homeEnv def envImageScript(tag: String): String = s"""repo=$$(printf '%s' "$$GITHUB_REPOSITORY" | tr '[:upper:]' '[:lower:]') @@ -248,7 +247,6 @@ object SbtImage: cacheHome = CacheHome, layerEnv = baked.env, ) - end planOf private def imagePin(pins: Seq[Pin]): Either[SbtImageError, String] = pinVersion(pins, ImageId).toRight(SbtImageError.MissingImagePin) diff --git a/modules/core/src/main/scala/zipx/core/SbtImageLayer.scala b/modules/core/src/main/scala/zipx/core/SbtImageLayer.scala index bb675ef..9eb0b69 100644 --- a/modules/core/src/main/scala/zipx/core/SbtImageLayer.scala +++ b/modules/core/src/main/scala/zipx/core/SbtImageLayer.scala @@ -114,8 +114,6 @@ object SbtImageLayer: } runs <- stitch(layer.id, pairs) yield runs - end if - end runsOf private def pair(id: String, index: Int, left: ImageStep, right: ImageStep): Either[SbtImageError, StepPair] = (left, right) match @@ -158,7 +156,6 @@ object SbtImageLayer: case other :: tail => loop(tail, aptDone, script(other) :: acc) val runs = loop(steps, false, Nil) if runs.isEmpty then Left(SbtImageError.EmptyLayer(id)) else Right(runs) - end stitch private def https(id: String, url: String): Either[SbtImageError, Unit] = val bad = @@ -264,7 +261,6 @@ object SbtImageLayer: "rm -rf /var/lib/apt/lists/*", ) ) - end aptScript private def fetch(amd: String, arm: String, dest: String, source: FileSource, mode: FileMode): String = val (choose, url) = archUrl(amd, arm) @@ -300,7 +296,6 @@ object SbtImageLayer: s"rm -rf $file $file.d", ) run(choose ++ place) - end fetch private def extract(amd: String, arm: String, dest: String, format: ArchiveFormat, strip: Int): String = val (choose, url) = archUrl(amd, arm) @@ -317,7 +312,6 @@ object SbtImageLayer: s"rm -f $file", ) ) - end extract /** Lines that set `url`, and the shell word that expands to it. One word when both arches share a URL. */ private def archUrl(amd: String, arm: String): (List[String], String) = @@ -359,6 +353,7 @@ enum FileMode: def bits: String = this match case Executable => "0755" case Readable => "0644" +end FileMode enum ArchiveFormat: case TarGz, TarXz, Zip diff --git a/modules/core/src/main/scala/zipx/core/SbtImageWorkflow.scala b/modules/core/src/main/scala/zipx/core/SbtImageWorkflow.scala index 3eb5226..f5a8c59 100644 --- a/modules/core/src/main/scala/zipx/core/SbtImageWorkflow.scala +++ b/modules/core/src/main/scala/zipx/core/SbtImageWorkflow.scala @@ -60,7 +60,6 @@ object SbtImageWorkflow: None, LocalCacheMode.Restore, ) - end bakedSetup private def runsSbt(job: Job): Boolean = job.uses.isEmpty && job.steps.exists { step => @@ -88,7 +87,6 @@ object SbtImageWorkflow: plan.cacheHome, ), ) - end decorate /** A job that already sets permissions does not inherit the repo token, so a private env image is denied. A job with * no permissions block keeps inheriting that token. diff --git a/modules/core/src/main/scala/zipx/core/SettingDef.scala b/modules/core/src/main/scala/zipx/core/SettingDef.scala index 3452fee..e6c420d 100644 --- a/modules/core/src/main/scala/zipx/core/SettingDef.scala +++ b/modules/core/src/main/scala/zipx/core/SettingDef.scala @@ -38,7 +38,6 @@ object TypeLabel extends Subtype[String]: .replace("zipx.core.", "") .replace("zipx.workflow.", "") functionArrow(stripped) - end shorten /** `Function1[A, B]` → `A => B` for the docs type column. */ private def functionArrow(shown: String): String = @@ -52,7 +51,6 @@ object TypeLabel extends Subtype[String]: case Some((a, b, endExclusive)) => val pretty = s"${a.trim} => ${b.trim}" functionArrow(shown.substring(0, idx) + pretty + shown.substring(endExclusive)) - end functionArrow /** (left, right, index after the closing `]`), split at the top-level comma. */ private def splitFunctionArgs(shown: String, from: Int): Option[(String, String, Int)] = @@ -69,9 +67,7 @@ object TypeLabel extends Subtype[String]: case ',' if depth == 0 && comma < 0 => comma = i case _ => () i += 1 - end while None - end splitFunctionArgs end TypeLabel /** Purpose / `settingKey` description prose shared by the plugin and docs. */ diff --git a/modules/core/src/main/scala/zipx/core/ShipBumpRequest.scala b/modules/core/src/main/scala/zipx/core/ShipBumpRequest.scala index 64daa50..2142041 100644 --- a/modules/core/src/main/scala/zipx/core/ShipBumpRequest.scala +++ b/modules/core/src/main/scala/zipx/core/ShipBumpRequest.scala @@ -33,8 +33,6 @@ object ShipBumpRequest: if identities.contains(id) then kindOf(kind).map(One(id, _)) else Left(ShipBumpError.UnknownIdentity(id, identities.toList)) case more => Left(ShipBumpError.Extra(more)) - end match - end parse end ShipBumpRequest enum ShipBumpError: diff --git a/modules/core/src/main/scala/zipx/core/SnapshotGuard.scala b/modules/core/src/main/scala/zipx/core/SnapshotGuard.scala index f748459..6e44320 100644 --- a/modules/core/src/main/scala/zipx/core/SnapshotGuard.scala +++ b/modules/core/src/main/scala/zipx/core/SnapshotGuard.scala @@ -48,7 +48,6 @@ object SnapshotGuard: case ReleasedDrift.Unreadable(detail) => gate.place(unreadable(row, detail), refusals, warnings, hints) } SnapshotVerdict(refusals, warnings, hints) - end decide extension (gate: DriftGate) private def place( @@ -60,5 +59,4 @@ object SnapshotGuard: gate match case DriftGate.Fail => (refusals :+ sentence, warnings, hints) case DriftGate.Warn => (refusals, warnings :+ sentence, hints) - end extension end SnapshotGuard diff --git a/modules/core/src/main/scala/zipx/core/SnapshotPins.scala b/modules/core/src/main/scala/zipx/core/SnapshotPins.scala index 03fc40d..26142d4 100644 --- a/modules/core/src/main/scala/zipx/core/SnapshotPins.scala +++ b/modules/core/src/main/scala/zipx/core/SnapshotPins.scala @@ -67,7 +67,6 @@ object SnapshotPins: } val trimmed = collapsed.stripPrefix("-").stripSuffix("-") if trimmed.isEmpty then "repo" else trimmed.take(80) - end slug def annotation(pins: ::[ZipxCoord]): Either[String, Steps] = Word diff --git a/modules/core/src/main/scala/zipx/core/SnapshotPointer.scala b/modules/core/src/main/scala/zipx/core/SnapshotPointer.scala index 623100a..2d812bc 100644 --- a/modules/core/src/main/scala/zipx/core/SnapshotPointer.scala +++ b/modules/core/src/main/scala/zipx/core/SnapshotPointer.scala @@ -27,7 +27,6 @@ object SnapshotPointer: val until = xml.indexOf(end, from) if until < 0 then Left(s"pointer POM has no ") else GitSha.make(xml.substring(from, until).trim).left.map(err => s"pointer POM sha: $err") - end shaFromPom /** Maven unique-snapshot metadata: `` and `` under ``. */ def timestampedVersion(line: String, metadata: String): Option[String] = diff --git a/modules/core/src/main/scala/zipx/core/SnapshotStatus.scala b/modules/core/src/main/scala/zipx/core/SnapshotStatus.scala index 13bfaa9..ac66fa1 100644 --- a/modules/core/src/main/scala/zipx/core/SnapshotStatus.scala +++ b/modules/core/src/main/scala/zipx/core/SnapshotStatus.scala @@ -10,6 +10,7 @@ enum PointerRead: /** A plain publish wrote the pointer POM without `zipx.snapshot.sha`. */ case Unnamed +end PointerRead object PointerRead: def of(pom: Option[String]): PointerRead = @@ -26,7 +27,6 @@ object PointerRead: case Unnamed => loop(Unnamed) case Absent => loop(seen) loop(Absent) - end first end PointerRead /** What `zipxSnapshotStatus` prints. The pointer names the latest sha. The command does not rewrite the pin. */ diff --git a/modules/core/src/main/scala/zipx/core/Steps.scala b/modules/core/src/main/scala/zipx/core/Steps.scala index 9e7689c..6f90488 100644 --- a/modules/core/src/main/scala/zipx/core/Steps.scala +++ b/modules/core/src/main/scala/zipx/core/Steps.scala @@ -119,7 +119,6 @@ object Steps: s"$w. zipx validates it as text that cannot corrupt the generated file, but not as sbt syntax, so a typo is a " + "failing job rather than a compile error" ) - end commandWarnings /** `unwrapped`, not `render`: in an `if:`, `${{ a }} && ${{ b }}` is a template string, not the conjunction. */ private def gate(step: Step, condition: JobCondition): Step = diff --git a/modules/core/src/main/scala/zipx/core/TestAffected.scala b/modules/core/src/main/scala/zipx/core/TestAffected.scala index 885664c..9265141 100644 --- a/modules/core/src/main/scala/zipx/core/TestAffected.scala +++ b/modules/core/src/main/scala/zipx/core/TestAffected.scala @@ -20,6 +20,7 @@ object TestAffected: case Modules(command: SbtCommand, modules: ::[ModuleId]) case Nothing + end Run /** @param affected * [[Affected.outputModules]]' answer, where [[Affected.AllSentinel]] means everything. diff --git a/modules/core/src/main/scala/zipx/core/VerifyClean.scala b/modules/core/src/main/scala/zipx/core/VerifyClean.scala index 5fbac7d..0067273 100644 --- a/modules/core/src/main/scala/zipx/core/VerifyClean.scala +++ b/modules/core/src/main/scala/zipx/core/VerifyClean.scala @@ -15,3 +15,4 @@ object VerifyClean: /** A command, not a task, so generate checks the name. */ private val CleanFullCommand: SbtCommand = SbtCommand.unsafeCommand("cleanFull") +end VerifyClean diff --git a/modules/core/src/main/scala/zipx/core/VersionStrategy.scala b/modules/core/src/main/scala/zipx/core/VersionStrategy.scala index c7c6e80..45bdc6e 100644 --- a/modules/core/src/main/scala/zipx/core/VersionStrategy.scala +++ b/modules/core/src/main/scala/zipx/core/VersionStrategy.scala @@ -23,6 +23,7 @@ object VersionStrategy: candidates.lastOption def isPreRelease(version: String): Boolean = false + end ExactStrategy private final case class SemVer(major: Int, minor: Int, patch: Int, pre: Option[String]): def isPre: Boolean = pre.isDefined @@ -60,6 +61,5 @@ object VersionStrategy: minor <- parts(1).toIntOption patch <- parts(2).toIntOption yield SemVer(major, minor, patch, pre.filter(_.nonEmpty)) - end parse end SemverStrategy end VersionStrategy diff --git a/modules/core/src/main/scala/zipx/core/VersionUpdatesWorkflow.scala b/modules/core/src/main/scala/zipx/core/VersionUpdatesWorkflow.scala index aa83a4f..7ebb538 100644 --- a/modules/core/src/main/scala/zipx/core/VersionUpdatesWorkflow.scala +++ b/modules/core/src/main/scala/zipx/core/VersionUpdatesWorkflow.scala @@ -91,7 +91,6 @@ object VersionUpdatesWorkflow: checkout, "ubuntu-latest", ) - end plan def render( pins: ActionPins, diff --git a/modules/core/src/main/scala/zipx/core/ZipxCatalog.scala b/modules/core/src/main/scala/zipx/core/ZipxCatalog.scala index 542f352..f9fdff0 100644 --- a/modules/core/src/main/scala/zipx/core/ZipxCatalog.scala +++ b/modules/core/src/main/scala/zipx/core/ZipxCatalog.scala @@ -126,7 +126,6 @@ object ZipxCatalog: } CatalogUpdates(updates.bumps ++ bump, updates.held ++ held) } - end outdated /** Catalog order, each family's literal first. */ private def familiesOf(coords: Seq[ZipxCoord]): List[(ZipxCoord, List[ZipxCoord])] = @@ -142,7 +141,6 @@ object ZipxCatalog: } literal.orElse(members.headOption).map(lit => lit -> (lit :: members.filterNot(_ == lit))) } - end familiesOf def latest( versions: List[String], @@ -333,7 +331,6 @@ object ZipxCatalog: val actions = actionsOfImpl[a](catalog) val ships = shipsOfImpl[a](catalog) '{ zipx.CatalogContents(() => zipx.CatalogContents.Rows($coords, $pins, $actions, $ships)) } - end contentsImpl private def coordsOfImpl[A: Type](catalog: Expr[A])(using Quotes): Expr[Seq[ZipxCoord]] = import quotes.reflect.* @@ -346,7 +343,6 @@ object ZipxCatalog: } } if parts.isEmpty then '{ Seq.empty[ZipxCoord] } else '{ ${ Expr.ofList(parts) }.flatten } - end coordsOfImpl private def pinsOfImpl[A: Type](catalog: Expr[A])(using Quotes): Expr[Seq[Pin]] = import quotes.reflect.* @@ -359,7 +355,6 @@ object ZipxCatalog: } } if parts.isEmpty then '{ Seq.empty[Pin] } else '{ ${ Expr.ofList(parts) }.flatten } - end pinsOfImpl private def actionsOfImpl[A: Type](catalog: Expr[A])(using Quotes): Expr[Seq[Action]] = import quotes.reflect.* @@ -372,7 +367,6 @@ object ZipxCatalog: } } if parts.isEmpty then '{ Seq.empty[Action] } else '{ ${ Expr.ofList(parts) }.flatten } - end actionsOfImpl private def shipsOfImpl[A: Type](catalog: Expr[A])(using Quotes): Expr[Seq[PublishedRow]] = import quotes.reflect.* @@ -385,7 +379,6 @@ object ZipxCatalog: } } if parts.isEmpty then '{ Seq.empty[PublishedRow] } else '{ ${ Expr.ofList(parts) }.flatten } - end shipsOfImpl private def catalogValParts[A: Type](using Quotes): List[(quotes.reflect.Symbol, quotes.reflect.TypeRepr)] = import quotes.reflect.* @@ -411,7 +404,6 @@ object ZipxCatalog: case _ => None catch case _: Exception => None fromTree.getOrElse(cls.declaredFields.filterNot(_.flags.is(Flags.Synthetic))) - end valsInSourceOrder val tpe = TypeRepr.of[A].dealias tpe.baseClasses.filterNot(skipClass).reverse.flatMap(valsInSourceOrder).distinct.map { sym => diff --git a/modules/core/src/main/scala/zipx/core/ZipxComposites.scala b/modules/core/src/main/scala/zipx/core/ZipxComposites.scala index d010e8d..7889a92 100644 --- a/modules/core/src/main/scala/zipx/core/ZipxComposites.scala +++ b/modules/core/src/main/scala/zipx/core/ZipxComposites.scala @@ -112,7 +112,6 @@ object ZipxComposites: if purge then cached else cached + ("restore-keys" -> (s"$build$runId-" :: build :: older).mkString("\n")), ) - end cacheStep val hostToolchain = "inputs.baked-toolchain != 'true'" val cacheModes = List(false, true).flatMap { home => @@ -240,7 +239,6 @@ object ZipxComposites: ), ), ) - end awsLogin // Pin keys mirrored from zipx-aws so core does not depend on that module. Keep the strings identical. val CredentialsPinKey: String = "aws-actions/configure-aws-credentials" @@ -291,7 +289,6 @@ object ZipxComposites: } ) .build - end sbtSetupStep /** Omitted when this job has no LocalDir restore to skip. */ private def cachePurgeInput(config: PlanConfig, cacheMode: LocalCacheMode): Option[String] = @@ -324,6 +321,5 @@ object ZipxComposites: ) ) .build - end awsLoginStep end ZipxComposites diff --git a/modules/core/src/main/scala/zipx/core/ZipxDep.scala b/modules/core/src/main/scala/zipx/core/ZipxDep.scala index 9cd9d99..7470384 100644 --- a/modules/core/src/main/scala/zipx/core/ZipxDep.scala +++ b/modules/core/src/main/scala/zipx/core/ZipxDep.scala @@ -40,6 +40,7 @@ enum Cross: /** `%%%` (Scala.js full). */ case Full +end Cross /** `excludeAll(ExclusionRule(...))` on a plugin or library ModuleID. */ final case class ZipxExclude(organization: GroupId, artifact: Option[ArtifactId] = None) @@ -50,6 +51,7 @@ object ZipxExclude: inline def org(inline organization: String, inline artifact: String): ZipxExclude = ZipxExclude(GroupId(organization), Some(ArtifactId(artifact))) +end ZipxExclude /** One row in `zipxVersions`: a library (`Lib`) or an sbt plugin (`Plugin`). */ sealed trait ZipxCoord: @@ -65,6 +67,7 @@ object AsCoords: def apply[A](using ev: AsCoords[A]): AsCoords[A] = ev given ofCoord[A <: ZipxCoord]: AsCoords[A] = a => Seq(a) +end AsCoords trait AsPins[A]: def pins(value: A): Seq[Pin] @@ -73,6 +76,7 @@ object AsPins: def apply[A](using ev: AsPins[A]): AsPins[A] = ev given ofPin: AsPins[Pin] = p => Seq(p) +end AsPins trait AsActions[A]: def actions(value: A): Seq[Action] @@ -81,6 +85,7 @@ object AsActions: def apply[A](using ev: AsActions[A]): AsActions[A] = ev given ofAction: AsActions[Action] = a => Seq(a) +end AsActions trait AsShips[A]: def ships(value: A): Seq[PublishedRow] @@ -89,6 +94,7 @@ object AsShips: def apply[A](using ev: AsShips[A]): AsShips[A] = ev given ofRow[A <: PublishedRow]: AsShips[A] = a => Seq(a) +end AsShips type ShipGroupName = ShipGroupName.Type object ShipGroupName extends Subtype[String]: @@ -125,6 +131,7 @@ object ReleaseVersion extends Subtype[String]: case ReleaseBump.Major => s"${p.major + 1}.0.0") def isInitialDevelopment: Boolean = parts(version).major == 0 + end extension end ReleaseVersion enum ReleaseBump: @@ -210,6 +217,7 @@ object GitSha extends Subtype[String]: override inline def validate(input: String): Boolean | String = if input.matches(Hex40) then true else s"a git SHA must be 40 hex characters, got '$input'" +end GitSha final case class Lib( group: GroupId, @@ -232,7 +240,6 @@ final case class Lib( def fromGraph: Lib = copy(alignTo = Some(family.getOrElse(artifact))) def isAligned: Boolean = alignTo.nonEmpty def coordinate: LibCoordinate = LibCoordinate(group, artifact) -end Lib /** A library as `libraryDependencies` names it: group and base artifact, before any cross suffix. */ final case class LibCoordinate(group: GroupId, artifact: ArtifactId) @@ -340,7 +347,6 @@ object Action: gsha <- GitSha.make(sha) _ <- zipx.workflow.ActionRef.make(s"$trimmed@$sha") yield new Action(trimmed, ver, gsha) - end make end Action final case class ActionBump(action: Action, bump: BumpKind, toVersion: String, toSha: String) diff --git a/modules/core/src/main/scala/zipx/core/ZipxSelf.scala b/modules/core/src/main/scala/zipx/core/ZipxSelf.scala index 9b8993f..bde775d 100644 --- a/modules/core/src/main/scala/zipx/core/ZipxSelf.scala +++ b/modules/core/src/main/scala/zipx/core/ZipxSelf.scala @@ -33,6 +33,4 @@ object ZipxSelf: Left( s"zipx: cannot emit $group % $artifact: Implementation-Version is missing. Pass version, or set it on $origin." ) - end match - end fromVersion end ZipxSelf diff --git a/modules/core/src/test/resources/remote-cache-fixture/project/RemoteCacheFromEnv.scala b/modules/core/src/test/resources/remote-cache-fixture/project/RemoteCacheFromEnv.scala index 8be2e95..4810907 100644 --- a/modules/core/src/test/resources/remote-cache-fixture/project/RemoteCacheFromEnv.scala +++ b/modules/core/src/test/resources/remote-cache-fixture/project/RemoteCacheFromEnv.scala @@ -30,7 +30,6 @@ object RemoteCacheFromEnv extends AutoPlugin: sys.props.getOrElse("java.specification.version", "unknown"), sys.props.getOrElse("os.name", "unknown").toLowerCase.split(' ').head, ) - end if }, ) diff --git a/modules/core/src/test/scala/zipx/core/FakeReleases.scala b/modules/core/src/test/scala/zipx/core/FakeReleases.scala index d30f20b..0c9ecce 100644 --- a/modules/core/src/test/scala/zipx/core/FakeReleases.scala +++ b/modules/core/src/test/scala/zipx/core/FakeReleases.scala @@ -9,3 +9,4 @@ object FakeReleases: val crossing: CatalogCrossing = CatalogCrossing("3.9.0", "3", "2") def of(published: (String, List[String])*): FakeReleases = FakeReleases(published.toMap) +end FakeReleases diff --git a/modules/core/src/test/scala/zipx/core/HttpLookupSpec.scala b/modules/core/src/test/scala/zipx/core/HttpLookupSpec.scala index 27643d9..1885cb8 100644 --- a/modules/core/src/test/scala/zipx/core/HttpLookupSpec.scala +++ b/modules/core/src/test/scala/zipx/core/HttpLookupSpec.scala @@ -124,7 +124,6 @@ object HttpLookupSpec extends ZIOSpecDefault: !second.isMiss, seen.get("conditional") == "\"abc\"", ) - end for }, test("a caller's own If-None-Match gets the server's 304 back") { val send: HttpLookup.Send = req => @@ -173,7 +172,6 @@ object HttpLookupSpec extends ZIOSpecDefault: blocked == Left("HTTP 401"), plain == Left("HTTP 302"), ) - end for }, test("a pre-signed hop that is missing is a miss, not an unreachable registry") { val signed = "https://signed.example/missing" diff --git a/modules/core/src/test/scala/zipx/core/LayerTargetsSpec.scala b/modules/core/src/test/scala/zipx/core/LayerTargetsSpec.scala index df8dcea..6785e2c 100644 --- a/modules/core/src/test/scala/zipx/core/LayerTargetsSpec.scala +++ b/modules/core/src/test/scala/zipx/core/LayerTargetsSpec.scala @@ -41,7 +41,6 @@ object LayerTargetsSpec extends ZIOSpecDefault: .copy(scope = CapabilityScope.Layer) .withMatrixCollapse(MatrixCollapse.Off) if shared then base.withSharedTargets(targets) else base - end deployLayers private def plan(caps: Capability*) = Planner.plan(dockerGraph, caps.toList, config) diff --git a/modules/core/src/test/scala/zipx/core/ModverSpec.scala b/modules/core/src/test/scala/zipx/core/ModverSpec.scala index 08094a5..9677955 100644 --- a/modules/core/src/test/scala/zipx/core/ModverSpec.scala +++ b/modules/core/src/test/scala/zipx/core/ModverSpec.scala @@ -24,7 +24,6 @@ object ModverSpec extends ZIOSpecDefault: sourcePaths = sources, matrixRootOpt = Option.when(matrixRoot != id)(mid(matrixRoot)), ) - end node private val graph = GraphFixture( List( @@ -497,7 +496,6 @@ object ModverSpec extends ZIOSpecDefault: libs.exists(r => r.kind == BumpKind.Minor && Modver.checkFails(r.status)), client.exists(r => Modver.checkFails(r.status)), ) - end match }, test("None floors do not seed reverse-deps") { val seed = BumpSet(Map(ShipRef.Group(ShipGroupName("libs")) -> BumpKind.None)) diff --git a/modules/core/src/test/scala/zipx/core/PinFeedSpec.scala b/modules/core/src/test/scala/zipx/core/PinFeedSpec.scala index 56ec60d..464adba 100644 --- a/modules/core/src/test/scala/zipx/core/PinFeedSpec.scala +++ b/modules/core/src/test/scala/zipx/core/PinFeedSpec.scala @@ -156,7 +156,6 @@ object PinFeedSpec extends ZIOSpecDefault: i.findings.forall(_.pin.purl.isDefined), ) case _ => assertTrue(false) - end match } }, test("Off and empty feeds omit the builtin") { @@ -441,7 +440,6 @@ object PinFeedSpec extends ZIOSpecDefault: chosen.size == 1, materialized.size == 1, ) - end match } }, test("materialize is Left when the feed is not in the list") { diff --git a/modules/core/src/test/scala/zipx/core/RemoteCacheSmokeSpec.scala b/modules/core/src/test/scala/zipx/core/RemoteCacheSmokeSpec.scala index 4580f5f..eb88167 100644 --- a/modules/core/src/test/scala/zipx/core/RemoteCacheSmokeSpec.scala +++ b/modules/core/src/test/scala/zipx/core/RemoteCacheSmokeSpec.scala @@ -27,4 +27,3 @@ object RemoteCacheSmokeSpec extends ZIOSpecDefault: ) }, ) -end RemoteCacheSmokeSpec diff --git a/modules/core/src/test/scala/zipx/core/Rendered.scala b/modules/core/src/test/scala/zipx/core/Rendered.scala index 2c97c4b..33cd2c5 100644 --- a/modules/core/src/test/scala/zipx/core/Rendered.scala +++ b/modules/core/src/test/scala/zipx/core/Rendered.scala @@ -5,3 +5,4 @@ object Rendered: extension (result: Either[String, String]) def yaml: String = result.fold(error => throw AssertionError(s"unexpected render failure: $error"), identity) +end Rendered diff --git a/modules/core/src/test/scala/zipx/core/SbtImageSpec.scala b/modules/core/src/test/scala/zipx/core/SbtImageSpec.scala index 8aa2ca0..81284a9 100644 --- a/modules/core/src/test/scala/zipx/core/SbtImageSpec.scala +++ b/modules/core/src/test/scala/zipx/core/SbtImageSpec.scala @@ -43,7 +43,6 @@ object SbtImageSpec extends ZIOSpecDefault: ImageStep.EmptyFile("/opt/widgets/READY"), ImageStep.Apt("libwidgets", "1.2.3-1"), ) - end widgetSteps private def widgets(steps: (String, CpuArch) => List[ImageStep]): SbtImageLayer = SbtImageLayer("widgets", noLookup).install(steps).withEnv("WIDGET_PATH" -> "/opt/widgets") diff --git a/modules/core/src/test/scala/zipx/it/BazelRemoteTestContainer.scala b/modules/core/src/test/scala/zipx/it/BazelRemoteTestContainer.scala index b8c908e..aa2d7c9 100644 --- a/modules/core/src/test/scala/zipx/it/BazelRemoteTestContainer.scala +++ b/modules/core/src/test/scala/zipx/it/BazelRemoteTestContainer.scala @@ -40,7 +40,6 @@ final case class BazelRemoteTestContainer( .withStartupTimeout(Duration.ofMinutes(2)) ) c - end container def start: BazelRemoteTestContainer = try container.start() diff --git a/modules/core/src/test/scala/zipx/it/FixtureRunner.scala b/modules/core/src/test/scala/zipx/it/FixtureRunner.scala index 0ea6cb4..8da25f2 100644 --- a/modules/core/src/test/scala/zipx/it/FixtureRunner.scala +++ b/modules/core/src/test/scala/zipx/it/FixtureRunner.scala @@ -91,7 +91,6 @@ object FixtureRunner: finally try c.stop() catch case _: Throwable => () - end try end runSbt def deleteTree(p: Path): Unit = @@ -102,7 +101,6 @@ object FixtureRunner: catch case _: Exception => () } catch case _: Exception => () - end deleteTree private def copyResourceTree(resourceRoot: String, dest: Path): Unit = val cl = Thread.currentThread().getContextClassLoader @@ -133,11 +131,8 @@ object FixtureRunner: Files.copy(p, out, StandardCopyOption.REPLACE_EXISTING) } finally fs.close() - end try case Some(url) => sys.error(s"Unsupported resource URL: $url") - end match - end copyResourceTree def writeUtf8(path: Path, content: String): Unit = Files.createDirectories(path.getParent) diff --git a/modules/core/src/test/scala/zipx/it/RemoteCacheItSpec.scala b/modules/core/src/test/scala/zipx/it/RemoteCacheItSpec.scala index c208b47..29c17ee 100644 --- a/modules/core/src/test/scala/zipx/it/RemoteCacheItSpec.scala +++ b/modules/core/src/test/scala/zipx/it/RemoteCacheItSpec.scala @@ -27,7 +27,6 @@ object RemoteCacheItSpec extends ZIOSpecDefault: finally if stream != null then stream.close() conn.disconnect() - end httpGet private def runFixture( bazel: BazelRemoteTestContainer, diff --git a/modules/sbt-plugin/src/main/scala/zipx/ZipxVersions.scala b/modules/sbt-plugin/src/main/scala/zipx/ZipxVersions.scala index ae6af49..2839d00 100644 --- a/modules/sbt-plugin/src/main/scala/zipx/ZipxVersions.scala +++ b/modules/sbt-plugin/src/main/scala/zipx/ZipxVersions.scala @@ -132,7 +132,6 @@ object ZipxVersions: ), ) catalog ++ versions - end applySettings /** The pointer POM records the full sha beside the `-SNAPSHOT` coordinate. One `` element. */ private[zipx] def withSnapshotSha(node: Node, sha: String): Node = diff --git a/modules/sbt-plugin/src/main/scala/zipx/plugin/CapabilityTasks.scala b/modules/sbt-plugin/src/main/scala/zipx/plugin/CapabilityTasks.scala index 7eb9c29..6fa152f 100644 --- a/modules/sbt-plugin/src/main/scala/zipx/plugin/CapabilityTasks.scala +++ b/modules/sbt-plugin/src/main/scala/zipx/plugin/CapabilityTasks.scala @@ -107,7 +107,6 @@ object CapabilityTasks: all match case h :: t => SbtCommand.session(h, t*) case Nil => sys.error("zipx: session requires at least one command") - end session /** One command per project ref running `task` (e.g. `matrix.projectRefs` from sbt-projectmatrix). */ def rows(refs: Seq[ProjectRef], task: RunnableKey): List[SbtCommand] = @@ -166,7 +165,6 @@ object CapabilityTasks: sys.error("""zipx: cmd"…" produced an empty sbt command""") // Safe: every literal piece is ShText, ids and key labels add only safe characters, and the result is non-empty. n => SbtCommand.unsafeBuilt(interleave(parts, splices.map(renderSplice(_, n)))) - end commandFrom private def interleave(parts: List[String], splices: List[String]): String = val sb = new StringBuilder @@ -210,7 +208,6 @@ object CapabilityTasks: } // Checking lives in `commandFrom`, plain Scala that can be tested; the macro only hands off. '{ CapabilityTasks.commandFrom(${ sc }.parts.toList, ${ Varargs(spliceExprs) }.toList) } - end cmdMacro /** [[zipx.core.Capability.deploy]] (Aggregate-by-target) with the deploy command given as a task key. */ def deploy( diff --git a/modules/sbt-plugin/src/main/scala/zipx/plugin/CatalogProbe.scala b/modules/sbt-plugin/src/main/scala/zipx/plugin/CatalogProbe.scala index 59012ba..543b0d8 100644 --- a/modules/sbt-plugin/src/main/scala/zipx/plugin/CatalogProbe.scala +++ b/modules/sbt-plugin/src/main/scala/zipx/plugin/CatalogProbe.scala @@ -30,6 +30,7 @@ private[plugin] object CatalogProbe: def message: String = this match case Unresolved(config, reason) => s"could not read what the $config graph asks of the catalog rows, so staleness is unchecked: $reason" + end ProbeError private final case class Graph( selected: Map[ResolvedModule, String], @@ -57,7 +58,6 @@ private[plugin] object CatalogProbe: found.flatMap(sofar => conflictsOf(config, graph, forced, seen, conf).map(sofar ++ _)) } .map(_.distinct.groupMap((module, _) => module)((_, want) => want)) - end wanted private def graphOf(config: ConfigurationReport): Graph = val modules = config.modules.toList.filterNot(_.evicted) @@ -69,7 +69,6 @@ private[plugin] object CatalogProbe: ) .toMap, ) - end graphOf /** In-repo modules are left out because no registry holds them. A conflict counts only on an edge the real graph has, * or on one to a module zipx excluded, so an exclusion the probe does not repeat cannot invent one. @@ -118,7 +117,6 @@ private[plugin] object CatalogProbe: .map(raw => module -> Wanted(DepRevision.of(raw), by)) } } - end conflictsOf private def wantedOf(constraint: VersionConstraint): Option[String] = constraint.preferred.orElse(constraint.interval.from).map(_.asString) @@ -149,7 +147,6 @@ private[plugin] object CatalogProbe: ) else Nil ) - end mirrors private def ivyProperties(ivyHome: Option[File]): Map[String, String] = val home = sys.props @@ -185,7 +182,6 @@ private[plugin] object CatalogProbe: dropInfoAttributes = true, ) .toOption - end if case _ => None private def fileUri(path: String): String = diff --git a/modules/sbt-plugin/src/main/scala/zipx/plugin/CatalogResolution.scala b/modules/sbt-plugin/src/main/scala/zipx/plugin/CatalogResolution.scala index a9623c3..114828f 100644 --- a/modules/sbt-plugin/src/main/scala/zipx/plugin/CatalogResolution.scala +++ b/modules/sbt-plugin/src/main/scala/zipx/plugin/CatalogResolution.scala @@ -57,7 +57,6 @@ private[plugin] object CatalogResolution: else CatalogConflict.unpinned(module, revisions.map(DepRevision.of)) } stale ++ unpinned - end conflicts /** Forced modules have no evictions here; [[CatalogProbe]] reads theirs. */ private def revisionsOf(report: UpdateReport): Map[ResolvedModule, List[String]] = diff --git a/modules/sbt-plugin/src/main/scala/zipx/plugin/PublishedModule.scala b/modules/sbt-plugin/src/main/scala/zipx/plugin/PublishedModule.scala index 2440973..723e523 100644 --- a/modules/sbt-plugin/src/main/scala/zipx/plugin/PublishedModule.scala +++ b/modules/sbt-plugin/src/main/scala/zipx/plugin/PublishedModule.scala @@ -8,3 +8,4 @@ object PublishedModule: /** sbt's own cross function, so the platform suffix and an sbt plugin's cross prefix match what sbt publishes. */ def artifactId(module: ModuleID, scala: Option[ScalaModuleInfo]): String = CrossVersion(module, scala).fold(module.name)(_(module.name)) +end PublishedModule diff --git a/modules/sbt-plugin/src/main/scala/zipx/plugin/ResolutionCache.scala b/modules/sbt-plugin/src/main/scala/zipx/plugin/ResolutionCache.scala index 100bb24..cc17e48 100644 --- a/modules/sbt-plugin/src/main/scala/zipx/plugin/ResolutionCache.scala +++ b/modules/sbt-plugin/src/main/scala/zipx/plugin/ResolutionCache.scala @@ -14,3 +14,4 @@ private[plugin] object ResolutionCache: def forgetOnUnload(pinned: State => Seq[ZipxCoord]): State => State = s => forgetIfPinned(pinned(s)) s +end ResolutionCache diff --git a/modules/sbt-plugin/src/main/scala/zipx/plugin/SnapshotCommands.scala b/modules/sbt-plugin/src/main/scala/zipx/plugin/SnapshotCommands.scala index e7addbd..eb63175 100644 --- a/modules/sbt-plugin/src/main/scala/zipx/plugin/SnapshotCommands.scala +++ b/modules/sbt-plugin/src/main/scala/zipx/plugin/SnapshotCommands.scala @@ -44,7 +44,6 @@ object SnapshotCommands: SnapshotStatus.report(family.literal, family.version, pointer, present) log.info(report.fold(err => sys.error(s"zipx: $err"), SnapshotStatus.render)) } - end status /** Returns the rewritten catalog, or the same text when every selected pin is already current. ` ` moves * the row to that line's latest commit; with no line, each pin stays on its own. @@ -79,8 +78,6 @@ object SnapshotCommands: case Right(Some(next)) => rewrite(src, family, next, log) } case None => advanceInPlace(row, coords, source, registry, names, cache, headers, log) - end match - end advance private def advanceInPlace( row: Option[String], @@ -114,9 +111,7 @@ object SnapshotCommands: case Some(SnapshotHold.Pointer(line)) => onLine(line) case Some(SnapshotHold.Commit(line)) => onLine(line) case None => src - end match } - end advanceInPlace private inline val AdvanceUsage = "zipxSnapshotAdvance takes a row and, optionally, a line: sbt 'zipxSnapshotAdvance widgets 1.5.0'" @@ -155,7 +150,6 @@ object SnapshotCommands: s"zipx: the $pointer pointer for ${family.literal} names no commit (no ${SnapshotPointer.ShaElement}). " + "Publish the line with sbt zipxSnapshotPublish, or pin a commit by its stored id." ) - end pointerOf def pinRelease( arg: String, @@ -179,9 +173,7 @@ object SnapshotCommands: val rewritten = PinRewrite.replace(src, family.written, next).fold(err => sys.error(s"zipx: $err"), identity) log.info(s"zipx: ${family.literal} ${family.version} -> $next") rewritten - end match } - end pinRelease /** One constructor in the catalog source, and every row that shares its version through `.mod`. */ private final case class Family(written: ZipxCoord, members: ::[ZipxCoord]): @@ -201,7 +193,6 @@ object SnapshotCommands: case head :: tail => Some(Family(head.copy(artifact = literalOf(head), family = None), ::(head, tail))) case Nil => None selected.flatMap(familyOf).distinctBy(_.written) - end families def defaultBranch(root: File): String = gitLine(root, "symbolic-ref", "--short", "refs/remotes/origin/HEAD") @@ -281,7 +272,6 @@ object SnapshotCommands: case DepRevision.Commit(pin) => present(pin) case DepRevision.UnstoredCommit(pin) => present(pin) case _ => false - end artifactPresent /** A POM under `version`: the plain file, or sbt's unique snapshot named by that directory's metadata. `base` is the * version without `-SNAPSHOT`, which the timestamp replaces in the file name. @@ -301,7 +291,6 @@ object SnapshotCommands: .flatMap(body => SnapshotPointer.timestampedVersion(base, body)) .flatMap(stamped => get(SnapshotPointer.uniquePomRelative(organization, artifact, version, stamped))) } - end pomUnder private def fetch(root: String, relative: String, cache: File, headers: Map[String, String]): Option[String] = val url = s"${root.stripSuffix("/")}/$relative" @@ -335,8 +324,6 @@ object SnapshotCommands: finally held.release() channel.close() - end try - end locked private def clean(cache: File, force: Boolean = false): Unit = val xml = new File(cache, "maven-metadata.xml") @@ -352,7 +339,6 @@ object SnapshotCommands: IO.delete(xml) IO.delete(new File(cache, "maven-metadata.xml.sha1")) IO.delete(new File(cache, "maven-metadata.xml.md5")) - end clean private def sidecar(file: File): Option[String] = if file.isFile then IO.read(file).trim.split("\\s+").headOption.filter(_.nonEmpty) else None diff --git a/modules/sbt-plugin/src/main/scala/zipx/plugin/SnapshotGit.scala b/modules/sbt-plugin/src/main/scala/zipx/plugin/SnapshotGit.scala index bdcaea1..1f0bb43 100644 --- a/modules/sbt-plugin/src/main/scala/zipx/plugin/SnapshotGit.scala +++ b/modules/sbt-plugin/src/main/scala/zipx/plugin/SnapshotGit.scala @@ -31,8 +31,6 @@ object SnapshotGit: Left(SnapshotRevisionError.NotCommitPin(s"git abbrev $short is not 12 unique hex")) case None => Right(Tree.Missing(stamp)) - end if - end describe def porcelain(root: File): String = git(root, "status", "--porcelain").getOrElse("") diff --git a/modules/sbt-plugin/src/main/scala/zipx/plugin/ZipxDeps.scala b/modules/sbt-plugin/src/main/scala/zipx/plugin/ZipxDeps.scala index a671c8e..68b445d 100644 --- a/modules/sbt-plugin/src/main/scala/zipx/plugin/ZipxDeps.scala +++ b/modules/sbt-plugin/src/main/scala/zipx/plugin/ZipxDeps.scala @@ -25,7 +25,6 @@ object ZipxDeps: val withCfg = lib.config.fold(mid)(cfg => mid % cfg) val withEx = lib.excludes.foldLeft(withCfg)(applyExclude) lib.alignTo.fold(withEx)(src => withEx.withExtraAttributes(Map(FromGraphAttr -> (src: String)))) - end moduleID def moduleID(plugin: ZipxPluginCoord): ModuleID = val mid = (plugin.group: String) % (plugin.artifact: String) % (plugin.version: String) diff --git a/modules/sbt-plugin/src/main/scala/zipx/plugin/ZipxPlugin.scala b/modules/sbt-plugin/src/main/scala/zipx/plugin/ZipxPlugin.scala index 5b56201..b157798 100644 --- a/modules/sbt-plugin/src/main/scala/zipx/plugin/ZipxPlugin.scala +++ b/modules/sbt-plugin/src/main/scala/zipx/plugin/ZipxPlugin.scala @@ -287,7 +287,6 @@ object ZipxPlugin extends AutoPlugin: def pagesPermissions = zipx.specular.ZipxDocs.pagesPermissions def deployWhen = zipx.specular.ZipxDocs.deployWhen def DocsName = zipx.specular.ZipxDocs.DocsName - end ZipxDocs val ZipxAgentEnv = zipx.agent.ZipxAgentEnv type CloudAgent = zipx.core.CloudAgent @@ -327,7 +326,6 @@ object ZipxPlugin extends AutoPlugin: def packagesPermissions = zipx.github.ZipxGitHubPackages.packagesPermissions def DefaultName = zipx.github.ZipxGitHubPackages.DefaultName def PublishFlagEnv = zipx.github.ZipxGitHubPackages.PublishFlagEnv - end ZipxGitHubPackages object ZipxMaven: def releases( @@ -343,7 +341,6 @@ object ZipxPlugin extends AutoPlugin: token: EnvValue, ): ReleaseWorkflow = zipx.maven.ZipxMaven.releases(snapshots, releases, token) - end ZipxMaven /** scoverage, via `zipxCoverageWorkflow` or `zipxCapabilities += Coverage.once()`. In core, not a pack, because it * guards against `test` being `testQuick`. @@ -902,7 +899,6 @@ object ZipxPlugin extends AutoPlugin: .distinct .sorted .toList - end sourcePathsFor /** sbt's output tree, or a `projectMatrix` row's synthetic base under `.sbt/`. */ private def isGeneratedPath(path: String): Boolean = @@ -977,8 +973,6 @@ object ZipxPlugin extends AutoPlugin: ) .fold(err => sys.error(err.message), identity) ) - end if - end sbtImagePlan /** A committed pin YAML is leftover input and always fails, even when `zipxActions` is set. */ private def resolveActionPins(extracted: Extracted, root: File): ActionPins = @@ -991,7 +985,6 @@ object ZipxPlugin extends AutoPlugin: else val rows = readBuildSetting(extracted, zipxActionRows, Seq.empty) orFail(ActionPins.overlay(ActionPins.Defaults, rows)) - end resolveActionPins private def builtinCapabilities( graph: ModuleGraph, @@ -1022,7 +1015,6 @@ object ZipxPlugin extends AutoPlugin: ) val base = List(test, fmt, wfCheck, adv, Capability.publish) if graph.nodes.exists(_.docker) then base :+ Capability.docker else base - end builtinCapabilities private def verifyGate(opt: VerifyOpt, name: CapabilityName, gate: String, command: SbtCommand): Capability = opt match @@ -1058,7 +1050,6 @@ object ZipxPlugin extends AutoPlugin: """zipx: zipxVerify.fmt is On but ZipxVersions has no Plugin("org.scalameta", "sbt-scalafmt", "…"). """ + """Add that row, or zipxVerify := ZipxVerify.Strict.copy(fmt = VerifyOpt.Skip("reason")), then sbt zipxWorkflowGenerate.""" ) - end checkFmtPlugin private def checkCommandNames( declared: List[SbtCommandName], @@ -1089,8 +1080,6 @@ object ZipxPlugin extends AutoPlugin: """Missing scalafmtCheckAll: add Plugin("org.scalameta", "sbt-scalafmt", "…") to ZipxVersions (or zipxVerify := ZipxVerify.Strict.copy(fmt = VerifyOpt.Skip("reason"))), then sbt zipxWorkflowGenerate.""" else "Or set zipxCheckCommandNames := false") ) - end if - end if private def distance(a: String, b: String): Int = val m = a.length @@ -1102,7 +1091,6 @@ object ZipxPlugin extends AutoPlugin: val cost = if a(i - 1) == b(j - 1) then 0 else 1 dp(i)(j) = (dp(i - 1)(j) + 1).min(dp(i)(j - 1) + 1).min(dp(i - 1)(j - 1) + cost) dp(m)(n) - end distance private def capabilitiesOf(extracted: Extracted, graph: ModuleGraph): List[Capability] = val userCaps = readBuildSetting(extracted, zipxCapabilities, Seq.empty) @@ -1139,7 +1127,6 @@ object ZipxPlugin extends AutoPlugin: case head :: tail => val note = orFail(SnapshotPins.annotation(::(head, tail))) combined.map(c => if c.name == Capability.TestName then c.plusExtraSteps(note) else c) - end capabilitiesOf /** The images-and-deploys half that `zipx-deploy.yml` runs, refused when it could not run as declared. `None` under * `DeployTrigger.OnMerge`. @@ -1223,7 +1210,6 @@ object ZipxPlugin extends AutoPlugin: enabled, ) zipx.agent.SbtImageFeeds.withUser(agent, pins, enabled, layers) - end pinFeeds private def agentFiles( agent: zipx.core.CloudAgent.On, @@ -1297,7 +1283,6 @@ object ZipxPlugin extends AutoPlugin: log.info(s"zipx wrote ${file.getPath}") } if rendered.dockerfile.isEmpty then deleteGenerated(root, ZipxAgentEnv.DockerfileRel, log) - end match } private def checkAgentEnv(root: File, cfg: PlanConfig, extracted: Extracted, log: Logger): Unit = @@ -1333,8 +1318,6 @@ object ZipxPlugin extends AutoPlugin: sys.error( s"zipx: ${ZipxAgentEnv.DockerfileRel} is leftover. Run 'sbt zipxWorkflowGenerate' and commit the deletion." ) - end match - end checkAgentEnv private def warnUnusedAgentPins(sbtImage: Boolean, pins: Seq[Pin], log: Logger): Unit = if sbtImage then @@ -1409,7 +1392,6 @@ object ZipxPlugin extends AutoPlugin: sys.error(s"zipx: ${LeftoverOpt.leftoverMessage(found, None)}") case LeftoverOpt.Warn(reason) => log.warn(s"zipx: ${LeftoverOpt.leftoverMessage(found, Some(reason))}") - end checkLeftoverSteward private def writePinWorkflowsIfEnabled: Def.Initialize[Task[Unit]] = Def.task { val extracted = Project.extract(state.value) @@ -1441,8 +1423,6 @@ object ZipxPlugin extends AutoPlugin: ) ) writeCompanion(root, PinSnapshotWorkflow.DefaultPath, snapBody, log) - end if - end if } private def checkPinWorkflows(root: File, cfg: PlanConfig, extracted: Extracted, log: Logger): Unit = @@ -1471,9 +1451,6 @@ object ZipxPlugin extends AutoPlugin: ) ) checkCompanion(root, PinSnapshotWorkflow.DefaultPath, expectedSnap, log) - end if - end if - end checkPinWorkflows private def writeVersionUpdatesIfEnabled: Def.Initialize[Task[Unit]] = Def.task { val extracted = Project.extract(state.value) @@ -1494,7 +1471,6 @@ object ZipxPlugin extends AutoPlugin: else if file.exists then IO.delete(file) log.info(s"zipx deleted ${file.getPath}") - end if } private def checkVersionUpdates(root: File, cfg: PlanConfig, extracted: Extracted, log: Logger): Unit = @@ -1513,8 +1489,6 @@ object ZipxPlugin extends AutoPlugin: sys.error( s"zipx: $rel is leftover. Set zipxVersionUpdates := true or delete $rel, then sbt zipxWorkflowGenerate." ) - end if - end checkVersionUpdates /** Checks command names itself: this task runs beside `renderWorkflow`, so that one failing does not stop the write. */ @@ -1566,7 +1540,6 @@ object ZipxPlugin extends AutoPlugin: val expected = orFail(ShellWorkflow.render(spec, cfg)) checkCompanion(root, spec.path, expected, log) } - end checkShellWorkflows private def deployYaml(st: State, graph: ModuleGraph, cfg: PlanConfig): Option[String] = val extracted = Project.extract(st) @@ -1609,7 +1582,6 @@ object ZipxPlugin extends AutoPlugin: cap.name == zipx.specular.ZipxDocs.DocsName && cap.workflowCall.isDefined } Some(orFail(ReleaseWorkflow.render(release, cfg, TagScheme.of(ShipIndex.from(ships)), docs))) - end releaseYaml private def writeReleaseWorkflow: Def.Initialize[Task[Unit]] = Def.task { val root = (LocalRootProject / baseDirectory).value @@ -1738,7 +1710,6 @@ object ZipxPlugin extends AutoPlugin: sys.error( s"zipx: $id is a local build. Commit the tree, or publish it with zipxSnapshotPublish local.$extra" ) - end match installSnapshotProps(tree, local) val (task, destination) = target match case SnapshotTarget.Local => (publishLocal, "the local ivy repository") @@ -1768,7 +1739,6 @@ object ZipxPlugin extends AutoPlugin: back :+ "zipxSnapshotRefuse" else back ("reload" :: publishes.map(c => c.text: String) ::: pointer ::: restoreCommands).foldRight(next)(_ :: _) - end match } private def snapshotId(row: PublishedRow, tree: SnapshotGit.Tree): String = @@ -1788,7 +1758,6 @@ object ZipxPlugin extends AutoPlugin: sys.props(SnapshotPublishRevision.DirtyProperty) = at.value case SnapshotGit.Tree.Missing(at) => sys.props(SnapshotPublishRevision.DirtyProperty) = at.value - end installSnapshotProps private def clearSnapshotProps(): Unit = sys.props -= SnapshotPublishRevision.PointerProperty @@ -1832,7 +1801,6 @@ object ZipxPlugin extends AutoPlugin: val drift = releasedDrift(extracted, graph, catalog, binaries, release) .fold(err => sys.error(s"zipx: ${err.message}"), identity) SnapshotGuard.decide(drift, gate) - end publishVerdict private def reportVerdict(st: State, verdict: SnapshotVerdict): Unit = verdict.hints.foreach(line => st.log.info(s"zipx: $line")) @@ -1844,7 +1812,6 @@ object ZipxPlugin extends AutoPlugin: st.log.error(s"zipx: $line") annotate("error", "zipx shadowed snapshot", line) } - end reportVerdict private def annotate(kind: String, title: String, text: String): Unit = if sys.env.contains("GITHUB_ACTIONS") then println(s"::$kind title=$title::$text") @@ -1870,7 +1837,6 @@ object ZipxPlugin extends AutoPlugin: case Right(_) => Right(found) } } - end releasedDrift private def releaseDriftTask: Def.Initialize[Task[Seq[String]]] = Def.task { val st = state.value @@ -1931,8 +1897,6 @@ object ZipxPlugin extends AutoPlugin: case DriftGate.Fail => log.error(located) sys.error(s"zipx: $text") - end match - end if } private def rowDrift( @@ -1974,7 +1938,6 @@ object ZipxPlugin extends AutoPlugin: else s"Set ${named.mkString(" and ")}, or add a credentials file for $host." sys.error(s"zipx: no credentials for $host. $hint") } - end requireCredentials /** Present only when the registry's env vars are set; a credentials file the build already has stays ahead. */ private def directCredentials(release: ReleaseWorkflow): List[Credentials] = @@ -1992,7 +1955,6 @@ object ZipxPlugin extends AutoPlugin: case RegistryCredentials.Bearer(token) => RegistryCredentials.read(token, sys.env).toList.map(value => Credentials(realm, host, "token", value)) } - end directCredentials private def snapshotRegistriesOf( release: Option[ReleaseWorkflow], @@ -2017,7 +1979,6 @@ object ZipxPlugin extends AutoPlugin: } } .map(RowStatus.of) - end rowStatus private def writeCompanion(root: File, rel: String, body: String, log: Logger): Unit = val file = root / rel @@ -2102,7 +2063,6 @@ object ZipxPlugin extends AutoPlugin: orFail(PinEngine.materialize(feeds, applied)) log.info(s"zipx: applied ${applied.size} pin update(s)") else log.info("zipx: no pin updates applied") - end if } private def writePinCatalog( @@ -2117,7 +2077,6 @@ object ZipxPlugin extends AutoPlugin: val next = orFail(ZipxCatalog.applyAppliedPins(IO.read(file), items)) IO.write(file, next) log.info(s"zipx: wrote ${file.getPath}") - end writePinCatalog /** `Some(true)` if the operator confirmed, `Some(false)` if they declined, `None` if there is no console to ask. */ private def confirmUpdates(n: Int, what: String): Option[Boolean] = @@ -2141,7 +2100,6 @@ object ZipxPlugin extends AutoPlugin: val body = PinSnapshot.render(feeds, pins, sha, ref, jobId, java.time.Instant.now.toString, "zipx") orFail(PinSnapshot.submit(token, repo, body)) streams.value.log.info("zipx: submitted pin snapshot") - end if } private def inventoryAtBase(root: File, sha: String, log: Logger): Map[PinFeedName, List[PinnedDep]] = @@ -2157,7 +2115,6 @@ object ZipxPlugin extends AutoPlugin: val jsonFile = work / PinInventory.RelPath if !jsonFile.exists then sys.error(s"zipx: missing ${jsonFile.getPath}") orFail(PinInventory.parse(IO.read(jsonFile))) - end inventoryAtBase /** A user capability replaces the built-in of the same name, so a custom `docker` yields no duplicate jobs. */ private def combineCapabilities(builtins: List[Capability], user: List[Capability]): List[Capability] = @@ -2326,7 +2283,6 @@ object ZipxPlugin extends AutoPlugin: sys.error( "zipx: Ship rows cannot share version with sbt-dynver-ci. Remove Plugin(\"rocks.earlyeffect\", \"sbt-dynver-ci\", …) and the dynver plugin; zipx assigns version from Ship rows." ) - end validateCatalog private def dynverPresent(extracted: Extracted, coords: Seq[ZipxCoord]): Boolean = ZipxCatalog.plugins(coords).exists(p => (p.artifact: String) == "sbt-dynver-ci") || @@ -2385,8 +2341,6 @@ object ZipxPlugin extends AutoPlugin: s"zipx: ${file.getPath} pins a snapshot plugin but lacks the snapshot resolver. Run 'sbt zipxWorkflowGenerate'." ) log.info(s"zipx: ${file.getPath} plugin list is up to date.") - end if - end if sbtVer.foreach { ver => val expected = ZipxCatalog.renderBuildProperties(ver) val file = root / ZipxCatalog.BuildPropertiesPath @@ -2399,7 +2353,6 @@ object ZipxPlugin extends AutoPlugin: sys.error(s"${file.getPath} is out of date. Run 'sbt zipxWorkflowGenerate' and commit the result.") log.info(s"zipx: ${file.getPath} is up to date.") } - end syncCatalogFiles /** Every snapshot CI resolves: catalog rows, and a snapshot sbt-zipx that `plugins.sbt` loads. */ private def ciPins(extracted: Extracted): List[ZipxCoord] = @@ -2525,7 +2478,6 @@ object ZipxPlugin extends AutoPlugin: case head :: _ => sys.error(s"zipx: ${ReleaseError.UnknownShip(head, known.toList.sorted).message}") case Nil => Some(wanted) log.info(ReleaseReadiness.render(gates, SnapshotCommands.defaultBranch(root), names)) - end if } private final case class SnapshotCtx( @@ -2595,7 +2547,6 @@ object ZipxPlugin extends AutoPlugin: used.distinct match case Nil => List(crossed(module, rootScala)) case names => names - end libNames private def releaseGates(st: State, extracted: Extracted, own: Set[ModuleID]): List[ShipGate] = val ships = readBuildSetting(extracted, zipxShips, Seq.empty).toList @@ -2638,8 +2589,6 @@ object ZipxPlugin extends AutoPlugin: .distinct List(ShipGate(row.identity, row.version, blockers, rides)) } - end if - end releaseGates /** Both names come from sbt's own cross function, so a `%%` dependency matches the project it names on any platform. */ @@ -2701,8 +2650,6 @@ object ZipxPlugin extends AutoPlugin: IO.write(file, next) log.info(s"zipx: wrote ${file.getPath}") else log.info("zipx: no catalog updates applied") - end if - end if } private def modverBumpTask: Def.Initialize[InputTask[Unit]] = @@ -2739,8 +2686,6 @@ object ZipxPlugin extends AutoPlugin: ) IO.write(file, next) log.info(s"zipx: wrote ${file.getPath}") - end match - end if } /** `One` skips the registry, so an explicit bump runs offline. Under `Released`, a partial row or an unreachable @@ -2826,8 +2771,6 @@ object ZipxPlugin extends AutoPlugin: IO.write(file, next) log.info(s"zipx: wrote ${file.getPath}. reload, then sbt zipxWorkflowGenerate.") else log.info("zipx: no Action updates applied") - end if - end if } private def advisoryCheckTask: Def.Initialize[Task[Unit]] = Def.task { @@ -2876,7 +2819,6 @@ object ZipxPlugin extends AutoPlugin: val report = orFail(PinEngine.prGate(feeds, pinRows, osv, if base.isEmpty then PinPrGate.All else gate, base)) log.info(PinEngine.summary(report)) if report.failsJob then findings += "zipx: pin advisory findings. Run sbt \"zipxPinUpdate yes\"." - end if val listed = findings.result() listed.foreach(msg => log.error(msg)) if listed.nonEmpty then sys.error(listed.head) @@ -2934,7 +2876,6 @@ object ZipxPlugin extends AutoPlugin: case TestAffected.Run.Nothing => log.info(s"zipx: nothing the root aggregate tests is affected since ${base.getOrElse("")}") next - end match } /** `zipx-deploy.yml`'s resolve step: env in, plan out as files, for the reason [[affectedModulesTask]] writes one. */ @@ -3106,7 +3047,6 @@ object ZipxPlugin extends AutoPlugin: r <- buildFileReadings(root, extracted, graph, from, "HEAD", files, log) yield r Affected.outputModules(graph, changed, readings) - end affectedSinceMergeBase /** A catalog unreadable at either commit counts as build-wide. */ private def catalogEdit( @@ -3125,7 +3065,6 @@ object ZipxPlugin extends AutoPlugin: case _ => List(CatalogChange.BuildWide(s"$rel is unreadable at $from or $to")) CatalogEdit(rel, changes) } - end catalogEdit private def describeCatalog(edit: CatalogEdit): String = val readings = edit.changes.map { @@ -3160,7 +3099,6 @@ object ZipxPlugin extends AutoPlugin: reading } catalog.toList ++ buildSbt.toList - end buildFileReadings /** `None` means the diff failed, `Some(Nil)` that it found nothing. `Affected.outputModules` fails open on `None`, so * a bad base ref cannot skip every Verify job. @@ -3191,7 +3129,6 @@ object ZipxPlugin extends AutoPlugin: IO.write(file, ModverReport.render(report) + "\n") log.info(s"zipx: wrote ${file.getPath}") report - end if } private def modverCheckTask: Def.Initialize[Task[Unit]] = Def.task { @@ -3245,8 +3182,6 @@ object ZipxPlugin extends AutoPlugin: bumps = Modver.expand(BumpSet(kinds), graph, index, policy) report <- Modver.report(index, released, bumps.asMap, mimaRan) yield report - end for - end writeModverReport private val NoReleaseWorkflow = "Ship rows release from zipx-release.yml: set zipxReleaseWorkflow (ZipxCentral.releases, ZipxGitHubPackages.releases, or ZipxMaven.releases)" @@ -3286,7 +3221,6 @@ object ZipxPlugin extends AutoPlugin: } .map(ShipIndex.from) } - end lastReleases /** Central's public repo and a `file:` registry need no auth. A declared token missing from the environment fails: an * anonymous GET that a registry answers with 404 must not read as "not released". @@ -3306,7 +3240,6 @@ object ZipxPlugin extends AutoPlugin: .encodeToString(s"$u:$p".getBytes(java.nio.charset.StandardCharsets.UTF_8)) Right(Map("Authorization" -> s"Basic $raw")) case _ => Left(missingCredentials(release)) - end registryHeaders private def missingCredentials(release: ReleaseWorkflow): String = val host = release.registry.publishHosts.mkString(", ") @@ -3346,7 +3279,6 @@ object ZipxPlugin extends AutoPlugin: } } .groupMap(_._1)(_._2) - end liveBinaries private def versionSchemeOf(extracted: Extracted, id: ModuleId): Either[String, LibraryVersionScheme] = val ref = extracted.structure.allProjectRefs.find(_.project == (id: String)) @@ -3386,7 +3318,6 @@ object ZipxPlugin extends AutoPlugin: val lib = new com.typesafe.tools.mima.lib.MiMaLib(Nil) if lib.collectProblems(old, classes, Nil).isEmpty then MemberProbe.Clean else MemberProbe.BinaryBreak } - end probeMember private def releasedJar( extracted: Extracted, @@ -3405,7 +3336,6 @@ object ZipxPlugin extends AutoPlugin: extracted.get(LocalRootProject / baseDirectory) / "target" / "zipx-mima" / s"${gav.artifact}-$version.jar" dest.getParentFile.mkdirs() registryHeaders(release).flatMap(headers => download(release.registry.jarUrl(gav), headers, dest)) - end releasedJar /** `Right(None)` when the registry answers that the artifact is not there: a member absent from that release. */ private def download(url: String, headers: Map[String, String], dest: File): Either[String, Option[File]] = @@ -3440,7 +3370,6 @@ object ZipxPlugin extends AutoPlugin: java.nio.file.StandardOpenOption.TRUNCATE_EXISTING, ), ) - end fetch val first = fetch(url, sendHeaders = true) val raw = first.headers().firstValue("location") val location = if raw.isEmpty then None else Some(raw.orElse("")) @@ -3479,12 +3408,9 @@ object ZipxPlugin extends AutoPlugin: Some(payload), ) log.info("zipx: posted sticky comment") - end match catch case scala.util.control.NonFatal(e) => log.warn(s"zipx: sticky comment failed (${e.getMessage}); continuing") - end match - end postModverComment private final case class GhComment(id: Long, body: String) derives JsonDecoder private final case class GhEvent(number: Option[Int], pull_request: Option[GhPr]) derives JsonDecoder @@ -3508,6 +3434,5 @@ object ZipxPlugin extends AutoPlugin: proc #< new java.io.ByteArrayInputStream(body.getBytes(java.nio.charset.StandardCharsets.UTF_8)) val code = piped.!(scala.sys.process.ProcessLogger(out.append, err.append)) if code == 0 then Right(out.toString) else Left(err.toString) - end osProcess end ZipxPlugin diff --git a/modules/sbt-plugin/src/main/scala/zipx/plugin/ZipxSelf.scala b/modules/sbt-plugin/src/main/scala/zipx/plugin/ZipxSelf.scala index 59f9947..cbf845a 100644 --- a/modules/sbt-plugin/src/main/scala/zipx/plugin/ZipxSelf.scala +++ b/modules/sbt-plugin/src/main/scala/zipx/plugin/ZipxSelf.scala @@ -9,3 +9,4 @@ object ZipxSelf: def emit(group: String, artifact: String, from: Class[?], version: Option[String] = None): Plugin = zipx.core.ZipxSelf.plugin(group, artifact, from, version).fold(msg => sys.error(msg), identity) +end ZipxSelf diff --git a/modules/shell/src/main/scala/zipx/shell/Command.scala b/modules/shell/src/main/scala/zipx/shell/Command.scala index 47eab47..050ac47 100644 --- a/modules/shell/src/main/scala/zipx/shell/Command.scala +++ b/modules/shell/src/main/scala/zipx/shell/Command.scala @@ -70,6 +70,7 @@ final case class Exec(program: Word, args: List[Word]) extends InlineCommand: def inlineLines: ShLines = Word.spaceJoined(program :: args) override def rawFragments: List[String] = (program :: args).flatMap(_.rawFragments) +end Exec object Exec: @@ -101,7 +102,6 @@ final case class Continued(program: Word, argLines: List[List[Word]], continuati val emitted = first :: rest val joined = emitted.dropRight(1).map(_ + " \\") :+ emitted.last ShLines.stack(joined.head, joined.tail) - end inlineLines override def rawFragments: List[String] = (program :: argLines.flatten).flatMap(_.rawFragments) end Continued @@ -110,6 +110,7 @@ object Continued: inline def apply(inline program: String, argLines: List[List[Word]]): Continued = Continued(Word.Lit(ShText.unsafeMake(ProgramName(program).unwrap)), argLines) +end Continued // The three list operators join onto the left side's *last* line, so `Continued(…) | wc -l` puts the pipe after the // final continuation rather than after the first line. @@ -134,6 +135,7 @@ final case class Redirect(command: InlineCommand, target: Word, append: Boolean, command.inlineLines ++ ShLines.composed(s" $fd$arrow ") ++ target.lines(Quoting.Unquoted) override def rawFragments: List[String] = command.rawFragments ++ target.rawFragments +end Redirect final case class RedirectFd(command: InlineCommand, from: FileDescriptor, to: FileDescriptor) extends InlineCommand: def inlineLines: ShLines = command.inlineLines ++ ShLines.composed(s" ${from.unwrap}>&${to.unwrap}") @@ -164,6 +166,7 @@ object Assign: case Plain, Local, Export, ReadOnly inline def apply(inline name: String, value: Word): Assign = Assign(VarName(name), value) +end Assign final case class If( cond: ShTest, @@ -192,12 +195,14 @@ final case class ForIn(name: VarName, words: List[Word], body: Block) extends Co body.lines(ctx.nested) ::: ctx.line("done") override def rawFragments: List[String] = words.flatMap(_.rawFragments) ++ body.rawFragments +end ForIn final case class While(cond: ShTest, body: Block) extends Command: def lines(ctx: Script.Ctx): List[ScriptLine] = ctx.emit(ShLines.of("while ") ++ cond.lines + "; do") ::: body.lines(ctx.nested) ::: ctx.line("done") override def rawFragments: List[String] = cond.rawFragments ++ body.rawFragments +end While /** @param quoted * `<<'TAG'`, so the body is not expanded. On by default: an unexpanded body cannot have its `$` interpreted. @@ -254,6 +259,7 @@ object Raw: bad match case Some(err) => Left(err) case None => Right(Raw(split.map(ScriptLine.unsafeMake))) +end Raw /** **Escape hatch.** [[Raw]] for a single-command position: a list cannot promise one line, so this holds exactly one. */ @@ -268,3 +274,4 @@ object RawLine: inline def apply(inline text: String): RawLine = RawLine(ScriptLine(text)) def make(text: String): Either[String, RawLine] = ScriptLine.make(text).map(RawLine(_)) +end RawLine diff --git a/modules/shell/src/main/scala/zipx/shell/Script.scala b/modules/shell/src/main/scala/zipx/shell/Script.scala index 01d7715..48ca8ab 100644 --- a/modules/shell/src/main/scala/zipx/shell/Script.scala +++ b/modules/shell/src/main/scala/zipx/shell/Script.scala @@ -58,4 +58,5 @@ object Script: val IndentWidth = 2 val root: Ctx = Ctx(0) + end Ctx end Script diff --git a/modules/shell/src/main/scala/zipx/shell/interpolator.scala b/modules/shell/src/main/scala/zipx/shell/interpolator.scala index b02f1c3..62bdb80 100644 --- a/modules/shell/src/main/scala/zipx/shell/interpolator.scala +++ b/modules/shell/src/main/scala/zipx/shell/interpolator.scala @@ -38,4 +38,3 @@ private def shMacro(sc: Expr[StringContext], args: Expr[Seq[Word]])(using Quotes .zipAll(splices.map(Some(_)), None, None) .flatMap((part, splice) => part.toList ++ splice.toList) '{ Word.Cat(${ Expr.ofList(words.toList) }) } -end shMacro diff --git a/modules/shell/src/main/scala/zipx/shell/primitives.scala b/modules/shell/src/main/scala/zipx/shell/primitives.scala index 4543d37..7042ea6 100644 --- a/modules/shell/src/main/scala/zipx/shell/primitives.scala +++ b/modules/shell/src/main/scala/zipx/shell/primitives.scala @@ -107,6 +107,7 @@ object ExitCode extends Newtype[Int]: val Success: ExitCode = ExitCode(0) val Failure: ExitCode = ExitCode(1) +end ExitCode /** Only single digits are portable: `10>` is a valid fd in bash but ambiguous with `1` followed by `0>` in POSIX sh. */ type FileDescriptor = FileDescriptor.Type @@ -119,6 +120,7 @@ object FileDescriptor extends Newtype[Int]: val Stdin: FileDescriptor = FileDescriptor(0) val Stdout: FileDescriptor = FileDescriptor(1) val Stderr: FileDescriptor = FileDescriptor(2) +end FileDescriptor // Literal Strings, not Regex values: `validate` is inlined and a compiled Regex is not comptime-evaluable. object Patterns: @@ -129,3 +131,4 @@ object Patterns: inline val NoWhitespaceOrQuotes = "[^\\s'\"\\\\\\x00-\\x1F\\x7F]+" inline val ProgramName = "[A-Za-z0-9_./+-]+" +end Patterns diff --git a/modules/shell/src/test/scala/zipx/shell/PrimitivesSpec.scala b/modules/shell/src/test/scala/zipx/shell/PrimitivesSpec.scala index 833c4a3..bfa73e9 100644 --- a/modules/shell/src/test/scala/zipx/shell/PrimitivesSpec.scala +++ b/modules/shell/src/test/scala/zipx/shell/PrimitivesSpec.scala @@ -16,7 +16,6 @@ object PrimitivesSpec extends ZIOSpecDefault: val UnitSeparator: Char = 0x1f.toChar val Space: Char = ' ' val Delete: Char = 0x7f.toChar - end Ascii private def surroundedBy(c: Char): String = s"a${c}b" diff --git a/modules/syntax/src/main/scala/zipx/syntax/BuildSbtDiff.scala b/modules/syntax/src/main/scala/zipx/syntax/BuildSbtDiff.scala index f42fed4..ee872a0 100644 --- a/modules/syntax/src/main/scala/zipx/syntax/BuildSbtDiff.scala +++ b/modules/syntax/src/main/scala/zipx/syntax/BuildSbtDiff.scala @@ -55,8 +55,6 @@ object BuildSbtDiff: // An aggregator names what it aggregates without reading it, and runs nothing of its own. val readers = h.named.collect { case (n, s) if modulesOf(n).nonEmpty && s.refs.exists(names) => n } Some((projects ++ readers).flatMap(modulesOf).filter(_.ciRelevant).map(n => n.id: String)) - end if - end compare /** Wrapped as an object body so bare settings parse. */ private def parse(source: String): Option[Stats] = @@ -78,7 +76,6 @@ object BuildSbtDiff: } Stats(byName.toMap, bare) } - end parse private def bodyOf(tree: Tree)(using Context): Option[List[Tree]] = tree match diff --git a/modules/syntax/src/main/scala/zipx/syntax/CatalogApply.scala b/modules/syntax/src/main/scala/zipx/syntax/CatalogApply.scala index e643425..8c54d34 100644 --- a/modules/syntax/src/main/scala/zipx/syntax/CatalogApply.scala +++ b/modules/syntax/src/main/scala/zipx/syntax/CatalogApply.scala @@ -79,7 +79,6 @@ object CatalogApply: } .toRight(s"zipx: catalog has no Ship / ShipGroup constructor for '${bump.identity}' ${bump.from}") } - end applyOneShipBump private def findShipVersionLit(tree: Tree, identity: String)(using Context): Option[Tree] = var found: Option[Tree] = None @@ -115,7 +114,6 @@ object CatalogApply: case _ => () go(tree) found - end findShipVersionLit private def versionLit(args: List[Tree]): Option[Tree] = val stringArgs = args.collect { @@ -154,7 +152,6 @@ object CatalogApply: case _ => () go(tree) found - end findCtor private def isCtor(tree: Tree, ctor: String): Boolean = tree match diff --git a/modules/syntax/src/main/scala/zipx/syntax/CatalogDiff.scala b/modules/syntax/src/main/scala/zipx/syntax/CatalogDiff.scala index d91ea64..7ac3013 100644 --- a/modules/syntax/src/main/scala/zipx/syntax/CatalogDiff.scala +++ b/modules/syntax/src/main/scala/zipx/syntax/CatalogDiff.scala @@ -49,8 +49,6 @@ object CatalogDiff: movedLibs.map(l => CatalogChange.LibMoved(l.coordinate)) ++ movedActions.map(a => CatalogChange.ActionMoved(a.name) ) - end match - end changes private def restoreLib(text: String, moved: Lib, base: Lib): String = text.replace( diff --git a/modules/syntax/src/main/scala/zipx/syntax/CatalogSource.scala b/modules/syntax/src/main/scala/zipx/syntax/CatalogSource.scala index 095604e..3e9df78 100644 --- a/modules/syntax/src/main/scala/zipx/syntax/CatalogSource.scala +++ b/modules/syntax/src/main/scala/zipx/syntax/CatalogSource.scala @@ -16,7 +16,6 @@ final case class CatalogConstructors( scala: Option[ScalaVersion], ): def coords: List[ZipxCoord] = libs ++ plugins -end CatalogConstructors object CatalogSource: @@ -147,7 +146,6 @@ object CatalogSource: byName.get("version").orElse(pos.lift(1)), byName.get("sha").orElse(pos.lift(2)), ) - end named private def mkLib(g: String, a: String, v: String): Option[Lib] = for diff --git a/modules/workflow/src/main/scala/zipx/workflow/Ast.scala b/modules/workflow/src/main/scala/zipx/workflow/Ast.scala index e167305..f2dc37c 100644 --- a/modules/workflow/src/main/scala/zipx/workflow/Ast.scala +++ b/modules/workflow/src/main/scala/zipx/workflow/Ast.scala @@ -61,6 +61,7 @@ enum DayOfWeek: case Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday def cronValue: Int = ordinal +end DayOfWeek /** Ranges live in [[CronHour]] / [[CronMinute]] rather than a render-time check, so `Cron.daily(hour = 24)` is a * compile error and [[render]] is total. [[Cron.Raw]] covers the step-value and range forms the variants cannot say. @@ -204,8 +205,6 @@ object Step: s"(keys: ${step.`with`.keys.toList.sorted.mkString(", ")})" ) case _ => None - end match - end problem def validate(step: Step): Either[String, Step] = problem(step).toLeft(step) diff --git a/modules/workflow/src/main/scala/zipx/workflow/Render.scala b/modules/workflow/src/main/scala/zipx/workflow/Render.scala index 051124f..9a1e68f 100644 --- a/modules/workflow/src/main/scala/zipx/workflow/Render.scala +++ b/modules/workflow/src/main/scala/zipx/workflow/Render.scala @@ -70,7 +70,6 @@ object Render: Seq("jobs" -> jobsYaml(wf.jobs)) ) Yaml.Mapping(entries.map((k, v) => (Yaml.Scalar(k), v))) - end workflowYaml private def concurrencyYaml(c: Concurrency): Yaml = val cancel = c.cancelInProgress match @@ -101,7 +100,6 @@ object Render: t.workflowDispatch.map(d => "workflow_dispatch" -> dispatchYaml(d)) ++ (if t.workflowCall then Seq("workflow_call" -> Yaml.NullValue) else Nil) Yaml.Mapping.fromStringKeys(entries*) - end triggersYaml private def dispatchYaml(dispatch: WorkflowDispatch): Yaml = if dispatch.inputs.isEmpty then Yaml.NullValue @@ -214,11 +212,9 @@ object Render: if sawMatrix then nested else nested ++ Chunk(Yaml.Scalar("matrix") -> Yaml.Mapping(Chunk(Yaml.Scalar("include") -> include))) (k, Yaml.Mapping(withMatrix)) - end match case other => other }) case other => other - end nestMatrixInclude /** The file as written to `.github/actions//action.yml`, header included. */ def renderComposite(action: CompositeAction): Either[String, String] = @@ -249,7 +245,6 @@ object Render: else Seq("outputs" -> Yaml.Mapping(Chunk.from(outputEntries)))) ++ Seq("runs" -> runs) Yaml.Mapping.fromStringKeys(entries*) - end compositeYaml /** GitHub's convention for a single runner label is a scalar; the derived codec always emits a sequence. */ private def collapseSingletonRunsOn(job: Yaml): Yaml = job match diff --git a/modules/workflow/src/main/scala/zipx/workflow/YamlPrinter.scala b/modules/workflow/src/main/scala/zipx/workflow/YamlPrinter.scala index ad7fb47..30de16f 100644 --- a/modules/workflow/src/main/scala/zipx/workflow/YamlPrinter.scala +++ b/modules/workflow/src/main/scala/zipx/workflow/YamlPrinter.scala @@ -106,7 +106,6 @@ object YamlPrinter: firstEntry = false } case _ => writeNode(sb, elem, indent + 2, isTopLevel = false) - end match first = false } @@ -123,7 +122,6 @@ object YamlPrinter: if line.nonEmpty then appendIndent(sb, contentIndent) sb.append(line) } - end writeBlockScalar private def writeScalarKey(sb: java.lang.StringBuilder, key: Yaml): Unit = key match case Yaml.Scalar(value, tag) => writeScalar(sb, value, tag) @@ -146,7 +144,6 @@ object YamlPrinter: case c if c < 0x20 => sb.append("\\u").append(String.format("%04x", Int.box(c.toInt))) case c => sb.append(c) idx += 1 - end while sb.append('"') else sb.append(value) @@ -170,7 +167,6 @@ object YamlPrinter: if c == '#' && idx > 0 && value.charAt(idx - 1) == ' ' then return true idx += 1 false - end needsQuoting private def isSpecialValue(value: String): Boolean = value match case "null" | "~" | "Null" | "NULL" => true @@ -192,7 +188,6 @@ object YamlPrinter: val c1 = value.charAt(1) if c1 >= '0' && c1 <= '9' then return true false - end looksNumeric private def appendIndent(sb: java.lang.StringBuilder, indent: Int): Unit = var idx = 0 diff --git a/modules/workflow/src/main/scala/zipx/workflow/names.scala b/modules/workflow/src/main/scala/zipx/workflow/names.scala index 8ede280..02a83eb 100644 --- a/modules/workflow/src/main/scala/zipx/workflow/names.scala +++ b/modules/workflow/src/main/scala/zipx/workflow/names.scala @@ -156,7 +156,6 @@ object RawExpr extends Newtype[String]: else if input.split("\\$\\{\\{", -1).length != input.split("\\}\\}", -1).length then s"unbalanced \\$${{ }} in raw expression '$input'" else true -end RawExpr /** UTC: GitHub runs schedules in UTC and offers no timezone field. */ type CronHour = CronHour.Type @@ -166,6 +165,7 @@ object CronHour extends Newtype[Int]: else true val Midnight: CronHour = CronHour(0) +end CronHour type CronMinute = CronMinute.Type object CronMinute extends Newtype[Int]: @@ -174,6 +174,7 @@ object CronMinute extends Newtype[Int]: else true val Zero: CronMinute = CronMinute(0) +end CronMinute /** **Escape hatch** for [[Cron.Raw]]. Field contents are unvalidated: a step value, a `1-5` range and `MON` are what * the typed variants cannot say. Untrimmed input is rejected rather than trimmed, so what renders is what was written.