diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c1d0e208..69f8abc1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: with: jvm: 8 - name: Test - run: sbtn ++${{ matrix.SCALA_VERSION }} test + run: ./mill -i '__[${{ matrix.SCALA_VERSION }}].test' bin-compat: runs-on: ubuntu-latest @@ -37,8 +37,8 @@ jobs: - uses: coursier/setup-action@v1.3 with: jvm: 8 - - name: Test - run: sbtn +mimaReportBinaryIssues + - name: Check + run: ./mill -i __.mimaReportBinaryIssues format: runs-on: ubuntu-latest @@ -72,7 +72,7 @@ jobs: - name: Wrap GPG binary run: .github/scripts/wrap-gpg.sh - name: Release - run: sbtn ci-release + run: ./mill -i publishSonatype __.publishArtifacts env: PGP_PASSPHRASE: ${{ secrets.PUBLISH_SECRET_KEY_PASSWORD }} PGP_SECRET: ${{ secrets.PUBLISH_SECRET_KEY }} diff --git a/.gitignore b/.gitignore index 7d18b4f3..5641b795 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -target/ +out/ .bsp/ .idea .idea_modules @@ -7,4 +7,3 @@ target/ .bloop/ .metals/ .vscode/ -project/metals.sbt diff --git a/.mill-version b/.mill-version new file mode 100644 index 00000000..2427081a --- /dev/null +++ b/.mill-version @@ -0,0 +1 @@ +0.11.7-109-59a5cb \ No newline at end of file diff --git a/build.sbt b/build.sbt deleted file mode 100644 index 7ff0789b..00000000 --- a/build.sbt +++ /dev/null @@ -1,134 +0,0 @@ -import sbtcrossproject.crossProject - -import Settings._ - -inThisBuild(List( - organization := "com.github.alexarchambault", - homepage := Some(url("https://github.com/alexarchambault/case-app")), - licenses := Seq("Apache 2.0" -> url("http://opensource.org/licenses/Apache-2.0")), - developers := List( - Developer( - "alexarchambault", - "Alexandre Archambault", - "", - url("https://github.com/alexarchambault") - ) - ) -)) - -lazy val annotations = crossProject(JSPlatform, JVMPlatform, NativePlatform) - .nativeConfigure(_.disablePlugins(MimaPlugin)) - .jvmSettings(Mima.settings) - .jsSettings(Mima.settings) - .settings( - shared, - caseAppPrefix - ) - -lazy val annotationsJVM = annotations.jvm -lazy val annotationsJS = annotations.js -lazy val annotationsNative = annotations.native - -lazy val util = crossProject(JSPlatform, JVMPlatform, NativePlatform) - .nativeConfigure(_.disablePlugins(MimaPlugin)) - .jvmSettings(Mima.settings) - .jsSettings(Mima.settings) - .settings( - shared, - caseAppPrefix, - libraryDependencies ++= { - val sv = scalaVersion.value - if (sv.startsWith("2.")) - Seq( - Deps.shapeless.value, - Deps.scalaCompiler.value % "provided", - Deps.scalaReflect.value % "provided" - ) - else - Nil - } - ) - -lazy val utilJVM = util.jvm -lazy val utilJS = util.js -lazy val utilNative = util.native - -lazy val cats = crossProject(JSPlatform, JVMPlatform) - .dependsOn(core) - .jvmSettings(Mima.settings) - .jsSettings(Mima.settings) - .settings( - shared, - name := "case-app-cats", - libraryDependencies ++= Seq( - Deps.catsEffect3.value - ), - libraryDependencies += Deps.utest.value % Test, - testFrameworks += new TestFramework("utest.runner.Framework") - ) - -lazy val catsJVM = cats.jvm -lazy val catsJS = cats.js - -lazy val cats2 = crossProject(JSPlatform, JVMPlatform) - .dependsOn(core) - .jvmSettings(Mima.settings) - .jsSettings(Mima.settings) - .settings( - shared, - name := "case-app-cats-effect-2", - Compile / sources := (catsJVM / Compile / sources).value, - libraryDependencies ++= Seq( - Deps.catsEffect2.value - ), - libraryDependencies += Deps.utest.value % Test, - testFrameworks += new TestFramework("utest.runner.Framework") - ) - -lazy val cats2JVM = cats2.jvm -lazy val cats2JS = cats2.js - -lazy val core = crossProject(JSPlatform, JVMPlatform, NativePlatform) - .nativeConfigure(_.disablePlugins(MimaPlugin)) - .dependsOn(annotations, util) - .jvmSettings(Mima.settings) - .jsSettings(Mima.settings) - .settings( - shared, - name := "case-app", - libraryDependencies ++= { - val sv = scalaVersion.value - val maybeDataClass = - if (sv.startsWith("2.")) Seq(Deps.dataClass % Provided) - else Nil - Seq(Deps.sourcecode.value) ++ maybeDataClass - } - ) - -lazy val coreJVM = core.jvm -lazy val coreJS = core.js -lazy val coreNative = core.native - -lazy val tests = crossProject(JSPlatform, JVMPlatform, NativePlatform) - .disablePlugins(MimaPlugin) - .dependsOn(core) - .settings( - shared, - caseAppPrefix, - publish / skip := true, - libraryDependencies ++= Seq( - Deps.pprint.value % Test, - Deps.utest.value % Test - ), - testFrameworks += new TestFramework("utest.runner.Framework") - ) - -lazy val testsJVM = tests.jvm -lazy val testsJS = tests.js -lazy val testsNative = tests.native - -disablePlugins(MimaPlugin) -publish / skip := true -crossScalaVersions := Nil - -Global / onChangedBuildSource := ReloadOnSourceChanges diff --git a/build.sc b/build.sc new file mode 100644 index 00000000..4100b877 --- /dev/null +++ b/build.sc @@ -0,0 +1,427 @@ +import $ivy.`com.github.lolgab::mill-mima::0.0.23` + +import com.github.lolgab.mill.mima.Mima +import mill._ +import mill.scalajslib._ +import mill.scalalib._ +import mill.scalanativelib._ + +import scala.concurrent.duration.DurationInt + +object Versions { + def scala212 = "2.12.19" + def scala213 = "2.13.14" + def scala3 = "3.3.3" + def scala = Seq(scala212, scala213, scala3) + + def scalaJs = "1.16.0" + def scalaNative = "0.5.2" +} + +object Deps { + def catsEffect = ivy"org.typelevel::cats-effect::3.5.4" + def catsEffect2 = ivy"org.typelevel::cats-effect::2.5.5" + def dataClass = ivy"io.github.alexarchambault::data-class:0.2.6" + def macroParadise = ivy"org.scalamacros:::paradise:2.1.1" + def pprint = ivy"com.lihaoyi::pprint::0.9.0" + def scalaCompiler(sv: String) = ivy"org.scala-lang:scala-compiler:$sv" + def scalaReflect(sv: String) = ivy"org.scala-lang:scala-reflect:$sv" + def shapeless = ivy"com.chuusai::shapeless::2.3.12" + def sourcecode = ivy"com.lihaoyi::sourcecode::0.4.2" + def utest = ivy"com.lihaoyi::utest::0.8.3" +} + +object annotations extends Module { + object jvm extends Cross[AnnotationsJvm](Versions.scala) + object js extends Cross[AnnotationsJs](Versions.scala) + object native extends Cross[AnnotationsNative](Versions.scala) + + trait AnnotationsJvm extends Annotations with MimaChecks + trait AnnotationsJs extends Annotations with CaseAppScalaJsModule with MimaChecks + trait AnnotationsNative extends Annotations with CaseAppScalaNativeModule + + trait Annotations extends CrossSbtModule with CrossSources with CaseAppPublishModule { + def artifactName = "case-app-annotations" + } +} + +object util extends Module { + object jvm extends Cross[UtilJvm](Versions.scala) + object js extends Cross[UtilJs](Versions.scala) + object native extends Cross[UtilNative](Versions.scala) + + trait UtilJvm extends Util with MimaChecks + trait UtilJs extends Util with CaseAppScalaJsModule with MimaChecks + trait UtilNative extends Util with CaseAppScalaNativeModule + + trait Util extends CrossSbtModule with CrossSources with CaseAppPublishModule { + def artifactName = "case-app-util" + def ivyDeps = T { + if (scalaVersion().startsWith("2.")) + Agg(Deps.shapeless) + else + Agg() + } + def compileIvyDeps = T { + if (scalaVersion().startsWith("2.")) + Agg( + Deps.scalaCompiler(scalaVersion()), + Deps.scalaReflect(scalaVersion()) + ) + else + Agg() + } + } +} + +object core extends Module { + object jvm extends Cross[CoreJvm](Versions.scala) + object js extends Cross[CoreJs](Versions.scala) + object native extends Cross[CoreNative](Versions.scala) + + trait CoreJvm extends Core with MimaChecks { + def moduleDeps = Seq( + annotations.jvm(), + util.jvm() + ) + + object test extends Tests with TestCrossSources { + def ivyDeps = Agg(Deps.utest) + def testFramework = "utest.runner.Framework" + } + } + trait CoreJs extends Core with CaseAppScalaJsModule with MimaChecks { + def moduleDeps = Seq( + annotations.js(), + util.js() + ) + + object test extends SbtModuleTests with ScalaJSTests with TestCrossSources { + def ivyDeps = Agg(Deps.utest) + def testFramework = "utest.runner.Framework" + } + } + trait CoreNative extends Core with CaseAppScalaNativeModule { + def moduleDeps = Seq( + annotations.native(), + util.native() + ) + + object test extends SbtModuleTests with ScalaNativeTests with TestCrossSources { + def ivyDeps = Agg(Deps.utest) + def testFramework = "utest.runner.Framework" + } + } + + trait Core extends CrossSbtModule with CrossSources with CaseAppPublishModule { + def artifactName = "case-app" + def ivyDeps = T { + val maybeDataClass = + if (scalaVersion().startsWith("2.")) Agg(Deps.dataClass) + else Agg.empty + Agg(Deps.sourcecode) ++ maybeDataClass + } + def scalacOptions = T { + val maybeMacroAnn = + if (scalaVersion().startsWith("2.13.")) + Seq("-Ymacro-annotations") + else + Nil + super.scalacOptions() ++ maybeMacroAnn + } + def scalacPluginIvyDeps = T { + if (scalaVersion().startsWith("2.12.")) + Agg(Deps.macroParadise) + else + Agg.empty + } + } +} + +object cats extends Module { + object jvm extends Cross[CatsJvm](Versions.scala) + object js extends Cross[CatsJs](Versions.scala) + + trait CatsJvm extends Cats with MimaChecks { + def moduleDeps = Seq(core.jvm()) + + object test extends Tests with TestCrossSources { + def ivyDeps = Agg(Deps.utest) + def testFramework = "utest.runner.Framework" + } + } + trait CatsJs extends Cats with CaseAppScalaJsModule with MimaChecks { + def moduleDeps = Seq(core.js()) + + object test extends SbtModuleTests with ScalaJSTests with TestCrossSources { + def ivyDeps = Agg(Deps.utest) + def testFramework = "utest.runner.Framework" + } + } + + trait Cats extends CrossSbtModule with CrossSources with CaseAppPublishModule { + def artifactName = "case-app-cats" + def ivyDeps = Agg(Deps.catsEffect) + } +} + +object cats2 extends Module { + object jvm extends Cross[Cats2Jvm](Versions.scala) + object js extends Cross[Cats2Js](Versions.scala) + + trait Cats2Jvm extends Cats2 with MimaChecks { + def moduleDeps = Seq(core.jvm()) + def sources = cats.jvm().sources() + + object test extends Tests with TestCrossSources { + def ivyDeps = Agg(Deps.utest) + def testFramework = "utest.runner.Framework" + } + } + trait Cats2Js extends Cats2 with CaseAppScalaJsModule with MimaChecks { + def moduleDeps = Seq(core.js()) + def sources = cats.js().sources() + + object test extends SbtModuleTests with ScalaJSTests with TestCrossSources { + def ivyDeps = Agg(Deps.utest) + def testFramework = "utest.runner.Framework" + } + } + + trait Cats2 extends CrossSbtModule with CrossSources with CaseAppPublishModule { + def artifactName = "case-app-cats-effect-2" + def ivyDeps = Agg(Deps.catsEffect2) + } +} + +object tests extends Module { + object jvm extends Cross[TestsJvm](Versions.scala) + object js extends Cross[TestsJs](Versions.scala) + object native extends Cross[TestsNative](Versions.scala) + + trait TestsJvm extends Tests0 { + def moduleDeps = Seq(core.jvm()) + + object test extends Tests with TestCrossSources { + def ivyDeps = Agg( + Deps.pprint, + Deps.utest + ) + def testFramework = "utest.runner.Framework" + } + } + trait TestsJs extends Tests0 with CaseAppScalaJsModule { + def moduleDeps = Seq(core.js()) + + object test extends SbtModuleTests with ScalaJSTests with TestCrossSources { + def ivyDeps = Agg( + Deps.pprint, + Deps.utest + ) + def testFramework = "utest.runner.Framework" + } + } + trait TestsNative extends Tests0 with CaseAppScalaNativeModule { + def moduleDeps = Seq(core.native()) + + object test extends SbtModuleTests with ScalaNativeTests with TestCrossSources { + def ivyDeps = Agg( + Deps.pprint, + Deps.utest + ) + def testFramework = "utest.runner.Framework" + } + } + + trait Tests0 extends CrossSbtModule with CrossSources { + def ivyDeps = T { + val maybeDataClass = + if (scalaVersion().startsWith("2.")) Agg(Deps.dataClass) + else Agg.empty + Agg(Deps.sourcecode) ++ maybeDataClass + } + /*def scalacOptions = T { + val maybeMacroAnn = + if (scalaVersion().startsWith("2.13.")) + Seq("-Ymacro-annotations") + else + Nil + super.scalacOptions() ++ maybeMacroAnn + } + def scalacPluginIvyDeps = T { + if (scalaVersion().startsWith("2.12.")) + Agg(Deps.macroParadise) + else + Agg.empty + }*/ + } +} + +trait CaseAppScalaJsModule extends ScalaJSModule { + def scalaJSVersion = Versions.scalaJs +} + +trait CaseAppScalaNativeModule extends ScalaNativeModule { + def scalaNativeVersion = Versions.scalaNative +} + +trait CaseAppPublishModule extends PublishModule { + import CaseAppPublishModule._ + import mill.scalalib.publish._ + def pomSettings = PomSettings( + description = artifactName(), + organization = "com.github.alexarchambault", + url = "https://github.com/alexarchambault/case-app", + licenses = Seq(License.`Apache-2.0`), + versionControl = VersionControl.github("alexarchambault", "case-app"), + developers = Seq( + Developer("alexarchambault", "Alex Archambault", "https://github.com/alexarchambault") + ) + ) + def publishVersion = T(buildVersion) +} + +object CaseAppPublishModule { + private lazy val latestTaggedVersion = + os.proc("git", "describe", "--abbrev=0", "--tags", "--match", "v*") + .call().out + .trim() + private lazy val buildVersion = { + val gitHead = os.proc("git", "rev-parse", "HEAD").call().out.trim() + val maybeExactTag = scala.util.Try { + os.proc("git", "describe", "--exact-match", "--tags", "--always", gitHead) + .call().out + .trim() + .stripPrefix("v") + } + maybeExactTag.toOption.getOrElse { + val commitsSinceTaggedVersion = + os.proc("git", "rev-list", gitHead, "--not", latestTaggedVersion, "--count") + .call().out.trim() + .toInt + val gitHash = os.proc("git", "rev-parse", "--short", "HEAD").call().out.trim() + s"${latestTaggedVersion.stripPrefix("v")}-$commitsSinceTaggedVersion-$gitHash-SNAPSHOT" + } + } +} + +object CrossSources { + def extraSourcesDirs(sv: String, millSourcePath: os.Path, scope: String): Seq[PathRef] = { + val (maj, majMin) = sv.split('.') match { + case Array(maj0, min, _*) => (maj0, s"$maj0.$min") + case _ => sys.error(s"Malformed Scala version: $sv") + } + val baseDir = millSourcePath / os.up / "shared" / "src" / scope + Seq( + PathRef(baseDir / "scala"), + PathRef(baseDir / s"scala-$maj"), + PathRef(baseDir / s"scala-$majMin") + ) + } +} + +trait CrossSources extends SbtModule { + import CrossSources._ + def sources = T.sources { + val sv = scalaVersion() + super.sources() ++ extraSourcesDirs(sv, millSourcePath, "main") + } +} + +trait TestCrossSources extends SbtModule { + import CrossSources._ + def sources = T.sources { + val sv = scalaVersion() + super.sources() ++ extraSourcesDirs(sv, millSourcePath, "test") + } +} + +trait MimaChecks extends Mima { + + def mimaPreviousVersions = T { + os.proc( + "git", + "tag", + "--merged", + "HEAD^", + "--contains", + "993ac3020db84ba06231e0268c920c9f8b9f3520" + ) + .call() + .out.lines() + .map(_.trim) + .filter(_.startsWith("v")) + .map(_.stripPrefix("v")) + } + + def mimaPreviousArtifacts = T { + val versions = mimaPreviousVersions().distinct + mill.api.Result.Success( + Agg.from( + versions.map(version => + ivy"${pomSettings().organization}:${artifactId()}:$version" + ) + ) + ) + } +} + +object ci extends Module { + + def publishSonatype(tasks: mill.main.Tasks[PublishModule.PublishData]) = T.command { + publishSonatype0( + data = define.Target.sequence(tasks.value)(), + log = T.ctx().log + ) + } + + def publishSonatype0( + data: Seq[PublishModule.PublishData], + log: mill.api.Logger + ): Unit = { + + val credentials = sys.env("SONATYPE_USERNAME") + ":" + sys.env("SONATYPE_PASSWORD") + val pgpPassword = sys.env("PGP_PASSPHRASE") + val timeout = 10.minutes + + val artifacts = data.map { + case PublishModule.PublishData(a, s) => + (s.map { case (p, f) => (p.path, f) }, a) + } + + val isRelease = { + val versions = artifacts.map(_._2.version).toSet + val set = versions.map(!_.endsWith("-SNAPSHOT")) + assert( + set.size == 1, + s"Found both snapshot and non-snapshot versions: ${versions.toVector.sorted.mkString(", ")}" + ) + set.head + } + val publisher = new scalalib.publish.SonatypePublisher( + uri = "https://oss.sonatype.org/service/local", + snapshotUri = "https://oss.sonatype.org/content/repositories/snapshots", + credentials = credentials, + signed = true, + // format: off + gpgArgs = Seq( + "--detach-sign", + "--batch=true", + "--yes", + "--pinentry-mode", "loopback", + "--passphrase", pgpPassword, + "--armor", + "--use-agent" + ), + // format: on + readTimeout = timeout.toMillis.toInt, + connectTimeout = timeout.toMillis.toInt, + log = log, + awaitTimeout = timeout.toMillis.toInt, + stagingRelease = isRelease + ) + + publisher.publishAll(isRelease, artifacts: _*) + } + +} diff --git a/mill b/mill new file mode 100755 index 00000000..c4157ae6 --- /dev/null +++ b/mill @@ -0,0 +1,206 @@ +#!/usr/bin/env sh + +# This is a wrapper script, that automatically download mill from GitHub release pages +# You can give the required mill version with --mill-version parameter +# If no version is given, it falls back to the value of DEFAULT_MILL_VERSION +# +# Project page: https://github.com/lefou/millw +# Script Version: 0.4.7 +# +# If you want to improve this script, please also contribute your changes back! +# +# Licensed under the Apache License, Version 2.0 + +set -e + +if [ -z "${DEFAULT_MILL_VERSION}" ] ; then + DEFAULT_MILL_VERSION="0.11.0" +fi + + +if [ -z "${GITHUB_RELEASE_CDN}" ] ; then + GITHUB_RELEASE_CDN="" +fi + + +MILL_REPO_URL="https://github.com/com-lihaoyi/mill" + +if [ -z "${CURL_CMD}" ] ; then + CURL_CMD=curl +fi + +# Explicit commandline argument takes precedence over all other methods +if [ "$1" = "--mill-version" ] ; then + shift + if [ "x$1" != "x" ] ; then + MILL_VERSION="$1" + shift + else + echo "You specified --mill-version without a version." 1>&2 + echo "Please provide a version that matches one provided on" 1>&2 + echo "${MILL_REPO_URL}/releases" 1>&2 + false + fi +fi + +# Please note, that if a MILL_VERSION is already set in the environment, +# We reuse it's value and skip searching for a value. + +# If not already set, read .mill-version file +if [ -z "${MILL_VERSION}" ] ; then + if [ -f ".mill-version" ] ; then + MILL_VERSION="$(head -n 1 .mill-version 2> /dev/null)" + elif [ -f ".config/mill-version" ] ; then + MILL_VERSION="$(head -n 1 .config/mill-version 2> /dev/null)" + fi +fi + +if [ -z "${MILL_DOWNLOAD_PATH}" ] ; then + if [ -n "${XDG_CACHE_HOME}" ] ; then + MILL_DOWNLOAD_PATH="${XDG_CACHE_HOME}/mill/download" + else + MILL_DOWNLOAD_PATH="${HOME}/.cache/mill/download" + fi +fi + +# If not already set, try to fetch newest from Github +if [ -z "${MILL_VERSION}" ] ; then + # TODO: try to load latest version from release page + echo "No mill version specified." 1>&2 + echo "You should provide a version via '.mill-version' file or --mill-version option." 1>&2 + + mkdir -p "${MILL_DOWNLOAD_PATH}" + LANG=C touch -d '1 hour ago' "${MILL_DOWNLOAD_PATH}/.expire_latest" 2>/dev/null || ( + # we might be on OSX or BSD which don't have -d option for touch + # but probably a -A [-][[hh]mm]SS + touch "${MILL_DOWNLOAD_PATH}/.expire_latest"; touch -A -010000 "${MILL_DOWNLOAD_PATH}/.expire_latest" + ) || ( + # in case we still failed, we retry the first touch command with the intention + # to show the (previously suppressed) error message + LANG=C touch -d '1 hour ago' "${MILL_DOWNLOAD_PATH}/.expire_latest" + ) + + # POSIX shell variant of bash's -nt operator, see https://unix.stackexchange.com/a/449744/6993 + # if [ "${MILL_DOWNLOAD_PATH}/.latest" -nt "${MILL_DOWNLOAD_PATH}/.expire_latest" ] ; then + if [ -n "$(find -L "${MILL_DOWNLOAD_PATH}/.latest" -prune -newer "${MILL_DOWNLOAD_PATH}/.expire_latest")" ]; then + # we know a current latest version + MILL_VERSION=$(head -n 1 "${MILL_DOWNLOAD_PATH}"/.latest 2> /dev/null) + fi + + if [ -z "${MILL_VERSION}" ] ; then + # we don't know a current latest version + echo "Retrieving latest mill version ..." 1>&2 + LANG=C ${CURL_CMD} -s -i -f -I ${MILL_REPO_URL}/releases/latest 2> /dev/null | grep --ignore-case Location: | sed s'/^.*tag\///' | tr -d '\r\n' > "${MILL_DOWNLOAD_PATH}/.latest" + MILL_VERSION=$(head -n 1 "${MILL_DOWNLOAD_PATH}"/.latest 2> /dev/null) + fi + + if [ -z "${MILL_VERSION}" ] ; then + # Last resort + MILL_VERSION="${DEFAULT_MILL_VERSION}" + echo "Falling back to hardcoded mill version ${MILL_VERSION}" 1>&2 + else + echo "Using mill version ${MILL_VERSION}" 1>&2 + fi +fi + +MILL="${MILL_DOWNLOAD_PATH}/${MILL_VERSION}" + +try_to_use_system_mill() { + MILL_IN_PATH="$(command -v mill || true)" + + if [ -z "${MILL_IN_PATH}" ]; then + return + fi + + UNIVERSAL_SCRIPT_MAGIC="@ 2>/dev/null # 2>nul & echo off & goto BOF" + + if ! head -c 128 "${MILL_IN_PATH}" | grep -qF "${UNIVERSAL_SCRIPT_MAGIC}"; then + if [ -n "${MILLW_VERBOSE}" ]; then + echo "Could not determine mill version of ${MILL_IN_PATH}, as it does not start with the universal script magic2" 1>&2 + fi + return + fi + + # Roughly the size of the universal script. + MILL_VERSION_SEARCH_RANGE="2403" + MILL_IN_PATH_VERSION=$(head -c "${MILL_VERSION_SEARCH_RANGE}" "${MILL_IN_PATH}" |\ + sed -n 's/^.*-DMILL_VERSION=\([^\s]*\) .*$/\1/p' |\ + head -n 1) + + if [ -z "${MILL_IN_PATH_VERSION}" ]; then + echo "Could not determine mill version, even though ${MILL_IN_PATH} has the universal script magic" 1>&2 + return + fi + + if [ "${MILL_IN_PATH_VERSION}" = "${MILL_VERSION}" ]; then + MILL="${MILL_IN_PATH}" + fi +} +try_to_use_system_mill + +# If not already downloaded, download it +if [ ! -s "${MILL}" ] ; then + + # support old non-XDG download dir + MILL_OLD_DOWNLOAD_PATH="${HOME}/.mill/download" + OLD_MILL="${MILL_OLD_DOWNLOAD_PATH}/${MILL_VERSION}" + if [ -x "${OLD_MILL}" ] ; then + MILL="${OLD_MILL}" + else + case $MILL_VERSION in + 0.0.* | 0.1.* | 0.2.* | 0.3.* | 0.4.* ) + DOWNLOAD_SUFFIX="" + DOWNLOAD_FROM_MAVEN=0 + ;; + 0.5.* | 0.6.* | 0.7.* | 0.8.* | 0.9.* | 0.10.* | 0.11.0-M* ) + DOWNLOAD_SUFFIX="-assembly" + DOWNLOAD_FROM_MAVEN=0 + ;; + *) + DOWNLOAD_SUFFIX="-assembly" + DOWNLOAD_FROM_MAVEN=1 + ;; + esac + + DOWNLOAD_FILE=$(mktemp mill.XXXXXX) + + if [ "$DOWNLOAD_FROM_MAVEN" = "1" ] ; then + DOWNLOAD_URL="https://repo1.maven.org/maven2/com/lihaoyi/mill-dist/${MILL_VERSION}/mill-dist-${MILL_VERSION}.jar" + else + MILL_VERSION_TAG=$(echo $MILL_VERSION | sed -E 's/([^-]+)(-M[0-9]+)?(-.*)?/\1\2/') + DOWNLOAD_URL="${GITHUB_RELEASE_CDN}${MILL_REPO_URL}/releases/download/${MILL_VERSION_TAG}/${MILL_VERSION}${DOWNLOAD_SUFFIX}" + unset MILL_VERSION_TAG + fi + + # TODO: handle command not found + echo "Downloading mill ${MILL_VERSION} from ${DOWNLOAD_URL} ..." 1>&2 + ${CURL_CMD} -f -L -o "${DOWNLOAD_FILE}" "${DOWNLOAD_URL}" + chmod +x "${DOWNLOAD_FILE}" + mkdir -p "${MILL_DOWNLOAD_PATH}" + mv "${DOWNLOAD_FILE}" "${MILL}" + + unset DOWNLOAD_FILE + unset DOWNLOAD_SUFFIX + fi +fi + +if [ -z "$MILL_MAIN_CLI" ] ; then + MILL_MAIN_CLI="${0}" +fi + +MILL_FIRST_ARG="" +if [ "$1" = "--bsp" ] || [ "$1" = "-i" ] || [ "$1" = "--interactive" ] || [ "$1" = "--no-server" ] || [ "$1" = "--repl" ] || [ "$1" = "--help" ] ; then + # Need to preserve the first position of those listed options + MILL_FIRST_ARG=$1 + shift +fi + +unset MILL_DOWNLOAD_PATH +unset MILL_OLD_DOWNLOAD_PATH +unset OLD_MILL +unset MILL_VERSION +unset MILL_REPO_URL + +# We don't quote MILL_FIRST_ARG on purpose, so we can expand the empty value without quotes +# shellcheck disable=SC2086 +exec "${MILL}" $MILL_FIRST_ARG -D "mill.main.cli=${MILL_MAIN_CLI}" "$@" diff --git a/mill.bat b/mill.bat new file mode 100644 index 00000000..bec5bd46 --- /dev/null +++ b/mill.bat @@ -0,0 +1,220 @@ +@echo off + +rem This is a wrapper script, that automatically download mill from GitHub release pages +rem You can give the required mill version with --mill-version parameter +rem If no version is given, it falls back to the value of DEFAULT_MILL_VERSION +rem +rem Project page: https://github.com/lefou/millw +rem Script Version: 0.4.7 +rem +rem If you want to improve this script, please also contribute your changes back! +rem +rem Licensed under the Apache License, Version 2.0 + +rem setlocal seems to be unavailable on Windows 95/98/ME +rem but I don't think we need to support them in 2019 +setlocal enabledelayedexpansion + +if [!DEFAULT_MILL_VERSION!]==[] ( + set "DEFAULT_MILL_VERSION=0.11.0" +) + +if [!GITHUB_RELEASE_CDN!]==[] ( + set "GITHUB_RELEASE_CDN=" +) + +set "MILL_REPO_URL=https://github.com/com-lihaoyi/mill" + +rem %~1% removes surrounding quotes +if [%~1%]==[--mill-version] ( + if not [%~2%]==[] ( + set MILL_VERSION=%~2% + rem shift command doesn't work within parentheses + set "STRIP_VERSION_PARAMS=true" + ) else ( + echo You specified --mill-version without a version. 1>&2 + echo Please provide a version that matches one provided on 1>&2 + echo %MILL_REPO_URL%/releases 1>&2 + exit /b 1 + ) +) + +if not defined STRIP_VERSION_PARAMS GOTO AfterStripVersionParams +rem strip the: --mill-version {version} +shift +shift +:AfterStripVersionParams + +if [!MILL_VERSION!]==[] ( + if exist .mill-version ( + set /p MILL_VERSION=<.mill-version + ) else ( + if exist .config\mill-version ( + set /p MILL_VERSION=<.config\mill-version + ) + ) +) + +if [!MILL_VERSION!]==[] ( + set MILL_VERSION=%DEFAULT_MILL_VERSION% +) + +if [!MILL_DOWNLOAD_PATH!]==[] ( + set MILL_DOWNLOAD_PATH=%USERPROFILE%\.mill\download +) + +rem without bat file extension, cmd doesn't seem to be able to run it +set MILL=%MILL_DOWNLOAD_PATH%\!MILL_VERSION!.bat + +if not exist "%MILL%" ( + set VERSION_PREFIX=%MILL_VERSION:~0,4% + # Since 0.5.0 + set DOWNLOAD_SUFFIX=-assembly + # Since 0.11.0 + set DOWNLOAD_FROM_MAVEN=1 + if [!VERSION_PREFIX!]==[0.0.] ( + set DOWNLOAD_SUFFIX= + set DOWNLOAD_FROM_MAVEN=0 + ) + if [!VERSION_PREFIX!]==[0.1.] ( + set DOWNLOAD_SUFFIX= + set DOWNLOAD_FROM_MAVEN=0 + ) + if [!VERSION_PREFIX!]==[0.2.] ( + set DOWNLOAD_SUFFIX= + set DOWNLOAD_FROM_MAVEN=0 + ) + if [!VERSION_PREFIX!]==[0.3.] ( + set DOWNLOAD_SUFFIX= + set DOWNLOAD_FROM_MAVEN=0 + ) + if [!VERSION_PREFIX!]==[0.4.] ( + set DOWNLOAD_SUFFIX= + set DOWNLOAD_FROM_MAVEN=0 + ) + if [!VERSION_PREFIX!]==[0.5.] ( + set DOWNLOAD_FROM_MAVEN=0 + ) + if [!VERSION_PREFIX!]==[0.6.] ( + set DOWNLOAD_FROM_MAVEN=0 + ) + if [!VERSION_PREFIX!]==[0.7.] ( + set DOWNLOAD_FROM_MAVEN=0 + ) + if [!VERSION_PREFIX!]==[0.8.] ( + set DOWNLOAD_FROM_MAVEN=0 + ) + if [!VERSION_PREFIX!]==[0.9.] ( + set DOWNLOAD_FROM_MAVEN=0 + ) + set VERSION_PREFIX=%MILL_VERSION:~0,5% + if [!VERSION_PREFIX!]==[0.10.] ( + set DOWNLOAD_FROM_MAVEN=0 + ) + set VERSION_PREFIX=%MILL_VERSION:~0,8% + if [!VERSION_PREFIX!]==[0.11.0-M] ( + set DOWNLOAD_FROM_MAVEN=0 + ) + set VERSION_PREFIX= + + for /F "delims=- tokens=1" %%A in ("!MILL_VERSION!") do set MILL_VERSION_BASE=%%A + for /F "delims=- tokens=2" %%A in ("!MILL_VERSION!") do set MILL_VERSION_MILESTONE=%%A + set VERSION_MILESTONE_START=!MILL_VERSION_MILESTONE:~0,1! + if [!VERSION_MILESTONE_START!]==[M] ( + set MILL_VERSION_TAG="!MILL_VERSION_BASE!-!MILL_VERSION_MILESTONE!" + ) else ( + set MILL_VERSION_TAG=!MILL_VERSION_BASE! + ) + + rem there seems to be no way to generate a unique temporary file path (on native Windows) + set DOWNLOAD_FILE=%MILL%.tmp + + if [!DOWNLOAD_FROM_MAVEN]==[1] ( + set DOWNLOAD_URL=https://repo1.maven.org/maven2/com/lihaoyi/mill-dist/!MILL_VERSION!/mill-dist-!MILL_VERSION!.jar + ) else ( + set DOWNLOAD_URL=!GITHUB_RELEASE_CDN!%MILL_REPO_URL%/releases/download/!MILL_VERSION_TAG!/!MILL_VERSION!!DOWNLOAD_SUFFIX! + ) + + echo Downloading mill %MILL_VERSION% from !DOWNLOAD_URL! ... 1>&2 + + if not exist "%MILL_DOWNLOAD_PATH%" mkdir "%MILL_DOWNLOAD_PATH%" + rem curl is bundled with recent Windows 10 + rem but I don't think we can expect all the users to have it in 2019 + where /Q curl + if %ERRORLEVEL% EQU 0 ( + curl -f -L "!DOWNLOAD_URL!" -o "!DOWNLOAD_FILE!" + ) else ( + rem bitsadmin seems to be available on Windows 7 + rem without /dynamic, github returns 403 + rem bitsadmin is sometimes needlessly slow but it looks better with /priority foreground + bitsadmin /transfer millDownloadJob /dynamic /priority foreground "!DOWNLOAD_URL!" "!DOWNLOAD_FILE!" + ) + if not exist "!DOWNLOAD_FILE!" ( + echo Could not download mill %MILL_VERSION% 1>&2 + exit /b 1 + ) + + move /y "!DOWNLOAD_FILE!" "%MILL%" + + set DOWNLOAD_FILE= + set DOWNLOAD_SUFFIX= +) + +set MILL_DOWNLOAD_PATH= +set MILL_VERSION= +set MILL_REPO_URL= + +if [!MILL_MAIN_CLI!]==[] ( + set "MILL_MAIN_CLI=%0" +) + +rem Need to preserve the first position of those listed options +set MILL_FIRST_ARG= +if [%~1%]==[--bsp] ( + set MILL_FIRST_ARG=%1% +) else ( + if [%~1%]==[-i] ( + set MILL_FIRST_ARG=%1% + ) else ( + if [%~1%]==[--interactive] ( + set MILL_FIRST_ARG=%1% + ) else ( + if [%~1%]==[--no-server] ( + set MILL_FIRST_ARG=%1% + ) else ( + if [%~1%]==[--repl] ( + set MILL_FIRST_ARG=%1% + ) else ( + if [%~1%]==[--help] ( + set MILL_FIRST_ARG=%1% + ) + ) + ) + ) + ) +) + +set "MILL_PARAMS=%*%" + +if not [!MILL_FIRST_ARG!]==[] ( + if defined STRIP_VERSION_PARAMS ( + for /f "tokens=1-3*" %%a in ("%*") do ( + set "MILL_PARAMS=%%d" + ) + ) else ( + for /f "tokens=1*" %%a in ("%*") do ( + set "MILL_PARAMS=%%b" + ) + ) +) else ( + if defined STRIP_VERSION_PARAMS ( + for /f "tokens=1-2*" %%a in ("%*") do ( + rem strip %%a - It's the "--mill-version" option. + rem strip %%b - it's the version number that comes after the option. + rem keep %%c - It's the remaining options. + set "MILL_PARAMS=%%c" + ) + ) +) + +"%MILL%" %MILL_FIRST_ARG% -D "mill.main.cli=%MILL_MAIN_CLI%" %MILL_PARAMS% diff --git a/project/Deps.scala b/project/Deps.scala deleted file mode 100644 index 7d0c843b..00000000 --- a/project/Deps.scala +++ /dev/null @@ -1,21 +0,0 @@ -import sbt._ -import sbt.Keys._ - -import org.portablescala.sbtplatformdeps.PlatformDepsPlugin.autoImport._ - -object Deps { - - import Def.setting - - def catsEffect2 = setting("org.typelevel" %%% "cats-effect" % "2.5.5") - def catsEffect3 = setting("org.typelevel" %%% "cats-effect" % "3.5.4") - def dataClass = "io.github.alexarchambault" %% "data-class" % "0.2.6" - def macroParadise = "org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.patch - def pprint = setting("com.lihaoyi" %%% "pprint" % "0.9.0") - def scalaCompiler = setting("org.scala-lang" % "scala-compiler" % scalaVersion.value) - def scalaReflect = setting("org.scala-lang" % "scala-reflect" % scalaVersion.value) - def shapeless = setting("com.chuusai" %%% "shapeless" % "2.3.12") - def sourcecode = setting("com.lihaoyi" %%% "sourcecode" % "0.4.2") - def utest = setting("com.lihaoyi" %%% "utest" % "0.8.3") - -} diff --git a/project/Mima.scala b/project/Mima.scala deleted file mode 100644 index e5000f07..00000000 --- a/project/Mima.scala +++ /dev/null @@ -1,29 +0,0 @@ -import com.typesafe.tools.mima.core._ -import com.typesafe.tools.mima.plugin.MimaPlugin -import sbt._ -import sbt.Keys._ - -import scala.sys.process._ - -object Mima { - - def binaryCompatibilityVersions: Set[String] = - Seq("git", "tag", "--merged", "HEAD^", "--contains", "993ac3020db84ba06231e0268c920c9f8b9f3520") - .!! - .linesIterator - .map(_.trim) - .filter(_.startsWith("v")) - .map(_.stripPrefix("v")) - .toSet - - def settings = Def.settings( - MimaPlugin.autoImport.mimaPreviousArtifacts := { - binaryCompatibilityVersions - .map { ver => - (organization.value % moduleName.value % ver) - .cross(crossVersion.value) - } - } - ) - -} diff --git a/project/Settings.scala b/project/Settings.scala deleted file mode 100644 index ee0dbf42..00000000 --- a/project/Settings.scala +++ /dev/null @@ -1,49 +0,0 @@ -import sbt._ -import sbt.Keys._ - -object Settings { - - private def scala212 = "2.12.19" - private def scala213 = "2.13.14" - private def scala3 = "3.3.3" - - private lazy val isAtLeastScala213 = Def.setting { - import Ordering.Implicits._ - CrossVersion.partialVersion(scalaVersion.value).exists(_ >= (2, 13)) - } - - lazy val shared = Seq( - scalaVersion := scala3, - crossScalaVersions := Seq(scala212, scala213, scala3), - scalacOptions ++= Seq( - "-target:jvm-1.8", - "-feature", - "-deprecation" - ), - libraryDependencies ++= { - CrossVersion.partialVersion(scalaVersion.value) match { - case Some((2, v)) if v <= 12 => - compilerPlugin(Deps.macroParadise) :: Nil - case _ => - // if scala 2.13.0-M4 or later, macro annotations merged into scala-reflect - // https://github.com/scala/scala/pull/6606 - Nil - } - }, - scalacOptions ++= { - if (isAtLeastScala213.value) Seq("-Ymacro-annotations") - else Nil - }, - autoAPIMappings := true - ) - - lazy val caseAppPrefix = - name := { - val shortenedName = name.value - .stripSuffix("JVM") - .stripSuffix("JS") - .stripSuffix("Native") - "case-app-" + shortenedName - } - -} diff --git a/project/build.properties b/project/build.properties deleted file mode 100644 index 04267b14..00000000 --- a/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.9.9 diff --git a/project/plugins.sbt b/project/plugins.sbt deleted file mode 100644 index e5378c07..00000000 --- a/project/plugins.sbt +++ /dev/null @@ -1,7 +0,0 @@ -addSbtPlugin("com.thoughtworks.sbt-api-mappings" % "sbt-api-mappings" % "3.0.2") -addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.12") -addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.3") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0") -addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2") -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.2") -addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.3.2")