diff options
Diffstat (limited to 'scalalib/src/publish')
-rw-r--r-- | scalalib/src/publish/Ivy.scala | 59 | ||||
-rw-r--r-- | scalalib/src/publish/JsonFormatters.scala | 11 | ||||
-rw-r--r-- | scalalib/src/publish/Licence.scala | 479 | ||||
-rw-r--r-- | scalalib/src/publish/LocalPublisher.scala | 32 | ||||
-rw-r--r-- | scalalib/src/publish/Pom.scala | 117 | ||||
-rw-r--r-- | scalalib/src/publish/SonatypeHttpApi.scala | 134 | ||||
-rw-r--r-- | scalalib/src/publish/SonatypePublisher.scala | 164 | ||||
-rw-r--r-- | scalalib/src/publish/VersionControl.scala | 131 | ||||
-rw-r--r-- | scalalib/src/publish/package.scala | 3 | ||||
-rw-r--r-- | scalalib/src/publish/settings.scala | 91 |
10 files changed, 1221 insertions, 0 deletions
diff --git a/scalalib/src/publish/Ivy.scala b/scalalib/src/publish/Ivy.scala new file mode 100644 index 00000000..22e26ff6 --- /dev/null +++ b/scalalib/src/publish/Ivy.scala @@ -0,0 +1,59 @@ +package mill.scalalib.publish + +import mill.util.Loose.Agg + +import scala.xml.PrettyPrinter + +object Ivy { + + val head = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + + def apply( + artifact: Artifact, + dependencies: Agg[Dependency] + ): String = { + val xml = + <ivy-module version="2.0" xmlns:e="http://ant.apache.org/ivy/extra"> + <info + organisation={artifact.group} module={artifact.id} revision={artifact.version} status="release"> + <description/> + </info> + <configurations> + <conf name="pom" visibility="public" description=""/> + <conf extends="runtime" name="test" visibility="public" description=""/> + <conf name="provided" visibility="public" description=""/> + <conf name="optional" visibility="public" description=""/> + <conf name="compile" visibility="public" description=""/> + <conf extends="compile" name="runtime" visibility="public" description=""/> + </configurations> + + <publications> + <artifact name={artifact.id} type="pom" ext="pom" conf="pom"/> + <artifact name={artifact.id} type="jar" ext="jar" conf="compile"/> + <artifact name={artifact.id} type="src" ext="jar" conf="compile" e:classifier="sources"/> + <artifact name={artifact.id} type="doc" ext="jar" conf="compile" e:classifier="javadoc"/> + </publications> + <dependencies>{dependencies.map(renderDependency).toSeq}</dependencies> + </ivy-module> + + val pp = new PrettyPrinter(120, 4) + head + pp.format(xml).replaceAll(">", ">") + } + + private def renderDependency(dep: Dependency) = { + if (dep.exclusions.isEmpty) + <dependency org={dep.artifact.group} name={dep.artifact.id} rev={dep.artifact.version} conf={s"${scopeToConf(dep.scope)}->${dep.configuration.getOrElse("default(compile)")}"} /> + else + <dependency org={dep.artifact.group} name={dep.artifact.id} rev={dep.artifact.version} conf={s"${scopeToConf(dep.scope)}->${dep.configuration.getOrElse("default(compile)")}"}> + {dep.exclusions.map(ex => <exclude org={ex._1} name={ex._2} matcher="exact"/>)} + </dependency> + } + + private def scopeToConf(s: Scope): String = s match { + case Scope.Compile => "compile" + case Scope.Provided => "provided" + case Scope.Test => "test" + case Scope.Runtime => "runtime" + } + +} diff --git a/scalalib/src/publish/JsonFormatters.scala b/scalalib/src/publish/JsonFormatters.scala new file mode 100644 index 00000000..8fc90632 --- /dev/null +++ b/scalalib/src/publish/JsonFormatters.scala @@ -0,0 +1,11 @@ +package mill.scalalib.publish + +import upickle.default.{ReadWriter => RW} + +trait JsonFormatters { + implicit lazy val artifactFormat: RW[Artifact] = upickle.default.macroRW + implicit lazy val developerFormat: RW[Developer] = upickle.default.macroRW + implicit lazy val licenseFormat: RW[License] = upickle.default.macroRW + implicit lazy val versionControlFormat: RW[VersionControl] = upickle.default.macroRW + implicit lazy val pomSettingsFormat: RW[PomSettings] = upickle.default.macroRW +} diff --git a/scalalib/src/publish/Licence.scala b/scalalib/src/publish/Licence.scala new file mode 100644 index 00000000..8838ef69 --- /dev/null +++ b/scalalib/src/publish/Licence.scala @@ -0,0 +1,479 @@ +package mill.scalalib.publish + +case class License( + id: String, + name: String, + url: String, + isOsiApproved: Boolean, + isFsfLibre: Boolean, + distribution: String +) + +object License { + @deprecated("use License.LicenseName (ex: License.`Apache-2.0`)", "0.1.0") + def apply(name: String, url: String): License = + License(name, name, url, false, false, "repo") + + /* + wget https://raw.githubusercontent.com/spdx/license-list-data/master/json/licenses.json + + ``` + val circeVersion = "0.9.1" + libraryDependencies ++= Seq( + "io.circe" %% "circe-core", + "io.circe" %% "circe-generic", + "io.circe" %% "circe-parser" + ).map(_ % circeVersion) + + import io.circe._, io.circe.generic.auto._, io.circe.parser._, io.circe.syntax._ + import java.nio.file._ + import System.{lineSeparator => nl} + case class License( + reference: String, + isDeprecatedLicenseId: Boolean, + isFsfLibre: Option[Boolean], + detailsUrl: String, + referenceNumber: String, + name: String, + licenseId: String, + seeAlso: Option[List[String]], + isOsiApproved: Boolean + ) { + def ident: String = { + val startsWithDigit = (0 to 9).map(_.toString).exists(licenseId.startsWith) + if (licenseId.contains("-") || !startsWithDigit) s"`$licenseId`" + else licenseId + } + + def syntax(identPadding: Int, namePadding: Int): String = { + val s1 = " " * (identPadding - ident.size) + val s2 = " " * (namePadding - name.size) + val ticks = if (ident == licenseId) 2 else 0 + val s3 = " " * (identPadding - ticks - ident.size) + val s4 = if(isOsiApproved) " " else "" + s"""val ${ident}${s1} = spdx(\"\"\"$name\"\"\",$s2 "$licenseId", $s3 $isOsiApproved, $s4 ${isFsfLibre.getOrElse(false)})""" + } + } + + + case class Data(licenses: List[License]) + + val json = new String(Files.readAllBytes(Paths.get("data.json"))) + + val licences = decode[Data](json).right.get.licenses + + val identPadding = licences.map(_.licenseId.size + 2).max + val namePadding = licences.map(_.name.size).max + + val output = licences.map(license => license.syntax(identPadding, namePadding)).mkString(nl) + Files.write(Paths.get("out.scala"), output.getBytes("utf-8")) + */ + val `0BSD` = spdx("BSD Zero Clause License", "0BSD", false, false) + val AAL = spdx("Attribution Assurance License", "AAL", true, false) + val Abstyles = spdx("Abstyles License", "Abstyles", false, false) + val `Adobe-2006` = spdx("Adobe Systems Incorporated Source Code License Agreement", "Adobe-2006", false, false) + val `Adobe-Glyph` = spdx("Adobe Glyph List License", "Adobe-Glyph", false, false) + val ADSL = spdx("Amazon Digital Services License", "ADSL", false, false) + val `AFL-1.1` = spdx("Academic Free License v1.1", "AFL-1.1", true, true) + val `AFL-1.2` = spdx("Academic Free License v1.2", "AFL-1.2", true, true) + val `AFL-2.0` = spdx("Academic Free License v2.0", "AFL-2.0", true, true) + val `AFL-2.1` = spdx("Academic Free License v2.1", "AFL-2.1", true, true) + val `AFL-3.0` = spdx("Academic Free License v3.0", "AFL-3.0", true, true) + val Afmparse = spdx("Afmparse License", "Afmparse", false, false) + val `AGPL-1.0` = spdx("Affero General Public License v1.0", "AGPL-1.0", false, true) + val `AGPL-3.0-only` = spdx("GNU Affero General Public License v3.0 only", "AGPL-3.0-only", true, false) + val `AGPL-3.0-or-later` = spdx("GNU Affero General Public License v3.0 or later", "AGPL-3.0-or-later", true, false) + val Aladdin = spdx("Aladdin Free Public License", "Aladdin", false, false) + val AMDPLPA = spdx("AMD's plpa_map.c License", "AMDPLPA", false, false) + val AML = spdx("Apple MIT License", "AML", false, false) + val AMPAS = spdx("Academy of Motion Picture Arts and Sciences BSD", "AMPAS", false, false) + val `ANTLR-PD` = spdx("ANTLR Software Rights Notice", "ANTLR-PD", false, false) + val `Apache-1.0` = spdx("Apache License 1.0", "Apache-1.0", false, true) + val `Apache-1.1` = spdx("Apache License 1.1", "Apache-1.1", true, true) + val `Apache-2.0` = spdx("Apache License 2.0", "Apache-2.0", true, true) + val APAFML = spdx("Adobe Postscript AFM License", "APAFML", false, false) + val `APL-1.0` = spdx("Adaptive Public License 1.0", "APL-1.0", true, false) + val `APSL-1.0` = spdx("Apple Public Source License 1.0", "APSL-1.0", true, false) + val `APSL-1.1` = spdx("Apple Public Source License 1.1", "APSL-1.1", true, false) + val `APSL-1.2` = spdx("Apple Public Source License 1.2", "APSL-1.2", true, false) + val `APSL-2.0` = spdx("Apple Public Source License 2.0", "APSL-2.0", true, true) + val `Artistic-1.0-cl8` = spdx("Artistic License 1.0 w/clause 8", "Artistic-1.0-cl8", true, false) + val `Artistic-1.0-Perl` = spdx("Artistic License 1.0 (Perl)", "Artistic-1.0-Perl", true, false) + val `Artistic-1.0` = spdx("Artistic License 1.0", "Artistic-1.0", true, false) + val `Artistic-2.0` = spdx("Artistic License 2.0", "Artistic-2.0", true, true) + val Bahyph = spdx("Bahyph License", "Bahyph", false, false) + val Barr = spdx("Barr License", "Barr", false, false) + val Beerware = spdx("Beerware License", "Beerware", false, false) + val `BitTorrent-1.0` = spdx("BitTorrent Open Source License v1.0", "BitTorrent-1.0", false, false) + val `BitTorrent-1.1` = spdx("BitTorrent Open Source License v1.1", "BitTorrent-1.1", false, true) + val Borceux = spdx("Borceux license", "Borceux", false, false) + val `BSD-1-Clause` = spdx("BSD 1-Clause License", "BSD-1-Clause", false, false) + val `BSD-2-Clause-FreeBSD` = spdx("BSD 2-Clause FreeBSD License", "BSD-2-Clause-FreeBSD", false, true) + val `BSD-2-Clause-NetBSD` = spdx("BSD 2-Clause NetBSD License", "BSD-2-Clause-NetBSD", false, false) + val `BSD-2-Clause-Patent` = spdx("BSD-2-Clause Plus Patent License", "BSD-2-Clause-Patent", true, false) + val `BSD-2-Clause` = spdx("BSD 2-Clause \"Simplified\" License", "BSD-2-Clause", true, false) + val `BSD-3-Clause-Attribution` = spdx("BSD with attribution", "BSD-3-Clause-Attribution", false, false) + val `BSD-3-Clause-Clear` = spdx("BSD 3-Clause Clear License", "BSD-3-Clause-Clear", false, true) + val `BSD-3-Clause-LBNL` = spdx("Lawrence Berkeley National Labs BSD variant license", "BSD-3-Clause-LBNL", false, false) + val `BSD-3-Clause-No-Nuclear-License-2014` = spdx("BSD 3-Clause No Nuclear License 2014", "BSD-3-Clause-No-Nuclear-License-2014", false, false) + val `BSD-3-Clause-No-Nuclear-License` = spdx("BSD 3-Clause No Nuclear License", "BSD-3-Clause-No-Nuclear-License", false, false) + val `BSD-3-Clause-No-Nuclear-Warranty` = spdx("BSD 3-Clause No Nuclear Warranty", "BSD-3-Clause-No-Nuclear-Warranty", false, false) + val `BSD-3-Clause` = spdx("BSD 3-Clause \"New\" or \"Revised\" License", "BSD-3-Clause", true, true) + val `BSD-4-Clause-UC` = spdx("BSD-4-Clause (University of California-Specific)", "BSD-4-Clause-UC", false, false) + val `BSD-4-Clause` = spdx("BSD 4-Clause \"Original\" or \"Old\" License", "BSD-4-Clause", false, true) + val `BSD-Protection` = spdx("BSD Protection License", "BSD-Protection", false, false) + val `BSD-Source-Code` = spdx("BSD Source Code Attribution", "BSD-Source-Code", false, false) + val `BSL-1.0` = spdx("Boost Software License 1.0", "BSL-1.0", true, true) + val `bzip2-1.0.5` = spdx("bzip2 and libbzip2 License v1.0.5", "bzip2-1.0.5", false, false) + val `bzip2-1.0.6` = spdx("bzip2 and libbzip2 License v1.0.6", "bzip2-1.0.6", false, false) + val Caldera = spdx("Caldera License", "Caldera", false, false) + val `CATOSL-1.1` = spdx("Computer Associates Trusted Open Source License 1.1", "CATOSL-1.1", true, false) + val `CC-BY-1.0` = spdx("Creative Commons Attribution 1.0", "CC-BY-1.0", false, false) + val `CC-BY-2.0` = spdx("Creative Commons Attribution 2.0", "CC-BY-2.0", false, false) + val `CC-BY-2.5` = spdx("Creative Commons Attribution 2.5", "CC-BY-2.5", false, false) + val `CC-BY-3.0` = spdx("Creative Commons Attribution 3.0", "CC-BY-3.0", false, false) + val `CC-BY-4.0` = spdx("Creative Commons Attribution 4.0", "CC-BY-4.0", false, true) + val `CC-BY-NC-1.0` = spdx("Creative Commons Attribution Non Commercial 1.0", "CC-BY-NC-1.0", false, false) + val `CC-BY-NC-2.0` = spdx("Creative Commons Attribution Non Commercial 2.0", "CC-BY-NC-2.0", false, false) + val `CC-BY-NC-2.5` = spdx("Creative Commons Attribution Non Commercial 2.5", "CC-BY-NC-2.5", false, false) + val `CC-BY-NC-3.0` = spdx("Creative Commons Attribution Non Commercial 3.0", "CC-BY-NC-3.0", false, false) + val `CC-BY-NC-4.0` = spdx("Creative Commons Attribution Non Commercial 4.0", "CC-BY-NC-4.0", false, false) + val `CC-BY-NC-ND-1.0` = spdx("Creative Commons Attribution Non Commercial No Derivatives 1.0", "CC-BY-NC-ND-1.0", false, false) + val `CC-BY-NC-ND-2.0` = spdx("Creative Commons Attribution Non Commercial No Derivatives 2.0", "CC-BY-NC-ND-2.0", false, false) + val `CC-BY-NC-ND-2.5` = spdx("Creative Commons Attribution Non Commercial No Derivatives 2.5", "CC-BY-NC-ND-2.5", false, false) + val `CC-BY-NC-ND-3.0` = spdx("Creative Commons Attribution Non Commercial No Derivatives 3.0", "CC-BY-NC-ND-3.0", false, false) + val `CC-BY-NC-ND-4.0` = spdx("Creative Commons Attribution Non Commercial No Derivatives 4.0", "CC-BY-NC-ND-4.0", false, false) + val `CC-BY-NC-SA-1.0` = spdx("Creative Commons Attribution Non Commercial Share Alike 1.0", "CC-BY-NC-SA-1.0", false, false) + val `CC-BY-NC-SA-2.0` = spdx("Creative Commons Attribution Non Commercial Share Alike 2.0", "CC-BY-NC-SA-2.0", false, false) + val `CC-BY-NC-SA-2.5` = spdx("Creative Commons Attribution Non Commercial Share Alike 2.5", "CC-BY-NC-SA-2.5", false, false) + val `CC-BY-NC-SA-3.0` = spdx("Creative Commons Attribution Non Commercial Share Alike 3.0", "CC-BY-NC-SA-3.0", false, false) + val `CC-BY-NC-SA-4.0` = spdx("Creative Commons Attribution Non Commercial Share Alike 4.0", "CC-BY-NC-SA-4.0", false, false) + val `CC-BY-ND-1.0` = spdx("Creative Commons Attribution No Derivatives 1.0", "CC-BY-ND-1.0", false, false) + val `CC-BY-ND-2.0` = spdx("Creative Commons Attribution No Derivatives 2.0", "CC-BY-ND-2.0", false, false) + val `CC-BY-ND-2.5` = spdx("Creative Commons Attribution No Derivatives 2.5", "CC-BY-ND-2.5", false, false) + val `CC-BY-ND-3.0` = spdx("Creative Commons Attribution No Derivatives 3.0", "CC-BY-ND-3.0", false, false) + val `CC-BY-ND-4.0` = spdx("Creative Commons Attribution No Derivatives 4.0", "CC-BY-ND-4.0", false, false) + val `CC-BY-SA-1.0` = spdx("Creative Commons Attribution Share Alike 1.0", "CC-BY-SA-1.0", false, false) + val `CC-BY-SA-2.0` = spdx("Creative Commons Attribution Share Alike 2.0", "CC-BY-SA-2.0", false, false) + val `CC-BY-SA-2.5` = spdx("Creative Commons Attribution Share Alike 2.5", "CC-BY-SA-2.5", false, false) + val `CC-BY-SA-3.0` = spdx("Creative Commons Attribution Share Alike 3.0", "CC-BY-SA-3.0", false, false) + val `CC-BY-SA-4.0` = spdx("Creative Commons Attribution Share Alike 4.0", "CC-BY-SA-4.0", false, true) + val `CC0-1.0` = spdx("Creative Commons Zero v1.0 Universal", "CC0-1.0", false, true) + val `CDDL-1.0` = spdx("Common Development and Distribution License 1.0", "CDDL-1.0", true, true) + val `CDDL-1.1` = spdx("Common Development and Distribution License 1.1", "CDDL-1.1", false, false) + val `CDLA-Permissive-1.0` = spdx("Community Data License Agreement Permissive 1.0", "CDLA-Permissive-1.0", false, false) + val `CDLA-Sharing-1.0` = spdx("Community Data License Agreement Sharing 1.0", "CDLA-Sharing-1.0", false, false) + val `CECILL-1.0` = spdx("CeCILL Free Software License Agreement v1.0", "CECILL-1.0", false, false) + val `CECILL-1.1` = spdx("CeCILL Free Software License Agreement v1.1", "CECILL-1.1", false, false) + val `CECILL-2.0` = spdx("CeCILL Free Software License Agreement v2.0", "CECILL-2.0", false, true) + val `CECILL-2.1` = spdx("CeCILL Free Software License Agreement v2.1", "CECILL-2.1", true, false) + val `CECILL-B` = spdx("CeCILL-B Free Software License Agreement", "CECILL-B", false, true) + val `CECILL-C` = spdx("CeCILL-C Free Software License Agreement", "CECILL-C", false, true) + val ClArtistic = spdx("Clarified Artistic License", "ClArtistic", false, true) + val `CNRI-Jython` = spdx("CNRI Jython License", "CNRI-Jython", false, false) + val `CNRI-Python-GPL-Compatible` = spdx("CNRI Python Open Source GPL Compatible License Agreement", "CNRI-Python-GPL-Compatible", false, false) + val `CNRI-Python` = spdx("CNRI Python License", "CNRI-Python", true, false) + val `Condor-1.1` = spdx("Condor Public License v1.1", "Condor-1.1", false, true) + val `CPAL-1.0` = spdx("Common Public Attribution License 1.0", "CPAL-1.0", true, true) + val `CPL-1.0` = spdx("Common Public License 1.0", "CPL-1.0", true, true) + val `CPOL-1.02` = spdx("Code Project Open License 1.02", "CPOL-1.02", false, false) + val Crossword = spdx("Crossword License", "Crossword", false, false) + val CrystalStacker = spdx("CrystalStacker License", "CrystalStacker", false, false) + val `CUA-OPL-1.0` = spdx("CUA Office Public License v1.0", "CUA-OPL-1.0", true, false) + val Cube = spdx("Cube License", "Cube", false, false) + val curl = spdx("curl License", "curl", false, false) + val `D-FSL-1.0` = spdx("Deutsche Freie Software Lizenz", "D-FSL-1.0", false, false) + val diffmark = spdx("diffmark license", "diffmark", false, false) + val DOC = spdx("DOC License", "DOC", false, false) + val Dotseqn = spdx("Dotseqn License", "Dotseqn", false, false) + val DSDP = spdx("DSDP License", "DSDP", false, false) + val dvipdfm = spdx("dvipdfm License", "dvipdfm", false, false) + val `ECL-1.0` = spdx("Educational Community License v1.0", "ECL-1.0", true, false) + val `ECL-2.0` = spdx("Educational Community License v2.0", "ECL-2.0", true, true) + val `EFL-1.0` = spdx("Eiffel Forum License v1.0", "EFL-1.0", true, false) + val `EFL-2.0` = spdx("Eiffel Forum License v2.0", "EFL-2.0", true, true) + val eGenix = spdx("eGenix.com Public License 1.1.0", "eGenix", false, false) + val Entessa = spdx("Entessa Public License v1.0", "Entessa", true, false) + val `EPL-1.0` = spdx("Eclipse Public License 1.0", "EPL-1.0", true, true) + val `EPL-2.0` = spdx("Eclipse Public License 2.0", "EPL-2.0", true, true) + val `ErlPL-1.1` = spdx("Erlang Public License v1.1", "ErlPL-1.1", false, false) + val EUDatagrid = spdx("EU DataGrid Software License", "EUDatagrid", true, true) + val `EUPL-1.0` = spdx("European Union Public License 1.0", "EUPL-1.0", false, false) + val `EUPL-1.1` = spdx("European Union Public License 1.1", "EUPL-1.1", true, true) + val `EUPL-1.2` = spdx("European Union Public License 1.2", "EUPL-1.2", true, false) + val Eurosym = spdx("Eurosym License", "Eurosym", false, false) + val Fair = spdx("Fair License", "Fair", true, false) + val `Frameworx-1.0` = spdx("Frameworx Open License 1.0", "Frameworx-1.0", true, false) + val FreeImage = spdx("FreeImage Public License v1.0", "FreeImage", false, false) + val FSFAP = spdx("FSF All Permissive License", "FSFAP", false, true) + val FSFUL = spdx("FSF Unlimited License", "FSFUL", false, false) + val FSFULLR = spdx("FSF Unlimited License (with License Retention)", "FSFULLR", false, false) + val FTL = spdx("Freetype Project License", "FTL", false, true) + val `GFDL-1.1-only` = spdx("GNU Free Documentation License v1.1 only", "GFDL-1.1-only", false, false) + val `GFDL-1.1-or-later` = spdx("GNU Free Documentation License v1.1 or later", "GFDL-1.1-or-later", false, false) + val `GFDL-1.2-only` = spdx("GNU Free Documentation License v1.2 only", "GFDL-1.2-only", false, false) + val `GFDL-1.2-or-later` = spdx("GNU Free Documentation License v1.2 or later", "GFDL-1.2-or-later", false, false) + val `GFDL-1.3-only` = spdx("GNU Free Documentation License v1.3 only", "GFDL-1.3-only", false, false) + val `GFDL-1.3-or-later` = spdx("GNU Free Documentation License v1.3 or later", "GFDL-1.3-or-later", false, false) + val Giftware = spdx("Giftware License", "Giftware", false, false) + val GL2PS = spdx("GL2PS License", "GL2PS", false, false) + val Glide = spdx("3dfx Glide License", "Glide", false, false) + val Glulxe = spdx("Glulxe License", "Glulxe", false, false) + val gnuplot = spdx("gnuplot License", "gnuplot", false, true) + val `GPL-1.0-only` = spdx("GNU General Public License v1.0 only", "GPL-1.0-only", false, false) + val `GPL-1.0-or-later` = spdx("GNU General Public License v1.0 or later", "GPL-1.0-or-later", false, false) + val `GPL-2.0-only` = spdx("GNU General Public License v2.0 only", "GPL-2.0-only", true, false) + val `GPL-2.0-or-later` = spdx("GNU General Public License v2.0 or later", "GPL-2.0-or-later", true, false) + val `GPL-3.0-only` = spdx("GNU General Public License v3.0 only", "GPL-3.0-only", true, false) + val `GPL-3.0-or-later` = spdx("GNU General Public License v3.0 or later", "GPL-3.0-or-later", true, false) + val `gSOAP-1.3b` = spdx("gSOAP Public License v1.3b", "gSOAP-1.3b", false, false) + val HaskellReport = spdx("Haskell Language Report License", "HaskellReport", false, false) + val HPND = spdx("Historical Permission Notice and Disclaimer", "HPND", true, true) + val `IBM-pibs` = spdx("IBM PowerPC Initialization and Boot Software", "IBM-pibs", false, false) + val ICU = spdx("ICU License", "ICU", false, false) + val IJG = spdx("Independent JPEG Group License", "IJG", false, true) + val ImageMagick = spdx("ImageMagick License", "ImageMagick", false, false) + val iMatix = spdx("iMatix Standard Function Library Agreement", "iMatix", false, true) + val Imlib2 = spdx("Imlib2 License", "Imlib2", false, true) + val `Info-ZIP` = spdx("Info-ZIP License", "Info-ZIP", false, false) + val `Intel-ACPI` = spdx("Intel ACPI Software License Agreement", "Intel-ACPI", false, false) + val Intel = spdx("Intel Open Source License", "Intel", true, true) + val `Interbase-1.0` = spdx("Interbase Public License v1.0", "Interbase-1.0", false, false) + val IPA = spdx("IPA Font License", "IPA", true, true) + val `IPL-1.0` = spdx("IBM Public License v1.0", "IPL-1.0", true, true) + val ISC = spdx("ISC License", "ISC", true, true) + val `JasPer-2.0` = spdx("JasPer License", "JasPer-2.0", false, false) + val JSON = spdx("JSON License", "JSON", false, false) + val `LAL-1.2` = spdx("Licence Art Libre 1.2", "LAL-1.2", false, false) + val `LAL-1.3` = spdx("Licence Art Libre 1.3", "LAL-1.3", false, false) + val Latex2e = spdx("Latex2e License", "Latex2e", false, false) + val Leptonica = spdx("Leptonica License", "Leptonica", false, false) + val `LGPL-2.0-only` = spdx("GNU Library General Public License v2 only", "LGPL-2.0-only", true, false) + val `LGPL-2.0-or-later` = spdx("GNU Library General Public License v2 or later", "LGPL-2.0-or-later", true, false) + val `LGPL-2.1-only` = spdx("GNU Lesser General Public License v2.1 only", "LGPL-2.1-only", true, false) + val `LGPL-2.1-or-later` = spdx("GNU Lesser General Public License v2.1 or later", "LGPL-2.1-or-later", true, false) + val `LGPL-3.0-only` = spdx("GNU Lesser General Public License v3.0 only", "LGPL-3.0-only", true, false) + val `LGPL-3.0-or-later` = spdx("GNU Lesser General Public License v3.0 or later", "LGPL-3.0-or-later", true, false) + val LGPLLR = spdx("Lesser General Public License For Linguistic Resources", "LGPLLR", false, false) + val Libpng = spdx("libpng License", "Libpng", false, false) + val libtiff = spdx("libtiff License", "libtiff", false, false) + val `LiLiQ-P-1.1` = spdx("Licence Libre du Québec – Permissive version 1.1", "LiLiQ-P-1.1", true, false) + val `LiLiQ-R-1.1` = spdx("Licence Libre du Québec – Réciprocité version 1.1", "LiLiQ-R-1.1", true, false) + val `LiLiQ-Rplus-1.1` = spdx("Licence Libre du Québec – Réciprocité forte version 1.1", "LiLiQ-Rplus-1.1", true, false) + val `LPL-1.0` = spdx("Lucent Public License Version 1.0", "LPL-1.0", true, false) + val `LPL-1.02` = spdx("Lucent Public License v1.02", "LPL-1.02", true, true) + val `LPPL-1.0` = spdx("LaTeX Project Public License v1.0", "LPPL-1.0", false, false) + val `LPPL-1.1` = spdx("LaTeX Project Public License v1.1", "LPPL-1.1", false, false) + val `LPPL-1.2` = spdx("LaTeX Project Public License v1.2", "LPPL-1.2", false, true) + val `LPPL-1.3a` = spdx("LaTeX Project Public License v1.3a", "LPPL-1.3a", false, true) + val `LPPL-1.3c` = spdx("LaTeX Project Public License v1.3c", "LPPL-1.3c", true, false) + val MakeIndex = spdx("MakeIndex License", "MakeIndex", false, false) + val MirOS = spdx("MirOS License", "MirOS", true, false) + val `MIT-advertising` = spdx("Enlightenment License (e16)", "MIT-advertising", false, false) + val `MIT-CMU` = spdx("CMU License", "MIT-CMU", false, false) + val `MIT-enna` = spdx("enna License", "MIT-enna", false, false) + val `MIT-feh` = spdx("feh License", "MIT-feh", false, false) + val MIT = spdx("MIT License", "MIT", true, true) + val MITNFA = spdx("MIT +no-false-attribs license", "MITNFA", false, false) + val Motosoto = spdx("Motosoto License", "Motosoto", true, false) + val mpich2 = spdx("mpich2 License", "mpich2", false, false) + val `MPL-1.0` = spdx("Mozilla Public License 1.0", "MPL-1.0", true, false) + val `MPL-1.1` = spdx("Mozilla Public License 1.1", "MPL-1.1", true, true) + val `MPL-2.0-no-copyleft-exception` = spdx("Mozilla Public License 2.0 (no copyleft exception)", "MPL-2.0-no-copyleft-exception", true, false) + val `MPL-2.0` = spdx("Mozilla Public License 2.0", "MPL-2.0", true, true) + val `MS-PL` = spdx("Microsoft Public License", "MS-PL", true, true) + val `MS-RL` = spdx("Microsoft Reciprocal License", "MS-RL", true, true) + val MTLL = spdx("Matrix Template Library License", "MTLL", false, false) + val Multics = spdx("Multics License", "Multics", true, false) + val Mup = spdx("Mup License", "Mup", false, false) + val `NASA-1.3` = spdx("NASA Open Source Agreement 1.3", "NASA-1.3", true, false) + val Naumen = spdx("Naumen Public License", "Naumen", true, false) + val `NBPL-1.0` = spdx("Net Boolean Public License v1", "NBPL-1.0", false, false) + val NCSA = spdx("University of Illinois/NCSA Open Source License", "NCSA", true, true) + val `Net-SNMP` = spdx("Net-SNMP License", "Net-SNMP", false, false) + val NetCDF = spdx("NetCDF license", "NetCDF", false, false) + val Newsletr = spdx("Newsletr License", "Newsletr", false, false) + val NGPL = spdx("Nethack General Public License", "NGPL", true, false) + val `NLOD-1.0` = spdx("Norwegian Licence for Open Government Data", "NLOD-1.0", false, false) + val NLPL = spdx("No Limit Public License", "NLPL", false, false) + val Nokia = spdx("Nokia Open Source License", "Nokia", true, true) + val NOSL = spdx("Netizen Open Source License", "NOSL", false, true) + val Noweb = spdx("Noweb License", "Noweb", false, false) + val `NPL-1.0` = spdx("Netscape Public License v1.0", "NPL-1.0", false, true) + val `NPL-1.1` = spdx("Netscape Public License v1.1", "NPL-1.1", false, true) + val `NPOSL-3.0` = spdx("Non-Profit Open Software License 3.0", "NPOSL-3.0", true, false) + val NRL = spdx("NRL License", "NRL", false, false) + val NTP = spdx("NTP License", "NTP", true, false) + val `OCCT-PL` = spdx("Open CASCADE Technology Public License", "OCCT-PL", false, false) + val `OCLC-2.0` = spdx("OCLC Research Public License 2.0", "OCLC-2.0", true, false) + val `ODbL-1.0` = spdx("ODC Open Database License v1.0", "ODbL-1.0", false, true) + val `OFL-1.0` = spdx("SIL Open Font License 1.0", "OFL-1.0", false, false) + val `OFL-1.1` = spdx("SIL Open Font License 1.1", "OFL-1.1", true, true) + val OGTSL = spdx("Open Group Test Suite License", "OGTSL", true, false) + val `OLDAP-1.1` = spdx("Open LDAP Public License v1.1", "OLDAP-1.1", false, false) + val `OLDAP-1.2` = spdx("Open LDAP Public License v1.2", "OLDAP-1.2", false, false) + val `OLDAP-1.3` = spdx("Open LDAP Public License v1.3", "OLDAP-1.3", false, false) + val `OLDAP-1.4` = spdx("Open LDAP Public License v1.4", "OLDAP-1.4", false, false) + val `OLDAP-2.0.1` = spdx("Open LDAP Public License v2.0.1", "OLDAP-2.0.1", false, false) + val `OLDAP-2.0` = spdx("Open LDAP Public License v2.0 (or possibly 2.0A and 2.0B)", "OLDAP-2.0", false, false) + val `OLDAP-2.1` = spdx("Open LDAP Public License v2.1", "OLDAP-2.1", false, false) + val `OLDAP-2.2.1` = spdx("Open LDAP Public License v2.2.1", "OLDAP-2.2.1", false, false) + val `OLDAP-2.2.2` = spdx("Open LDAP Public License 2.2.2", "OLDAP-2.2.2", false, false) + val `OLDAP-2.2` = spdx("Open LDAP Public License v2.2", "OLDAP-2.2", false, false) + val `OLDAP-2.3` = spdx("Open LDAP Public License v2.3", "OLDAP-2.3", false, true) + val `OLDAP-2.4` = spdx("Open LDAP Public License v2.4", "OLDAP-2.4", false, false) + val `OLDAP-2.5` = spdx("Open LDAP Public License v2.5", "OLDAP-2.5", false, false) + val `OLDAP-2.6` = spdx("Open LDAP Public License v2.6", "OLDAP-2.6", false, false) + val `OLDAP-2.7` = spdx("Open LDAP Public License v2.7", "OLDAP-2.7", false, true) + val `OLDAP-2.8` = spdx("Open LDAP Public License v2.8", "OLDAP-2.8", false, false) + val OML = spdx("Open Market License", "OML", false, false) + val OpenSSL = spdx("OpenSSL License", "OpenSSL", false, true) + val `OPL-1.0` = spdx("Open Public License v1.0", "OPL-1.0", false, false) + val `OSET-PL-2.1` = spdx("OSET Public License version 2.1", "OSET-PL-2.1", true, false) + val `OSL-1.0` = spdx("Open Software License 1.0", "OSL-1.0", true, true) + val `OSL-1.1` = spdx("Open Software License 1.1", "OSL-1.1", false, true) + val `OSL-2.0` = spdx("Open Software License 2.0", "OSL-2.0", true, true) + val `OSL-2.1` = spdx("Open Software License 2.1", "OSL-2.1", true, true) + val `OSL-3.0` = spdx("Open Software License 3.0", "OSL-3.0", true, true) + val `PDDL-1.0` = spdx("ODC Public Domain Dedication & License 1.0", "PDDL-1.0", false, false) + val `PHP-3.0` = spdx("PHP License v3.0", "PHP-3.0", true, false) + val `PHP-3.01` = spdx("PHP License v3.01", "PHP-3.01", false, true) + val Plexus = spdx("Plexus Classworlds License", "Plexus", false, false) + val PostgreSQL = spdx("PostgreSQL License", "PostgreSQL", true, false) + val psfrag = spdx("psfrag License", "psfrag", false, false) + val psutils = spdx("psutils License", "psutils", false, false) + val `Python-2.0` = spdx("Python License 2.0", "Python-2.0", true, true) + val Qhull = spdx("Qhull License", "Qhull", false, false) + val `QPL-1.0` = spdx("Q Public License 1.0", "QPL-1.0", true, true) + val Rdisc = spdx("Rdisc License", "Rdisc", false, false) + val `RHeCos-1.1` = spdx("Red Hat eCos Public License v1.1", "RHeCos-1.1", false, false) + val `RPL-1.1` = spdx("Reciprocal Public License 1.1", "RPL-1.1", true, false) + val `RPL-1.5` = spdx("Reciprocal Public License 1.5", "RPL-1.5", true, false) + val `RPSL-1.0` = spdx("RealNetworks Public Source License v1.0", "RPSL-1.0", true, true) + val `RSA-MD` = spdx("RSA Message-Digest License ", "RSA-MD", false, false) + val RSCPL = spdx("Ricoh Source Code Public License", "RSCPL", true, false) + val Ruby = spdx("Ruby License", "Ruby", false, true) + val `SAX-PD` = spdx("Sax Public Domain Notice", "SAX-PD", false, false) + val Saxpath = spdx("Saxpath License", "Saxpath", false, false) + val SCEA = spdx("SCEA Shared Source License", "SCEA", false, false) + val Sendmail = spdx("Sendmail License", "Sendmail", false, false) + val `SGI-B-1.0` = spdx("SGI Free Software License B v1.0", "SGI-B-1.0", false, false) + val `SGI-B-1.1` = spdx("SGI Free Software License B v1.1", "SGI-B-1.1", false, false) + val `SGI-B-2.0` = spdx("SGI Free Software License B v2.0", "SGI-B-2.0", false, true) + val `SimPL-2.0` = spdx("Simple Public License 2.0", "SimPL-2.0", true, false) + val `SISSL-1.2` = spdx("Sun Industry Standards Source License v1.2", "SISSL-1.2", false, false) + val SISSL = spdx("Sun Industry Standards Source License v1.1", "SISSL", true, false) + val Sleepycat = spdx("Sleepycat License", "Sleepycat", true, true) + val SMLNJ = spdx("Standard ML of New Jersey License", "SMLNJ", false, true) + val SMPPL = spdx("Secure Messaging Protocol Public License", "SMPPL", false, false) + val SNIA = spdx("SNIA Public License 1.1", "SNIA", false, false) + val `Spencer-86` = spdx("Spencer License 86", "Spencer-86", false, false) + val `Spencer-94` = spdx("Spencer License 94", "Spencer-94", false, false) + val `Spencer-99` = spdx("Spencer License 99", "Spencer-99", false, false) + val `SPL-1.0` = spdx("Sun Public License v1.0", "SPL-1.0", true, true) + val `SugarCRM-1.1.3` = spdx("SugarCRM Public License v1.1.3", "SugarCRM-1.1.3", false, false) + val SWL = spdx("Scheme Widget Library (SWL) Software License Agreement", "SWL", false, false) + val TCL = spdx("TCL/TK License", "TCL", false, false) + val `TCP-wrappers` = spdx("TCP Wrappers License", "TCP-wrappers", false, false) + val TMate = spdx("TMate Open Source License", "TMate", false, false) + val `TORQUE-1.1` = spdx("TORQUE v2.5+ Software License v1.1", "TORQUE-1.1", false, false) + val TOSL = spdx("Trusster Open Source License", "TOSL", false, false) + val `Unicode-DFS-2015` = spdx("Unicode License Agreement - Data Files and Software (2015)", "Unicode-DFS-2015", false, false) + val `Unicode-DFS-2016` = spdx("Unicode License Agreement - Data Files and Software (2016)", "Unicode-DFS-2016", false, false) + val `Unicode-TOU` = spdx("Unicode Terms of Use", "Unicode-TOU", false, false) + val Unlicense = spdx("The Unlicense", "Unlicense", false, true) + val `UPL-1.0` = spdx("Universal Permissive License v1.0", "UPL-1.0", true, true) + val Vim = spdx("Vim License", "Vim", false, true) + val VOSTROM = spdx("VOSTROM Public License for Open Source", "VOSTROM", false, false) + val `VSL-1.0` = spdx("Vovida Software License v1.0", "VSL-1.0", true, false) + val `W3C-19980720` = spdx("W3C Software Notice and License (1998-07-20)", "W3C-19980720", false, false) + val `W3C-20150513` = spdx("W3C Software Notice and Document License (2015-05-13)", "W3C-20150513", false, false) + val W3C = spdx("W3C Software Notice and License (2002-12-31)", "W3C", true, true) + val `Watcom-1.0` = spdx("Sybase Open Watcom Public License 1.0", "Watcom-1.0", true, false) + val Wsuipa = spdx("Wsuipa License", "Wsuipa", false, false) + val WTFPL = spdx("Do What The F*ck You Want To Public License", "WTFPL", false, true) + val X11 = spdx("X11 License", "X11", false, true) + val Xerox = spdx("Xerox License", "Xerox", false, false) + val `XFree86-1.1` = spdx("XFree86 License 1.1", "XFree86-1.1", false, true) + val xinetd = spdx("xinetd License", "xinetd", false, true) + val Xnet = spdx("X.Net License", "Xnet", true, false) + val xpp = spdx("XPP License", "xpp", false, false) + val XSkat = spdx("XSkat License", "XSkat", false, false) + val `YPL-1.0` = spdx("Yahoo! Public License v1.0", "YPL-1.0", false, false) + val `YPL-1.1` = spdx("Yahoo! Public License v1.1", "YPL-1.1", false, true) + val Zed = spdx("Zed License", "Zed", false, false) + val `Zend-2.0` = spdx("Zend License v2.0", "Zend-2.0", false, true) + val `Zimbra-1.3` = spdx("Zimbra Public License v1.3", "Zimbra-1.3", false, true) + val `Zimbra-1.4` = spdx("Zimbra Public License v1.4", "Zimbra-1.4", false, false) + val `zlib-acknowledgement` = spdx("zlib/libpng License with Acknowledgement", "zlib-acknowledgement", false, false) + val Zlib = spdx("zlib License", "Zlib", true, true) + val `ZPL-1.1` = spdx("Zope Public License 1.1", "ZPL-1.1", false, false) + val `ZPL-2.0` = spdx("Zope Public License 2.0", "ZPL-2.0", true, true) + val `ZPL-2.1` = spdx("Zope Public License 2.1", "ZPL-2.1", false, true) + val `AGPL-3.0` = spdx("GNU Affero General Public License v3.0", "AGPL-3.0", true, false) + val `eCos-2.0` = spdx("eCos license version 2.0", "eCos-2.0", false, false) + val `GFDL-1.1` = spdx("GNU Free Documentation License v1.1", "GFDL-1.1", false, false) + val `GFDL-1.2` = spdx("GNU Free Documentation License v1.2", "GFDL-1.2", false, false) + val `GFDL-1.3` = spdx("GNU Free Documentation License v1.3", "GFDL-1.3", false, false) + val `GPL-1.0+` = spdx("GNU General Public License v1.0 or later", "GPL-1.0+", false, false) + val `GPL-1.0` = spdx("GNU General Public License v1.0 only", "GPL-1.0", false, false) + val `GPL-2.0+` = spdx("GNU General Public License v2.0 or later", "GPL-2.0+", true, false) + val `GPL-2.0-with-autoconf-exception` = spdx("GNU General Public License v2.0 w/Autoconf exception", "GPL-2.0-with-autoconf-exception", false, false) + val `GPL-2.0-with-bison-exception` = spdx("GNU General Public License v2.0 w/Bison exception", "GPL-2.0-with-bison-exception", false, false) + val `GPL-2.0-with-classpath-exception` = spdx("GNU General Public License v2.0 w/Classpath exception", "GPL-2.0-with-classpath-exception", false, false) + val `GPL-2.0-with-font-exception` = spdx("GNU General Public License v2.0 w/Font exception", "GPL-2.0-with-font-exception", false, false) + val `GPL-2.0-with-GCC-exception` = spdx("GNU General Public License v2.0 w/GCC Runtime Library exception", "GPL-2.0-with-GCC-exception", false, false) + val `GPL-2.0` = spdx("GNU General Public License v2.0 only", "GPL-2.0", true, false) + val `GPL-3.0+` = spdx("GNU General Public License v3.0 or later", "GPL-3.0+", true, false) + val `GPL-3.0-with-autoconf-exception` = spdx("GNU General Public License v3.0 w/Autoconf exception", "GPL-3.0-with-autoconf-exception", false, false) + val `GPL-3.0-with-GCC-exception` = spdx("GNU General Public License v3.0 w/GCC Runtime Library exception", "GPL-3.0-with-GCC-exception", true, false) + val `GPL-3.0` = spdx("GNU General Public License v3.0 only", "GPL-3.0", true, false) + val `LGPL-2.0+` = spdx("GNU Library General Public License v2 or later", "LGPL-2.0+", true, false) + val `LGPL-2.0` = spdx("GNU Library General Public License v2 only", "LGPL-2.0", true, false) + val `LGPL-2.1+` = spdx("GNU Library General Public License v2 or later", "LGPL-2.1+", true, false) + val `LGPL-2.1` = spdx("GNU Lesser General Public License v2.1 only", "LGPL-2.1", true, false) + val `LGPL-3.0+` = spdx("GNU Lesser General Public License v3.0 or later", "LGPL-3.0+", true, false) + val `LGPL-3.0` = spdx("GNU Lesser General Public License v3.0 only", "LGPL-3.0", true, false) + val Nunit = spdx("Nunit License", "Nunit", false, false) + val `StandardML-NJ` = spdx("Standard ML of New Jersey License", "StandardML-NJ", false, false) + val wxWindows = spdx("wxWindows Library License", "wxWindows", false, false) + + private def spdx(fullName: String, id: String, isOsiApproved: Boolean, isFsfLibre: Boolean): License = + License(fullName, id, s"https://spdx.org/licenses/$id.html", isOsiApproved, isFsfLibre, "repo") + + val PublicDomain = License( + id = "Public Domain", + name = "Public Domain", + url = "https://creativecommons.org/publicdomain/zero/1.0/", + isOsiApproved = true, // sort of: https://opensource.org/faq#public-domain + isFsfLibre = true, // I'm not sure about this + distribution = "repo" + ) + + val Scala = License( + id = "Scala License", + name = "Scala License", + url = "http://www.scala-lang.org/license.html", + isOsiApproved = false, + isFsfLibre = false, + distribution = "repo" + ) + + val TypesafeSubscriptionAgreement = License( + id = "Typesafe Subscription Agreement", + name = "Typesafe Subscription Agreement", + url = "http://downloads.typesafe.com/website/legal/TypesafeSubscriptionAgreement.pdf", + isOsiApproved = false, + isFsfLibre = false, + distribution = "repo" + ) + + // https://github.com/sbt/sbt/issues/1937#issuecomment-214963983 + object Common { + val Apache2 = License.`Apache-2.0` + val MIT = License.MIT + val BSD4 = License.`BSD-4-Clause` + val Typesafe = License.TypesafeSubscriptionAgreement + val BSD3 = License.`BSD-3-Clause` + } +}
\ No newline at end of file diff --git a/scalalib/src/publish/LocalPublisher.scala b/scalalib/src/publish/LocalPublisher.scala new file mode 100644 index 00000000..d9839831 --- /dev/null +++ b/scalalib/src/publish/LocalPublisher.scala @@ -0,0 +1,32 @@ +package mill.scalalib.publish + + +object LocalPublisher { + + private val root: os.Path = os.home / ".ivy2" / "local" + + def publish(jar: os.Path, + sourcesJar: os.Path, + docJar: os.Path, + pom: os.Path, + ivy: os.Path, + artifact: Artifact): Unit = { + val releaseDir = root / artifact.group / artifact.id / artifact.version + writeFiles( + jar -> releaseDir / "jars" / s"${artifact.id}.jar", + sourcesJar -> releaseDir / "srcs" / s"${artifact.id}-sources.jar", + docJar -> releaseDir / "docs" / s"${artifact.id}-javadoc.jar", + pom -> releaseDir / "poms" / s"${artifact.id}.pom", + ivy -> releaseDir / "ivys" / "ivy.xml" + ) + } + + private def writeFiles(fromTo: (os.Path, os.Path)*): Unit = { + fromTo.foreach { + case (from, to) => + os.makeDir.all(to / os.up) + os.copy.over(from, to) + } + } + +} diff --git a/scalalib/src/publish/Pom.scala b/scalalib/src/publish/Pom.scala new file mode 100644 index 00000000..57a0e196 --- /dev/null +++ b/scalalib/src/publish/Pom.scala @@ -0,0 +1,117 @@ +package mill.scalalib.publish + +import mill.util.Loose.Agg + +import scala.xml.{Atom, Elem, NodeSeq, PrettyPrinter} + +object Pom { + + val head = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + + implicit class XmlOps(val e: Elem) extends AnyVal { + // source: https://stackoverflow.com/a/5254068/449071 + def optional : NodeSeq = { + require(e.child.length == 1) + e.child.head match { + case atom: Atom[Option[_]] => atom.data match { + case None => NodeSeq.Empty + case Some(x) => e.copy(child = x match { + case n: NodeSeq => n + case x => new Atom(x) + }) + } + case _ => e + } + } + } + + //TODO - not only jar packaging support? + def apply(artifact: Artifact, + dependencies: Agg[Dependency], + name: String, + pomSettings: PomSettings): String = { + val xml = + <project + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" + xmlns ="http://maven.apache.org/POM/4.0.0"> + + <modelVersion>4.0.0</modelVersion> + <name>{name}</name> + <groupId>{artifact.group}</groupId> + <artifactId>{artifact.id}</artifactId> + <packaging>jar</packaging> + <description>{pomSettings.description}</description> + + <version>{artifact.version}</version> + <url>{pomSettings.url}</url> + <licenses> + {pomSettings.licenses.map(renderLicense)} + </licenses> + <scm> + { <connection>{pomSettings.versionControl.connection}</connection>.optional } + { <developerConnection>{pomSettings.versionControl.developerConnection}</developerConnection>.optional } + { <tag>{pomSettings.versionControl.tag}</tag>.optional } + { <url>{pomSettings.versionControl.browsableRepository}</url>.optional } + </scm> + <developers> + {pomSettings.developers.map(renderDeveloper)} + </developers> + <dependencies> + {dependencies.map(renderDependency).toSeq} + </dependencies> + </project> + + val pp = new PrettyPrinter(120, 4) + head + pp.format(xml) + } + + private def renderLicense(l: License): Elem = { + <license> + <name>{l.name}</name> + <url>{l.url}</url> + <distribution>{l.distribution}</distribution> + </license> + } + + private def renderDeveloper(d: Developer): Elem = { + <developer> + <id>{d.id}</id> + <name>{d.name}</name> + { <organization>{d.organization}</organization>.optional } + { <organizationUrl>{d.organizationUrl}</organizationUrl>.optional } + </developer> + } + + private def renderDependency(d: Dependency): Elem = { + val scope = d.scope match { + case Scope.Compile => NodeSeq.Empty + case Scope.Provided => <scope>provided</scope> + case Scope.Test => <scope>test</scope> + case Scope.Runtime => <scope>runtime</scope> + } + if (d.exclusions.isEmpty) + <dependency> + <groupId>{d.artifact.group}</groupId> + <artifactId>{d.artifact.id}</artifactId> + <version>{d.artifact.version}</version> + {scope} + </dependency> + else + <dependency> + <groupId>{d.artifact.group}</groupId> + <artifactId>{d.artifact.id}</artifactId> + <version>{d.artifact.version}</version> + <exclusions> + {d.exclusions.map(ex => + <exclusion> + <groupId>{ex._1}</groupId> + <artifactId>{ex._2}</artifactId> + </exclusion> + )} + </exclusions> + {scope} + </dependency> + } + +} diff --git a/scalalib/src/publish/SonatypeHttpApi.scala b/scalalib/src/publish/SonatypeHttpApi.scala new file mode 100644 index 00000000..12defa93 --- /dev/null +++ b/scalalib/src/publish/SonatypeHttpApi.scala @@ -0,0 +1,134 @@ +package mill.scalalib.publish + +import java.util.Base64 + + + +import scala.concurrent.duration._ +import scalaj.http.{BaseHttp, HttpOptions, HttpRequest, HttpResponse} + +object PatientHttp + extends BaseHttp( + options = Seq( + HttpOptions.connTimeout(5.seconds.toMillis.toInt), + HttpOptions.readTimeout(1.minute.toMillis.toInt), + HttpOptions.followRedirects(false) + ) + ) + +class SonatypeHttpApi(uri: String, credentials: String) { + + private val base64Creds = base64(credentials) + + private val commonHeaders = Seq( + "Authorization" -> s"Basic $base64Creds", + "Accept" -> "application/json", + "Content-Type" -> "application/json" + ) + + // https://oss.sonatype.org/nexus-staging-plugin/default/docs/path__staging_profiles.html + def getStagingProfileUri(groupId: String): String = { + val response = withRetry( + PatientHttp(s"$uri/staging/profiles").headers(commonHeaders)) + .throwError + + val resourceUri = + ujson + .read(response.body)("data") + .arr + .find(profile => + groupId.split('.').startsWith(profile("name").str.split('.'))) + .map(_("resourceURI").str.toString) + + resourceUri.getOrElse( + throw new RuntimeException( + s"Could not find staging profile for groupId: ${groupId}") + ) + } + + def getStagingRepoState(stagingRepoId: String): String = { + val response = PatientHttp(s"${uri}/staging/repository/${stagingRepoId}") + .option(HttpOptions.readTimeout(60000)) + .headers(commonHeaders) + .asString + .throwError + + ujson.read(response.body)("type").str.toString + } + + // https://oss.sonatype.org/nexus-staging-plugin/default/docs/path__staging_profiles_-profileIdKey-_start.html + def createStagingRepo(profileUri: String, groupId: String): String = { + val response = withRetry(PatientHttp(s"${profileUri}/start") + .headers(commonHeaders) + .postData( + s"""{"data": {"description": "fresh staging profile for ${groupId}"}}""")) + .throwError + + ujson.read(response.body)("data")("stagedRepositoryId").str.toString + } + + // https://oss.sonatype.org/nexus-staging-plugin/default/docs/path__staging_profiles_-profileIdKey-_finish.html + def closeStagingRepo(profileUri: String, repositoryId: String): Boolean = { + val response = withRetry( + PatientHttp(s"${profileUri}/finish") + .headers(commonHeaders) + .postData( + s"""{"data": {"stagedRepositoryId": "${repositoryId}", "description": "closing staging repository"}}""" + )) + + response.code == 201 + } + + // https://oss.sonatype.org/nexus-staging-plugin/default/docs/path__staging_profiles_-profileIdKey-_promote.html + def promoteStagingRepo(profileUri: String, repositoryId: String): Boolean = { + val response = withRetry( + PatientHttp(s"${profileUri}/promote") + .headers(commonHeaders) + .postData( + s"""{"data": {"stagedRepositoryId": "${repositoryId}", "description": "promote staging repository"}}""" + )) + + response.code == 201 + } + + // https://oss.sonatype.org/nexus-staging-plugin/default/docs/path__staging_profiles_-profileIdKey-_drop.html + def dropStagingRepo(profileUri: String, repositoryId: String): Boolean = { + val response = withRetry( + PatientHttp(s"${profileUri}/drop") + .headers(commonHeaders) + .postData( + s"""{"data": {"stagedRepositoryId": "${repositoryId}", "description": "drop staging repository"}}""" + )) + + response.code == 201 + } + + private val uploadTimeout = 5.minutes.toMillis.toInt + + def upload(uri: String, data: Array[Byte]): HttpResponse[String] = { + PatientHttp(uri) + .option(HttpOptions.readTimeout(uploadTimeout)) + .method("PUT") + .headers( + "Content-Type" -> "application/binary", + "Authorization" -> s"Basic ${base64Creds}" + ) + .put(data) + .asString + } + + private def withRetry(request: HttpRequest, + retries: Int = 10): HttpResponse[String] = { + val resp = request.asString + if (resp.is5xx && retries > 0) { + Thread.sleep(500) + withRetry(request, retries - 1) + } else { + resp + } + } + + private def base64(s: String) = + new String(Base64.getEncoder.encode(s.getBytes)) + +} diff --git a/scalalib/src/publish/SonatypePublisher.scala b/scalalib/src/publish/SonatypePublisher.scala new file mode 100644 index 00000000..1843943b --- /dev/null +++ b/scalalib/src/publish/SonatypePublisher.scala @@ -0,0 +1,164 @@ +package mill.scalalib.publish + +import java.math.BigInteger +import java.security.MessageDigest + +import mill.api.Logger + +import scalaj.http.HttpResponse + +class SonatypePublisher(uri: String, + snapshotUri: String, + credentials: String, + gpgPassphrase: Option[String], + signed: Boolean, + log: Logger) { + + private val api = new SonatypeHttpApi(uri, credentials) + + def publish(fileMapping: Seq[(os.Path, String)], artifact: Artifact, release: Boolean): Unit = { + publishAll(release, fileMapping -> artifact) + } + def publishAll(release: Boolean, artifacts: (Seq[(os.Path, String)], Artifact)*): Unit = { + + val mappings = for ((fileMapping0, artifact) <- artifacts) yield { + val publishPath = Seq( + artifact.group.replace(".", "/"), + artifact.id, + artifact.version + ).mkString("/") + val fileMapping = fileMapping0.map{ case (file, name) => (file, publishPath+"/"+name) } + + val signedArtifacts = if (signed) fileMapping.map { + case (file, name) => poorMansSign(file, gpgPassphrase) -> s"$name.asc" + } else Seq() + + artifact -> (fileMapping ++ signedArtifacts).flatMap { + case (file, name) => + val content = os.read.bytes(file) + + Seq( + name -> content, + (name + ".md5") -> md5hex(content), + (name + ".sha1") -> sha1hex(content) + ) + } + } + + val (snapshots, releases) = mappings.partition(_._1.isSnapshot) + if(snapshots.nonEmpty) { + publishSnapshot(snapshots.flatMap(_._2), snapshots.map(_._1)) + } + val releaseGroups = releases.groupBy(_._1.group) + for((group, groupReleases) <- releaseGroups){ + publishRelease(release, groupReleases.flatMap(_._2), group, releases.map(_._1)) + } + } + + private def publishSnapshot(payloads: Seq[(String, Array[Byte])], + artifacts: Seq[Artifact]): Unit = { + + val publishResults = payloads.map { + case (fileName, data) => + log.info(s"Uploading $fileName") + val resp = api.upload(s"$snapshotUri/$fileName", data) + resp + } + reportPublishResults(publishResults, artifacts) + } + + private def publishRelease(release: Boolean, + payloads: Seq[(String, Array[Byte])], + stagingProfile: String, + artifacts: Seq[Artifact]): Unit = { + val profileUri = api.getStagingProfileUri(stagingProfile) + val stagingRepoId = + api.createStagingRepo(profileUri, stagingProfile) + val baseUri = s"$uri/staging/deployByRepositoryId/$stagingRepoId/" + + val publishResults = payloads.map { + case (fileName, data) => + log.info(s"Uploading ${fileName}") + api.upload(s"$baseUri/$fileName", data) + } + reportPublishResults(publishResults, artifacts) + + if (release) { + log.info("Closing staging repository") + api.closeStagingRepo(profileUri, stagingRepoId) + + log.info("Waiting for staging repository to close") + awaitRepoStatus("closed", stagingRepoId) + + log.info("Promoting staging repository") + api.promoteStagingRepo(profileUri, stagingRepoId) + + log.info("Waiting for staging repository to release") + awaitRepoStatus("released", stagingRepoId) + + log.info("Dropping staging repository") + api.dropStagingRepo(profileUri, stagingRepoId) + + log.info(s"Published ${artifacts.map(_.id).mkString(", ")} successfully") + } + } + + private def reportPublishResults(publishResults: Seq[HttpResponse[String]], + artifacts: Seq[Artifact]) = { + if (publishResults.forall(_.is2xx)) { + log.info(s"Published ${artifacts.map(_.id).mkString(", ")} to Sonatype") + } else { + val errors = publishResults.filterNot(_.is2xx).map { response => + s"Code: ${response.code}, message: ${response.body}" + } + throw new RuntimeException( + s"Failed to publish ${artifacts.map(_.id).mkString(", ")} to Sonatype. Errors: \n${errors.mkString("\n")}" + ) + } + } + + private def awaitRepoStatus(status: String, + stagingRepoId: String, + attempts: Int = 20): Unit = { + def isRightStatus = + api.getStagingRepoState(stagingRepoId).equalsIgnoreCase(status) + var attemptsLeft = attempts + + while (attemptsLeft > 0 && !isRightStatus) { + Thread.sleep(3000) + attemptsLeft -= 1 + if (attemptsLeft == 0) { + throw new RuntimeException( + s"Couldn't wait for staging repository to be ${status}. Failing") + } + } + } + + // http://central.sonatype.org/pages/working-with-pgp-signatures.html#signing-a-file + private def poorMansSign(file: os.Path, maybePassphrase: Option[String]): os.Path = { + val fileName = file.toString + maybePassphrase match { + case Some(passphrase) => + os.proc("gpg", "--passphrase", passphrase, "--batch", "--yes", "-a", "-b", fileName) + .call(stdin = os.Inherit, stdout = os.Inherit, stderr = os.Inherit) + case None => + os.proc("gpg", "--batch", "--yes", "-a", "-b", fileName) + .call(stdin = os.Inherit, stdout = os.Inherit, stderr = os.Inherit) + } + os.Path(fileName + ".asc") + } + + private def md5hex(bytes: Array[Byte]): Array[Byte] = + hexArray(md5.digest(bytes)).getBytes + + private def sha1hex(bytes: Array[Byte]): Array[Byte] = + hexArray(sha1.digest(bytes)).getBytes + + private def md5 = MessageDigest.getInstance("md5") + + private def sha1 = MessageDigest.getInstance("sha1") + + private def hexArray(arr: Array[Byte]) = + String.format("%0" + (arr.length << 1) + "x", new BigInteger(1, arr)) + +} diff --git a/scalalib/src/publish/VersionControl.scala b/scalalib/src/publish/VersionControl.scala new file mode 100644 index 00000000..aad38ac3 --- /dev/null +++ b/scalalib/src/publish/VersionControl.scala @@ -0,0 +1,131 @@ +package mill.scalalib.publish + +// https://maven.apache.org/pom.html#SCM +/* + * @param browsableRepository: a publicly browsable repository + * (example: https://github.com/lihaoyi/mill) + * @param connection: read-only connection to repository + * (example: scm:git:git://github.com/lihaoyi/mill.git) + * @param developerConnection: read-write connection to repository + * (example: scm:git:git@github.com:lihaoyi/mill.git) + * @param tag: tag that was created for this release. This is useful for + * git and mercurial since it's not possible to include the tag in + * the connection url. + * (example: v2.12.4, HEAD, my-branch, fd8a2567ad32c11bcf8adbaca85bdba72bb4f935, ...) + */ +case class VersionControl( + browsableRepository: Option[String] = None, + connection: Option[String] = None, + developerConnection: Option[String] = None, + tag: Option[String] = None +) + +@deprecated("use VersionControl", "0.1.3") +case class SCM( + url: String, + connection: String +) + +object VersionControl { + def github(owner: String, repo: String, tag: Option[String] = None): VersionControl = + VersionControl( + browsableRepository = Some(s"https://github.com/$owner/$repo"), + connection = Some(VersionControlConnection.gitGit("github.com", s"$owner/$repo.git")), + developerConnection = Some(VersionControlConnection.gitSsh("github.com", s":$owner/$repo.git", username = Some("git"))), + tag = tag + ) + def gitlab(owner: String, repo: String, tag: Option[String] = None): VersionControl = + VersionControl( + browsableRepository = Some(s"https://gitlab.com/$owner/$repo"), + connection = Some(VersionControlConnection.gitGit("gitlab.com", s"$owner/$repo.git")), + developerConnection = Some(VersionControlConnection.gitSsh("gitlab.com", s":$owner/$repo.git", username = Some("git"))), + tag = tag + ) +} + +object VersionControlConnection { + def network(scm: String, + protocol: String, + hostname: String, + path: String, + username: Option[String] = None, + password: Option[String] = None, + port: Option[Int] = None): String = { + val portPart = port.map(":" + _).getOrElse("") + val credentials = + username match { + case Some(user) => + val pass = password.map(":" + _).getOrElse("") + s"${user}${pass}@" + case None => + password match { + case Some(p) => sys.error(s"no username set for password: $p") + case _ => "" + } + } + + val path0 = + if(path.startsWith(":") || path.startsWith("/")) path + else "/" + path + + s"scm:${scm}:${protocol}://${credentials}${hostname}${portPart}${path0}" + } + + def file(scm: String, path: String): String = { + s"scm:$scm:file://$path" + } + + def gitGit(hostname: String, + path: String = "", + port: Option[Int] = None): String = + network("git", "git", hostname, path, port = port) + + def gitHttp(hostname: String, + path: String = "", + port: Option[Int] = None): String = + network("git", "http", hostname, path, port = port) + + def gitHttps(hostname: String, + path: String = "", + port: Option[Int] = None): String = + network("git", "https", hostname, path, port = port) + + def gitSsh(hostname: String, + path: String = "", + username: Option[String] = None, + port: Option[Int] = None): String = + network("git", "ssh", hostname, path, username = username, port = port) + + def gitFile(path: String): String = + file("git", path) + + def svnSsh(hostname: String, + path: String = "", + username: Option[String] = None, + port: Option[Int] = None): String = + network("svn", "svn+ssh", hostname, path, username, None, port) + + def svnHttp(hostname: String, + path: String = "", + username: Option[String] = None, + password: Option[String] = None, + port: Option[Int] = None): String = + network("svn", "http", hostname, path, username, password, port) + + def svnHttps(hostname: String, + path: String = "", + username: Option[String] = None, + password: Option[String] = None, + port: Option[Int] = None): String = + network("svn", "https", hostname, path, username, password, port) + + def svnSvn(hostname: String, + path: String = "", + username: Option[String] = None, + password: Option[String] = None, + port: Option[Int] = None): String = + network("svn", "svn", hostname, path, username, password, port) + + def svnFile(path: String): String = + file("svn", path) +}
\ No newline at end of file diff --git a/scalalib/src/publish/package.scala b/scalalib/src/publish/package.scala new file mode 100644 index 00000000..99eeec14 --- /dev/null +++ b/scalalib/src/publish/package.scala @@ -0,0 +1,3 @@ +package mill.scalalib + +package object publish extends JsonFormatters diff --git a/scalalib/src/publish/settings.scala b/scalalib/src/publish/settings.scala new file mode 100644 index 00000000..bca81cf0 --- /dev/null +++ b/scalalib/src/publish/settings.scala @@ -0,0 +1,91 @@ +package mill.scalalib.publish + +import mill.scalalib.Dep + +case class Artifact(group: String, id: String, version: String) { + def isSnapshot: Boolean = version.endsWith("-SNAPSHOT") +} + +object Artifact { + def fromDepJava(dep: Dep) = { + assert(dep.cross.isConstant, s"Not a Java dependency: $dep") + fromDep(dep, "", "", "") + } + + def fromDep(dep: Dep, + scalaFull: String, + scalaBin: String, + platformSuffix: String): Dependency = { + val name = dep.artifactName( + binaryVersion = scalaBin, + fullVersion = scalaFull, + platformSuffix = platformSuffix + ) + Dependency( + Artifact( + dep.dep.module.organization, + name, + dep.dep.version + ), + Scope.Compile, + if (dep.dep.configuration == "") None else Some(dep.dep.configuration), + dep.dep.exclusions.toList + ) + } +} + +sealed trait Scope +object Scope { + case object Compile extends Scope + case object Provided extends Scope + case object Runtime extends Scope + case object Test extends Scope +} + +case class Dependency( + artifact: Artifact, + scope: Scope, + configuration: Option[String] = None, + exclusions: Seq[(String, String)] = Nil +) + +case class Developer( + id: String, + name: String, + url: String, + organization: Option[String] = None, + organizationUrl: Option[String] = None +) + +case class PomSettings( + description: String, + organization: String, + url: String, + licenses: Seq[License], + versionControl: VersionControl, + developers: Seq[Developer] +) + +object PomSettings { + @deprecated("use VersionControl instead of SCM", "0.1.3") + def apply(description: String, + organization: String, + url: String, + licenses: Seq[License], + scm: SCM, + developers: Seq[Developer]): PomSettings = { + PomSettings( + description = description, + organization = organization, + url = url, + licenses = licenses, + versionControl = VersionControl( + browsableRepository = Some(scm.url), + connection = Some(scm.connection), + developerConnection = None, + tag = None + ), + developers = developers + ) + } +} |