Skip to content

Commit e229668

Browse files
committed
Rework changelog
- Reduce the number of args to increase usability - Test without invoking scala-cli - Introduce Platform, Config, ChangelogException - Refactor here and there
1 parent 8366ab3 commit e229668

12 files changed

+364
-457
lines changed

.github/workflows/test.yaml

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,24 @@ jobs:
2020
- name: Setup Scala CLI
2121
uses: VirtusLab/scala-cli-setup@main
2222

23-
- name: Test changelog compliance
24-
run: |
25-
scala-cli changelog -- --module-name toolkit --file Toolkit.scala --skip js
26-
scala-cli changelog -- --module-name toolkit --file Toolkit.js.scala --skip jvm --skip native
27-
scala-cli changelog -- --module-name toolkit-test --file ToolkitTest.scala --module-dep toolkit
23+
- name: Run changelog tests
24+
run: scala-cli test changelog
25+
26+
- name: Check changelog compliance
27+
run: scala-cli changelog
2828

29-
- name: Run test examples
29+
- name: Compile and run examples
3030
run: |
3131
cd examples
3232
for file in *.sc
3333
do
3434
scala-cli "$file"
3535
done
3636
37-
- name: Run examples of tests
37+
- name: Compile and test examples
3838
run: |
3939
cd examples
4040
for file in *.test.scala
4141
do
4242
scala-cli test "$file"
4343
done
44-
- name: Run checks tests
45-
run: scala-cli test checks

changelog/Changelog.scala

Lines changed: 23 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -1,154 +1,29 @@
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
81
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()
272

283
@main
294
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)
3423
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-
14224

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

Comments
 (0)