|
1 |
| -import scala.util.Try |
2 |
| -import coursier.* |
3 |
| -import coursier.given |
4 |
| -import coursier.graph.DependencyTree |
5 |
| -import scala.util.matching.Regex |
6 |
| -import upickle.default.* |
7 |
| -import scala.io.StdIn.readLine |
8 | 1 | import Dependencies.*
|
9 |
| -import scala.concurrent.Future |
10 |
| -import scala.concurrent.ExecutionContext.Implicits.global |
11 |
| -import ox.* |
12 |
| -import caseapp._ |
13 |
| - |
14 |
| -case class Params(file: String, moduleName: String, overwrite: Boolean = false, yes: Boolean = false, skip: List[String] = Nil, moduleDep: Option[String] = None) |
15 |
| - |
16 |
| -var params: Params = _ |
17 |
| - |
18 |
| -private def publish(flag: Option[String]) = |
19 |
| - val file = params.moduleDep match |
20 |
| - case None => params.file |
21 |
| - case Some(dep) => |
22 |
| - val copy = os.temp(os.read(os.pwd / params.file), suffix = ".scala") |
23 |
| - val moduleDep = s"${Config.organization}::$dep::${Config.developmentVersion}" |
24 |
| - os.write.append(copy, s"\n//> using dep ${moduleDep}") |
25 |
| - copy.toString |
26 |
| - os.proc("scala-cli", "--power", "publish", "local", "--cross", flag.map(f => s"--$f").toList, "--organization", Config.organization, "--project-version", Config.developmentVersion.toString, file).call() |
27 | 2 |
|
28 | 3 | @main
|
29 | 4 | def main(args: String*) =
|
30 |
| - Utility.requireCmd("scala-cli") |
31 |
| - params = CaseApp.parse[Params](args.toSeq) match |
32 |
| - case Left(err) => |
33 |
| - println(err) |
| 5 | + try |
| 6 | + Utility.requireCmd("scala-cli") |
| 7 | + val overwrite = args.contains("--overwrite") |
| 8 | + val yes = args.contains("--yes") |
| 9 | + val config = Config.loadFromEnv |
| 10 | + val exceptionsPath = os.pwd / "changelog" / "exceptions.txt" |
| 11 | + val exceptions = if os.exists(exceptionsPath) then os.read(exceptionsPath).split("\n").map(_.trim).toSeq else Seq.empty |
| 12 | + val changelogChecker = ChangelogChecker(Config.loadFromEnv, exceptions, overwrite, yes) |
| 13 | + changelogChecker.check(os.pwd / "Toolkit.scala", "toolkit", Platform.Jvm) |
| 14 | + changelogChecker.check(os.pwd / "Toolkit.scala", "toolkit", Platform.Native) |
| 15 | + changelogChecker.check(os.pwd / "Toolkit.js.scala", "toolkit", Platform.Js) |
| 16 | + val toolkitTestFile = addToolkitDependency(os.pwd / "ToolkitTest.scala", config.organization, config.developmentVersion) |
| 17 | + changelogChecker.check(toolkitTestFile, "toolkit-test", Platform.Jvm) |
| 18 | + changelogChecker.check(toolkitTestFile, "toolkit-test", Platform.Native) |
| 19 | + changelogChecker.check(toolkitTestFile, "toolkit-test", Platform.Js) |
| 20 | + catch |
| 21 | + case e: ChangelogException => |
| 22 | + Console.err.println("Exiting with failure status (1). " + e.getMessage) |
34 | 23 | sys.exit(1)
|
35 |
| - case Right((params, leftover)) => params |
36 |
| - |
37 |
| - val platformsSkipped = Config.platforms.filterNot(p => params.skip.contains(p._1)).toList |
38 |
| - |
39 |
| - println("Publishing locally to validate the dependency tree...") |
40 |
| - |
41 |
| - val trees = scoped { |
42 |
| - forkAllHold( |
43 |
| - for |
44 |
| - (_, option, _) <- platformsSkipped |
45 |
| - yield () => publish(option) |
46 |
| - ).join() |
47 |
| - |
48 |
| - forkAllHold( |
49 |
| - for |
50 |
| - crossVersion <- Config.crossVersions |
51 |
| - (_, _, platform) <- platformsSkipped |
52 |
| - versionSuffix = platform.map(p => s"${p}_").getOrElse("") + crossVersion |
53 |
| - previousSnapshot = fork(Dep.resolve(Config.organization, params.moduleName, versionSuffix, Config.releaseVersion)) |
54 |
| - currentSnapshot = fork(Dep.resolve(Config.organization, params.moduleName, versionSuffix, Config.developmentVersion)) |
55 |
| - yield () => (versionSuffix, previousSnapshot.join(), currentSnapshot.join()) |
56 |
| - ).join() |
57 |
| - } |
58 |
| - |
59 |
| - trees.foreach((checkTree _).tupled) |
60 |
| -end main |
61 |
| - |
62 |
| -def checkTree(releasedVersion: String, previousSnapshot: Dep, currentSnapshot: Dep) = |
63 |
| - println(s"\nChecking tree for $releasedVersion...") |
64 |
| - |
65 |
| - val summary = SnapshotDiffValidator.summarize(previousSnapshot, currentSnapshot) |
66 |
| - if summary.illegalDiffs.nonEmpty then |
67 |
| - println(s"For $releasedVersion found diffs illegal to introduce on this update type: ${summary.updateType}") |
68 |
| - println(s"Illegal diffs: \n${summary.illegalDiffs.mkString(" - ", "\n - ", "\n")}") |
69 |
| - val isException = os.read(os.pwd / "changelog" / "exceptions.txt").split("\n").contains(currentSnapshot.version.toString) |
70 |
| - if isException then |
71 |
| - println(currentSnapshot.version.toString + " is explicit exception, ignoring illegal diffs") |
72 |
| - else |
73 |
| - if params.overwrite then |
74 |
| - println("Could not generate changelog due to illegal diffs.") |
75 |
| - println("Exiting with failure status (1)") |
76 |
| - sys.exit(1) |
77 |
| - |
78 |
| - val diffsFound = summary.diffs.nonEmpty |
79 |
| - |
80 |
| - if diffsFound then |
81 |
| - println("Found diffs in development version: \n" + summary.diffs.mkString(" - ", "\n - ", "")) |
82 |
| - else |
83 |
| - println("No diffs found") |
84 |
| - |
85 |
| - val generatedChangelog = Changelog.generate(params.moduleName, summary) |
86 |
| - val changelogDir = Config.changelogDir / Config.developmentVersion.toString |
87 |
| - val baseFileName = s"${params.moduleName}_${Config.developmentVersion}_${releasedVersion}_changelog" |
88 |
| - val changelogJson = changelogDir / "json" / s"${baseFileName}.json" |
89 |
| - if !os.exists(changelogJson) then |
90 |
| - println("No changelog found, comparing with empty changelog.") |
91 |
| - os.makeDir.all(changelogDir / "json") |
92 |
| - val parsedChangelog = if(os.exists(changelogJson)) then read[Changelog](os.read(changelogJson)) else Changelog(params.moduleName, Set.empty, Set.empty, Map.empty) |
93 |
| - def changelogsDiff = generatedChangelog.diff(parsedChangelog) |
94 |
| - |
95 |
| - if changelogsDiff.nonEmpty then |
96 |
| - println("Found diffs in changelog: \n" + changelogsDiff.get) |
97 |
| - if !params.overwrite then |
98 |
| - println("Exiting with failure status (1). Please run with --overwrite to overwrite the expected changelog.") |
99 |
| - sys.exit(1) |
100 |
| - else |
101 |
| - val input = if(params.yes) then "y" else readLine("Overwrite symbols? (y/N)") |
102 |
| - if input == "y" || input == "Y" then |
103 |
| - os.write.over(changelogJson, write(generatedChangelog)) |
104 |
| - val changelogReadable = generatedChangelog.toMd |
105 |
| - val changelogMd = changelogDir / s"${baseFileName}.md" |
106 |
| - os.write.over(changelogMd, changelogReadable) |
107 |
| - os.write.append(changelogMd, "\n## Full dependency tree\n") |
108 |
| - os.write.append(changelogMd, currentSnapshot.toMdTree + "\n") |
109 |
| - println("Overwritten") |
110 |
| - else |
111 |
| - println("Changes rejected") |
112 |
| - sys.exit(1) |
113 |
| - else |
114 |
| - println("No diffs found in changelog") |
115 |
| - |
116 |
| -object SnapshotDiffValidator: |
117 |
| - |
118 |
| - def summarize(previous: Dep, current: Dep): DiffSummary = |
119 |
| - val versionDiff = Version.compareVersions(previous.version, current.version) |
120 |
| - |
121 |
| - def traverseDep(oldDep: Option[Dep], newDep: Option[Dep], enteredFrom: Option[Dep] = None): List[Diff] = |
122 |
| - def traverseDepTupled(enteredFrom: Dep)(pair: (Option[Dep], Option[Dep])) = traverseDep(pair._1, pair._2, Some(enteredFrom)) |
123 |
| - (oldDep, newDep) match |
124 |
| - case (Some(oldDep), Some(newDep)) => |
125 |
| - if oldDep.version == newDep.version then Nil |
126 |
| - else |
127 |
| - val diff = DepUpdated(oldDep, newDep, enteredFrom) |
128 |
| - val pairs = oldDep.deps.map(dep => (Some(dep), newDep.deps.find(_.id == dep.id))) |
129 |
| - val newDeps = newDep.deps.filterNot(dep => oldDep.deps.exists(_.id == dep.id)).map(dep => (None, Some(dep))) |
130 |
| - val diffs = pairs.flatMap(traverseDepTupled(newDep)) ++ newDeps.flatMap(traverseDepTupled(newDep)) |
131 |
| - diff :: diffs |
132 |
| - case (Some(oldDep), None) => Removed(oldDep, enteredFrom) :: oldDep.deps.flatMap(dep => traverseDep(Some(dep), None, Some(oldDep))).toList |
133 |
| - case (None, Some(newDep)) => Added(newDep, enteredFrom) :: newDep.deps.flatMap(dep => traverseDep(None, Some(dep), Some(newDep))).toList |
134 |
| - case _ => Nil |
135 |
| - |
136 |
| - val diffs: List[Diff] = traverseDep(Some(previous), Some(current)) |
137 |
| - val illegalDiffs: List[IllegalDiff] = diffs |
138 |
| - .map(diff => IllegalDiff(diff, getLeastOrderLegalUpdate(versionDiff, diff))) |
139 |
| - .filter(_.leastOrderLegalUpdate > versionDiff) |
140 |
| - DiffSummary(versionDiff, diffs, illegalDiffs) |
141 |
| - |
142 | 24 |
|
143 |
| - def getLeastOrderLegalUpdate(versionDiff: VersionDiff, diff: Diff): VersionDiff = |
144 |
| - diff match |
145 |
| - case _: Removed => VersionDiff.MajorUpdate |
146 |
| - case _: Added => VersionDiff.MinorUpdate |
147 |
| - case DepUpdated(oldDep, newDep, _) => |
148 |
| - Version.compareVersions(oldDep.version, newDep.version) |
149 |
| - |
150 |
| -object Utility: |
151 |
| - def requireCmd(cmd: String): Unit = |
152 |
| - if Try(os.proc("which", cmd).call()).isFailure then |
153 |
| - println(s"Please install $cmd") |
154 |
| - sys.exit(1) |
| 25 | +private def addToolkitDependency(file: os.Path, organization: String, version: Version): os.Path = |
| 26 | + val copy = os.temp(os.read(file), suffix = ".scala") |
| 27 | + val moduleDep = s"$organization::toolkit::$version" |
| 28 | + os.write.append(copy, s"\n//> using dep ${moduleDep}") |
| 29 | + copy |
0 commit comments