diff --git a/build.sbt b/build.sbt
index 518819d..7a61a13 100644
--- a/build.sbt
+++ b/build.sbt
@@ -1,7 +1,7 @@
name := "make-release-notes"
-scalaVersion := "2.13.11"
-scalacOptions ++= Seq("-feature", "-deprecation", "-Werror")
+scalaVersion := "3.3.0"
+scalacOptions ++= Seq("-feature", "-deprecation", "-Werror", "-source:future")
libraryDependencies ++= Seq(
"org.pegdown" % "pegdown" % "1.6.0",
diff --git a/src/main/scala/CommunityProjects.scala b/src/main/scala/CommunityProjects.scala
deleted file mode 100755
index cc4ffb1..0000000
--- a/src/main/scala/CommunityProjects.scala
+++ /dev/null
@@ -1,72 +0,0 @@
-
-case class ProjectInfo(name: String, desc: String, url: String, loc: String) {
-
- def toHtml =
{name}
- {desc}
- Location: {
- for(l <- (loc split "\n"))
- yield {l}
- }
-
-}
-
-/** This class encapsulates parsing/loading community project HTML from
- * the formatted emails people write. These emails are usually dumped
- * into a file called community-projects.txt with a one-line separator between them.
- */
-object CommunityProjects {
-
- def loadHtmlFromFile(file: java.io.File = new java.io.File("community-projects.txt")): String =
- loadCommunityProjects(loadLines(file)) match {
- case projects if projects.nonEmpty => renderProjectHtml(projects)
- case _ => ""
- }
-
-
- def loadLines(file: java.io.File): Seq[String] = {
- val in = new java.io.BufferedReader(new java.io.FileReader(file))
- def read(lines: Vector[String] = Vector.empty): Vector[String] = in.readLine match {
- case null => lines
- case line =>
- read(lines :+ line)
- }
- try read()
- finally in.close()
- }
-
- def loadCommunityProjects(lines: Seq[String]): Seq[ProjectInfo] = {
- val groups: Seq[Seq[String]] = {
- def grouper(left: Seq[String], current: Seq[Seq[String]] = Seq.empty): Seq[Seq[String]] =
- if(left.isEmpty) current
- else grouper(left dropWhile (x => !x.isEmpty) drop 1, current :+ (left takeWhile (x => !x.isEmpty)))
- grouper(lines)
- }
- groups map parseProject
- }
-
-
-
- def fix(header: String)(content: String): String =
- if(content.toLowerCase startsWith header.toLowerCase) content drop header.length
- else sys.error("Expected [" + header + "] found [" + content + "]")
-
- private def parseProject(content: Seq[String]): ProjectInfo =
- content match {
- case Seq(name, desc, url, location @ _*) if !location.isEmpty =>
- ProjectInfo(fix("Name:")(name),
- fix("Description:")(desc),
- fix("URL:")(url),
- fix("Location:")(location.head) + "\n" + (location.tail mkString "\n"))
- case _ => sys.error("Bad project group: " + content.mkString("\n"))
- }
-
- def renderProjectHtml(projects: Seq[ProjectInfo]): String = {
- def list = { for(p <- projects) yield p.toHtml }
- val sb = new StringBuffer
- sb append (" Community Projects
")
- sb append (s" Special thanks to the ${projects.size} projects that have made releases available for this version of Scala!
")
- sb append (list.toString)
- sb.toString
- }
-
-}
\ No newline at end of file
diff --git a/src/main/scala/GitInfo.scala b/src/main/scala/GitInfo.scala
index 2551f9f..56f0a10 100644
--- a/src/main/scala/GitInfo.scala
+++ b/src/main/scala/GitInfo.scala
@@ -1,45 +1,42 @@
-import scala.collection.parallel.CollectionConverters._ // for .par
+import scala.collection.parallel.CollectionConverters.* // for .par
-case class Commit(sha: String, author: String, header: String, body: String) {
+case class Commit(sha: String, author: String, header: String, body: String):
def trimmedHeader = header.take(80)
override def toString = " * " + sha + " (" + author + ") " + header + " - " + body.take(5) + " ..."
-}
-/** Gobal functions for dealing with git. */
-object GitHelper {
- def processGitCommits(gitDir: java.io.File, previousTag: String, currentTag: String): IndexedSeq[Commit] = {
- import sys.process._
+/** Global functions for dealing with git. */
+object GitHelper:
+ def processGitCommits(gitDir: java.io.File, previousTag: String, currentTag: String): IndexedSeq[Commit] =
+ import sys.process.*
val gitFormat = "%h %s" // sha and subject
val log = Process(Seq("git", "--no-pager", "log", s"${previousTag}..${currentTag}", "--format=format:" + gitFormat, "--no-merges", "--topo-order"), gitDir).lazyLines
log.par.map(_.split(" ", 2)).collect {
case Array(sha, title) =>
- val (author :: body) = Process(Seq("git", "--no-pager", "show", sha, "--format=format:%aN%n%b", "--quiet"), gitDir).lazyLines.toList
+ val author :: body =
+ Process(Seq("git", "--no-pager", "show", sha, "--format=format:%aN%n%b", "--quiet"), gitDir)
+ .lazyLines.toList: @unchecked
Commit(sha, author, title, body.mkString("\n"))
}.toVector
- }
def hasFixins(msg: String): Boolean = (
- (msg contains "SI-") /*&& ((msg.toLowerCase contains "fix") || (msg.toLowerCase contains "close"))*/
+ msg.contains("SI-") /*&& ((msg.toLowerCase contains "fix") || msg.toLowerCase.contains("close"))*/
)
val siPattern = java.util.regex.Pattern.compile("(SI-[0-9]+)")
- def fixLinks(commit: Commit)(implicit targetLanguage: TargetLanguage): String = {
+ def fixLinks(commit: Commit)(implicit targetLanguage: TargetLanguage): String =
val searchString = commit.body + commit.header
- val m = siPattern matcher searchString
+ val m = siPattern.matcher(searchString)
val issues = new collection.mutable.ArrayBuffer[String]
- while (m.find()) {
- issues += (m group 1)
- }
+ while m.find() do
+ issues += (m.group(1))
issues map (si => targetLanguage.createHyperLink(s"https://issues.scala-lang.org/browse/$si", si)) mkString ", "
- }
def htmlEncode(s: String) = org.apache.commons.text.StringEscapeUtils.escapeHtml4(s)
-}
-class GitInfo(gitDir: java.io.File, val previousTag: String, val currentTag: String)(implicit targetLanguage: TargetLanguage) {
- import GitHelper._
+class GitInfo(gitDir: java.io.File, val previousTag: String, val currentTag: String)(implicit targetLanguage: TargetLanguage):
+ import GitHelper.*
val commits = processGitCommits(gitDir, previousTag, currentTag)
val authors: Seq[(String, Int)] =
@@ -50,11 +47,11 @@ class GitInfo(gitDir: java.io.File, val previousTag: String, val currentTag: Str
.sortBy(_._2)
val fixCommits =
- for {
+ for
commit <- commits
searchString = commit.body + commit.header
if hasFixins(searchString)
- } yield commit
+ yield commit
private def commitShaLink(sha: String) =
targetLanguage.createHyperLink(s"https://github.com/scala/scala/commit/${sha}", sha)
@@ -62,38 +59,34 @@ class GitInfo(gitDir: java.io.File, val previousTag: String, val currentTag: Str
private def blankLine(): String = targetLanguage.blankLine()
private def header4(msg: String): String = targetLanguage.header4(msg)
- def renderCommitterList: String = {
+ def renderCommitterList: String =
val sb = new StringBuffer
- sb append blankLine()
- sb append header4("A big thank you to all the contributors!")
- sb append targetLanguage.tableHeader("#", "Author")
- for ((author, count) <- authors)
- sb append targetLanguage.tableRow(count.toString, author)
- sb append targetLanguage.tableEnd
+ sb.append(blankLine())
+ sb.append(header4("A big thank you to all the contributors!"))
+ sb.append(targetLanguage.tableHeader("#", "Author"))
+ for (author, count) <- authors do
+ sb.append(targetLanguage.tableRow(count.toString, author))
+ sb.append(targetLanguage.tableEnd)
sb.toString
- }
- def renderCommitList: String = {
+ def renderCommitList: String =
val sb = new StringBuffer
- sb append blankLine()
- sb append header4("Complete commit list!")
- sb append targetLanguage.tableHeader("sha", "Title")
- for (commit <- commits)
- sb append targetLanguage.tableRow(commitShaLink(commit.sha), commit.trimmedHeader)
- sb append targetLanguage.tableEnd
+ sb.append(blankLine())
+ sb.append(header4("Complete commit list!"))
+ sb.append(targetLanguage.tableHeader("sha", "Title"))
+ for commit <- commits do
+ sb.append(targetLanguage.tableRow(commitShaLink(commit.sha), commit.trimmedHeader))
+ sb.append(targetLanguage.tableEnd)
sb.toString
- }
- def renderFixedIssues: String = {
+ def renderFixedIssues: String =
val sb = new StringBuffer
- sb append blankLine()
- sb append header4(s"Commits and the issues they fixed since ${previousTag}")
- sb append targetLanguage.tableHeader("Issue(s)", "Commit", "Message")
- for (commit <- fixCommits)
- sb append targetLanguage.tableRow(fixLinks(commit), commitShaLink(commit.sha), commit.trimmedHeader)
- sb append targetLanguage.tableEnd
- sb append blankLine()
+ sb.append(blankLine())
+ sb.append(header4(s"Commits and the issues they fixed since ${previousTag}"))
+ sb.append(targetLanguage.tableHeader("Issue(s)", "Commit", "Message"))
+ for commit <- fixCommits do
+ sb.append(targetLanguage.tableRow(fixLinks(commit), commitShaLink(commit.sha), commit.trimmedHeader))
+ sb.append(targetLanguage.tableEnd)
+ sb.append(blankLine())
sb.toString
- }
-}
diff --git a/src/main/scala/IO.scala b/src/main/scala/IO.scala
deleted file mode 100644
index 6fd889a..0000000
--- a/src/main/scala/IO.scala
+++ /dev/null
@@ -1,9 +0,0 @@
-import java.io.File
-
-object IO {
- def write(f: java.io.File, contents: String): Unit = {
- val buf = new java.io.BufferedWriter(new java.io.FileWriter(f))
- try buf.write(contents)
- finally buf.close()
- }
-}
diff --git a/src/main/scala/JiraIssues.scala b/src/main/scala/JiraIssues.scala
deleted file mode 100644
index 66647b1..0000000
--- a/src/main/scala/JiraIssues.scala
+++ /dev/null
@@ -1,39 +0,0 @@
-
-//curl -D- -X GET https://issues.scala-lang.org/sr/jira.issueviews:searchrequest-printable/11907/SearchRequest-11907.html?tempMax=1000
-
-object JiraIssues {
-
- def slurp(in: java.io.InputStream): String = {
- val wrapped = new java.io.BufferedReader(new java.io.InputStreamReader(in))
- def read(buf: StringBuffer): String =
- wrapped.readLine match {
- case null => buf.toString
- case line =>
- buf append s"$line\n"
- read(buf)
- }
- try read(new StringBuffer)
- finally in.close()
- }
-
- def parseXml(in: String): scala.xml.NodeSeq = {
- import scala.xml.parsing.XhtmlParser
- XhtmlParser(scala.io.Source.fromString(in))
- }
-
- def makeOpenIssuesString = {
- import java.net.URL
- val url = new URL("https://issues.scala-lang.org/sr/jira.issueviews:searchrequest-printable/11907/SearchRequest-11907.html?tempMax=1000")
-
- val htmlString = slurp(url.openStream)
-
- val dropFront = htmlString drop (htmlString.indexOfSlice("""", dropFront.length))
-
- //val html = parseXml(htmlString)
-
- //val div = html \\ "div#printable-content"
- //div.toString
- dropEnd + "
"
- }
-}
\ No newline at end of file
diff --git a/src/main/scala/MakeDownloadPage.scala b/src/main/scala/MakeDownloadPage.scala
index 1664805..8cc23c2 100644
--- a/src/main/scala/MakeDownloadPage.scala
+++ b/src/main/scala/MakeDownloadPage.scala
@@ -1,21 +1,21 @@
import java.util.Date
-import java.text._
-import scala.concurrent._
-import scala.concurrent.duration._
+import java.text.*
+import scala.concurrent.*
+import scala.concurrent.duration.*
import ExecutionContext.Implicits.global
+import java.nio.file.{Files, Paths}
-class MakeDownloadPage(version: String, releaseDate: Date = new Date()) {
- def write() = {
+class MakeDownloadPage(version: String, releaseDate: Date = new Date()):
+ def write() =
require(!version.startsWith("v"), "version should *not* start with 'v'")
val fileName = s"${format("yyyy-MM-dd")}-$version.md"
- IO.write(new java.io.File(fileName), page)
+ Files.write(Paths.get(fileName), page.getBytes)
println(s"cp $fileName ../scala-lang/_downloads/")
println("# to prepare your scala-lang PR")
- }
// get size of `url` without actually downloading it
- def humanSize(url: String): Future[String] = Future {
- import scala.sys.process._
+ def humanSize(url: String): Future[String] = Future:
+ import scala.sys.process.*
println("## fetching size of "+ url)
scala.util.Try {
val responseHeader = Process(s"curl -m 5 --silent -D - -X HEAD $url").lazyLines
@@ -26,27 +26,23 @@ class MakeDownloadPage(version: String, releaseDate: Date = new Date()) {
case meh if meh < 1024 => ""
case kilo if kilo < 1024*1024 => f"${bytes.toDouble/1024}%.0fK"
case big => f"${bytes.toDouble/(1024*1024)}%.2fM"
- })} match {
+ })} match
case Some((status, humanSize)) if isGoodStatus(status) =>
humanSize
case _ =>
println(s"## warning: could not fetch $url")
""
- }
- }
def isGoodStatus(status: String): Boolean =
Seq("200 OK", "302 found", "HTTP/2 200").exists(status.contains)
- def resourceArchive(cls: String, name: String, ext: String, desc: String): Future[String] = {
+ def resourceArchive(cls: String, name: String, ext: String, desc: String): Future[String] =
val fileName = s"$name-$version.$ext"
val fullUrl = s"https://downloads.lightbend.com/scala/$version/$fileName"
resource(cls, fileName, desc, fullUrl, fullUrl)
- }
- def resource(cls: String, fileName: String, desc: String, fullUrl: String, urlForSize: String): Future[String] = {
+ def resource(cls: String, fileName: String, desc: String, fullUrl: String, urlForSize: String): Future[String] =
humanSize(urlForSize) map (size => s"""[$cls, "$fileName", "$fullUrl", "$desc", "$size"]""")
- }
def defaultClass = """"-non-main-sys""""
def unixClass = """"-main-unixsys""""
@@ -74,8 +70,7 @@ class MakeDownloadPage(version: String, releaseDate: Date = new Date()) {
)).map(_.mkString(",\n ")), 30.seconds)
// note: first and last lines must be exactly "---"
- def page: String = {
-s"""---
+ def page: String = s"""---
title: Scala $version
start: ${format("dd MMMM yyyy")}
layout: downloadpage
@@ -90,5 +85,3 @@ resources: [
]
---
"""
- }
-}
diff --git a/src/main/scala/MakeReleaseNotes.scala b/src/main/scala/MakeReleaseNotes.scala
index 6c4b337..19a3cbc 100644
--- a/src/main/scala/MakeReleaseNotes.scala
+++ b/src/main/scala/MakeReleaseNotes.scala
@@ -1,97 +1,86 @@
import java.util.Date
-import java.text._
+import java.text.*
import java.io.BufferedReader
+import java.nio.file.{Files, Paths}
import scala.io.Source
-object MakeReleaseNotes {
+object MakeReleaseNotes:
def main(args: Array[String]): Unit =
- args match {
+ args match
case Array(prevVersion, version, release) =>
genPR(prevVersion, version, release)
case Array(prevVersion, version, release, gitDir) =>
genPR(prevVersion, version, release, gitDir)
- }
- def genPR(prevVersion: String, version: String, release: String, gitDir: String = s"${sys.env("HOME")}/git/scala") = {
+ def genPR(prevVersion: String, version: String, release: String, gitDir: String = s"${sys.env("HOME")}/git/scala") =
val date = new SimpleDateFormat("yyyy/MM/dd").parse(release)
new MakeDownloadPage(version, date).write()
MakeReleaseNotes(new java.io.File(gitDir), version, s"v$prevVersion", s"v$version", MarkDown, date)
- }
- def write(page: String, version: String, releaseDate: Date, ext: String) = {
+ def write(page: String, version: String, releaseDate: Date, ext: String) =
def format(fmt: String) = new SimpleDateFormat(fmt).format(releaseDate)
require(!version.startsWith("v"), "version should *not* start with 'v'")
val fileName = s"${format("yyyy-MM-dd")}-release-notes-$version.$ext"
- IO.write(new java.io.File(fileName), page)
+ Files.write(Paths.get(fileName), page.getBytes)
println("# generated " + fileName)
- if (ext == "md") {
+ if ext == "md" then
println(s"cp $fileName ../scala-lang/_posts/")
println(s"# don't forget to\n${scala.util.Properties.envOrElse("EDITOR", "mate")} ../scala-lang/download/scala2.md ../scala-lang/_config.yml")
println("# and, to prepare and sanity check your scala-lang PR:")
println(s"maruku --html $fileName")
- }
- }
def apply(scalaDir: String, version: String, previousTag: String, currentTag: String, releaseDate: Date): Unit =
Seq(Html, MarkDown).foreach(fmt =>
apply(new java.io.File(scalaDir), version, previousTag, currentTag, fmt, releaseDate))
- def apply(scalaDir: java.io.File, version: String, previousTag: String, currentTag: String, targetLanguage: TargetLanguage = MarkDown, releaseDate: Date = new Date()): Unit = {
- val out = targetLanguage match {
+ def apply(scalaDir: java.io.File, version: String, previousTag: String, currentTag: String, targetLanguage: TargetLanguage = MarkDown, releaseDate: Date = new Date()): Unit =
+ val out = targetLanguage match
case Html => new java.io.File("release-notes.html")
case MarkDown => new java.io.File(s"release-notes-${currentTag}.md")
- }
val notes = makeReleaseNotes(scalaDir, version, previousTag, currentTag)(targetLanguage)
write(notes, currentTag.dropWhile(_ == 'v'), releaseDate, targetLanguage.ext)
- }
- private def parseHandWrittenNotes(file: java.io.File = new java.io.File("hand-written.md")): String = {
- import org.pegdown._
+ private def parseHandWrittenNotes(file: java.io.File = new java.io.File("hand-written.md")): String =
+ import org.pegdown.*
val parser = new PegDownProcessor
val in = new java.io.BufferedReader(new java.io.FileReader(file))
def read(buf: StringBuffer): String =
- in.readLine match {
+ in.readLine match
case null => buf.toString
case line =>
- buf append s"${line}\n"
+ buf.append(s"${line}\n")
read(buf)
- }
val content =
try read(new StringBuffer)
finally in.close()
- parser markdownToHtml content
- }
+ parser.markdownToHtml(content)
private def stripTripleDashedHtmlComments(s: String): String =
s.replaceAll("""(?ims)""", "")
- private def makeReleaseNotes(scalaDir: java.io.File, version: String, previousTag: String, currentTag: String)(implicit targetLanguage: TargetLanguage): String = {
- def rawHandWrittenNotes(file: java.io.File = new java.io.File(s"hand-written.md")): String = {
- val lines: List[String] = if (file.exists) {
+ private def makeReleaseNotes(scalaDir: java.io.File, version: String, previousTag: String, currentTag: String)(implicit targetLanguage: TargetLanguage): String =
+ def rawHandWrittenNotes(file: java.io.File = new java.io.File(s"hand-written.md")): String =
+ val lines: List[String] = if file.exists then
val src = Source.fromFile(file)
src.getLines().toList
- } else Nil
+ else Nil
// if you don't have the next line, sub-bullets would be screwed!
// please take this case into account and comment out 2 next lines and uncomment the line after!
- val newLines = lines.map(x => if (x.startsWith(" *")) "\n" + x.stripPrefix(" ") else x)
+ val newLines = lines.map(x => if x.startsWith(" *") then "\n" + x.stripPrefix(" ") else x)
val bulletFixed = newLines.mkString("\n")
val commentsStripped = stripTripleDashedHtmlComments(bulletFixed)
commentsStripped.replaceAll("\\$version", version)
- }
val info = new GitInfo(scalaDir, previousTag, currentTag)
- // val communityProjects = CommunityProjects.loadHtmlFromFile()
- import info.{ currentTag => _, _ }
- // Known issues
- // ${JiraIssues.makeOpenIssuesString}
+ import info.{ currentTag as _, * }
- targetLanguage match {
+ targetLanguage match
case Html => s"""
@@ -116,7 +105,3 @@ title: "Scala ${currentTag drop 1} is now available!"
---
${rawHandWrittenNotes()}
"""
- }
-
- }
-}
diff --git a/src/main/scala/TargetLanguage.scala b/src/main/scala/TargetLanguage.scala
index f4861dc..5dd9ff3 100644
--- a/src/main/scala/TargetLanguage.scala
+++ b/src/main/scala/TargetLanguage.scala
@@ -1,4 +1,4 @@
-sealed trait TargetLanguage {
+sealed trait TargetLanguage:
def createHyperLink(link: String, content: String): String
def blankLine(): String
def header4(msg: String): String
@@ -8,8 +8,8 @@ sealed trait TargetLanguage {
def tableRow(firstColumn: String, secondColumn: String, thirdColumn: String): String
def tableEnd: String
def ext: String
-}
-case object MarkDown extends TargetLanguage {
+
+case object MarkDown extends TargetLanguage:
val ext = "md"
def createHyperLink(link: String, content: String): String =
s"[$content]($link)"
@@ -29,17 +29,16 @@ $firstColumn | $secondColumn | $thirdColumn
def tableRow(firstColumn: String, secondColumn: String, thirdColumn: String): String = s"$firstColumn | $secondColumn | ${escapeHtml(thirdColumn)}\n"
def tableEnd: String = "\n"
- def markdownEncode(s: String): String = s.flatMap {
+ def markdownEncode(s: String): String = s.flatMap:
case c if (List('*', '`', '[', ']', '#').contains(c)) => "\\" + c
case x => x.toString
- }
- def escapeHtml(s: String): String = Html.htmlEncode(s).flatMap {
+ def escapeHtml(s: String): String = Html.htmlEncode(s).flatMap:
case '|' => "|" // it would destroy tables!
case c => c.toString
- }
-}
-case object Html extends TargetLanguage {
+end MarkDown
+
+case object Html extends TargetLanguage:
val ext = "html"
def createHyperLink(link: String, content: String): String =
s"""$content"""
@@ -56,4 +55,4 @@ case object Html extends TargetLanguage {
def tableEnd: String = ""
def htmlEncode(s: String) = org.apache.commons.text.StringEscapeUtils.escapeHtml4(s)
-}
+end Html