diff --git a/.circleci/config.yml b/.circleci/config.yml index 09a3e4fc..b26a85e0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,9 +1,9 @@ aliases: - &restore_sbt_cache - key: sbt-{{ checksum "/tmp/scala_version" }}-cache + key: sbt-cache-{{ checksum "/tmp/scala_version" }} - &save_sbt_cache - key: sbt-{{ checksum "/tmp/scala_version" }}-cache-{{ epoch }} + key: sbt-cache-{{ checksum "/tmp/scala_version" }}-{{ epoch }} paths: - "~/.ivy2/cache" - "~/.sbt" @@ -18,25 +18,87 @@ aliases: command: ./scripts/cibuild - save_cache: *save_sbt_cache + - &run_cipublish + - checkout + - run: echo "${SCALA_VERSION}" > /tmp/scala_version + - restore_cache: *restore_sbt_cache + - run: + name: "Import signing key" + command: | + echo "${GPG_KEY}" | base64 -d > signing_key.asc && \ + gpg --batch \ + --passphrase "${GPG_PASSPHRASE}" \ + --import signing_key.asc + - run: + name: Executing cipublish + command: ./scripts/cipublish + + # Build environments + - &openjdk8-scala2_11_12-nodelts_environment + docker: + - image: circleci/openjdk:8-stretch-node + environment: + SCALA_VERSION: 2.11.12 + + - &openjdk8-scala2_12_8-nodelts_environment + docker: + - image: circleci/openjdk:8-stretch-node + environment: + SCALA_VERSION: 2.12.8 + version: 2 workflows: version: 2 build: jobs: - - "openjdk8-scala2.11.12-nodelts" - - "openjdk8-scala2.12.8-nodelts" + - "openjdk8-scala2.11.12-nodelts": + filters: # required since `openjdk8-scala2.11.12-nodelts_deploy` has tag filters AND requires `openjdk8-scala2.11.12-nodelts` + tags: + only: + - /^(.*)$/ + - "openjdk8-scala2.12.8-nodelts": + filters: # required since `openjdk8-scala2.12.8-nodelts_deploy` has tag filters AND requires `openjdk8-scala2.12.8-nodelts` + tags: + only: + - /^(.*)$/ + - "openjdk8-scala2.11.12-nodelts_deploy": + requires: + - "openjdk8-scala2.11.12-nodelts" + filters: + tags: + only: + - /^(.*)$/ + branches: + only: + - master + - develop + - feature/jrb/automated-releases-to-sonatype + - "openjdk8-scala2.12.8-nodelts_deploy": + requires: + - "openjdk8-scala2.12.8-nodelts" + filters: + tags: + only: + - /^(.*)$/ + branches: + only: + - master + - develop + - feature/jrb/automated-releases-to-sonatype jobs: "openjdk8-scala2.11.12-nodelts": - docker: - - image: circleci/openjdk:8-stretch-node - environment: - SCALA_VERSION: 2.11.12 + <<: *openjdk8-scala2_11_12-nodelts_environment steps: *run_cibuild "openjdk8-scala2.12.8-nodelts": - docker: - - image: circleci/openjdk:8-stretch-node - environment: - SCALA_VERSION: 2.12.8 + <<: *openjdk8-scala2_12_8-nodelts_environment steps: *run_cibuild + + "openjdk8-scala2.11.12-nodelts_deploy": + <<: *openjdk8-scala2_11_12-nodelts_environment + steps: *run_cipublish + + "openjdk8-scala2.12.8-nodelts_deploy": + <<: *openjdk8-scala2_12_8-nodelts_environment + steps: *run_cipublish diff --git a/.github/ISSUE_TEMPLATE/release.md b/.github/ISSUE_TEMPLATE/release.md new file mode 100644 index 00000000..de05db84 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/release.md @@ -0,0 +1,31 @@ +--- +name: Release +about: When ready to cut a release +title: Release X.Y.Z +labels: release +assignees: '' + +--- + +- [ ] Start a new release branch: +```bash +$ git flow release start X.Y.Z +``` +- [ ] Rotate `CHANGELOG.md` (following [Keep a Changelog](https://keepachangelog.com/) principles) +- [ ] Ensure outstanding changes are committed: +```bash +$ git status # Is the git staging area clean? +$ git add CHANGELOG.md +$ git commit -m "X.Y.Z" +``` +- [ ] Publish the release branch: +```bash +$ git flow release publish X.Y.Z +``` +- [ ] Ensure that CI checks pass +- [ ] Finish and publish the release branch: + - When prompted, keep default commit messages + - Use `X.Y.Z` as the tag message +```bash +$ git flow release finish -p X.Y.Z +``` diff --git a/.jvmopts b/.jvmopts new file mode 100644 index 00000000..56aefe05 --- /dev/null +++ b/.jvmopts @@ -0,0 +1 @@ +-Xmx4G diff --git a/CHANGELOG.md b/CHANGELOG.md index e7939431..91ca58b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add STRTA and migrate to CircleCI [#93](https://github.com/geotrellis/maml/pull/93) - Add changelog and pull request template [#96](https://github.com/geotrellis/maml/pull/96) - Added `ParallelInterpreter` [#101](https://github.com/geotrellis/maml/pull/101) +- Add automated releases to Sonatype Nexus through CI [#98](https://github.com/geotrellis/maml/pull/98) ### Changed - Fixed 2.12 compilation in tests [#95](https://github.com/geotrellis/maml/pull/95) diff --git a/build.sbt b/build.sbt index 016506ce..1f863a86 100644 --- a/build.sbt +++ b/build.sbt @@ -1,22 +1,102 @@ +import xerial.sbt.Sonatype._ + import com.scalapenos.sbt.prompt.SbtPrompt.autoImport._ promptTheme := com.scalapenos.sbt.prompt.PromptThemes.ScalapenosTheme -/** Project configurations */ +val commonSettings = Seq( + // We are overriding the default behavior of sbt-git which, by default, + // only appends the `-SNAPSHOT` suffix if there are uncommitted + // changes in the workspace. + version := { + // Avoid Cyclic reference involving error + if (git.gitCurrentTags.value.isEmpty || git.gitUncommittedChanges.value) + git.gitDescribedVersion.value.get + "-SNAPSHOT" + else + git.gitDescribedVersion.value.get + }, + scalaVersion := "2.11.12", + crossScalaVersions := Seq("2.11.12", "2.12.8"), + resolvers ++= Seq( + Resolver.sonatypeRepo("releases"), + "locationtech-releases" at "https://repo.locationtech.org/content/groups/releases", + "locationtech-snapshots" at "https://repo.locationtech.org/content/groups/snapshots" + ), + addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full), + scalacOptions := Seq( + "-deprecation", + "-unchecked", + "-feature", + "-language:implicitConversions", + "-language:reflectiveCalls", + "-language:higherKinds", + "-language:postfixOps", + "-language:existentials", + "-language:experimental.macros", + "-feature", + "-Ypatmat-exhaust-depth", "100" + ) +) + +lazy val noPublishSettings = Seq( + publish := {}, + publishLocal := {}, + publishArtifact := false +) + +lazy val publishSettings = Seq( + organization := "com.azavea.geotrellis", + organizationName := "GeoTrellis", + organizationHomepage := Some(new URL("https://geotrellis.io/")), + description := "MAML is used to create a declarative structure that describes a combination of map algebra operations.", + publishArtifact in Test := false +) ++ sonatypeSettings ++ credentialSettings + +lazy val sonatypeSettings = Seq( + publishMavenStyle := true, + + sonatypeProfileName := "com.azavea", + sonatypeProjectHosting := Some(GitHubHosting(user="geotrellis", repository="maml", email="systems@azavea.com")), + developers := List( + Developer(id = "moradology", name = "Nathan Zimmerman", email = "nzimmerman@azavea.com", url = url("https://github.com/moradology")), + Developer(id = "echeipesh", name = "Eugene Cheipesh", email = "echeipesh@azavea.com", url = url("https://github.com/echeipesh")), + Developer(id = "lossyrob", name = "Rob Emanuele", email = "remanuele@azavea.com", url = url("https://github.com/lossyrob")) + ), + licenses := Seq("Apache-2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0.txt")), + + publishTo := sonatypePublishTo.value +) + +lazy val credentialSettings = Seq( + credentials += Credentials( + "GnuPG Key ID", + "gpg", + System.getenv().get("GPG_KEY_ID"), + "ignored" + ), + + credentials += Credentials( + "Sonatype Nexus Repository Manager", + "oss.sonatype.org", + System.getenv().get("SONATYPE_USERNAME"), + System.getenv().get("SONATYPE_PASSWORD") + ) +) + lazy val root = project.in(file(".")) + .settings(commonSettings) + .settings(publishSettings) // these settings are needed to release all aggregated modules under this root module + .settings(noPublishSettings) // this is to exclue the root module itself from being published .aggregate(mamlJs, mamlJvm, mamlSpark) - .settings(commonSettings:_*) - .settings( - licenses += ("Apache-2.0", url("http://www.apache.org/licenses/LICENSE-2.0.txt")) - ).enablePlugins(ScalaJSPlugin) + .enablePlugins(ScalaJSPlugin) val circeVer = "0.11.1" val circeOpticsVer = "0.11.0" val gtVer = "3.0.0-M3" lazy val maml = crossProject.in(file(".")) - .settings(publishSettings:_*) - .settings(commonSettings:_*) + .settings(commonSettings) + .settings(publishSettings) .settings( libraryDependencies ++= Seq( "org.scalacheck" %% "scalacheck" % "1.13.4" % "test", @@ -40,51 +120,13 @@ lazy val maml = crossProject.in(file(".")) lazy val mamlJvm = maml.jvm lazy val mamlJs = maml.js lazy val mamlSpark = project.in(file("spark")) - .dependsOn(mamlJvm) + .settings(commonSettings) + .settings(publishSettings) .settings( libraryDependencies ++= Seq( "org.locationtech.geotrellis" %% "geotrellis-spark-testkit" % gtVer % "test", "org.apache.spark" %% "spark-core" % "2.4.0" % "provided", "org.apache.spark" %% "spark-sql" % "2.4.0" % "provided" ) - ).settings(publishSettings:_*) - .settings(commonSettings:_*) - - -/** Common settings */ -lazy val publishSettings = - Seq( - bintrayOrganization := Some("azavea"), - bintrayRepository := "maven", - bintrayVcsUrl := Some("https://github.com/geotrellis/maml.git"), - publishMavenStyle := true, - publishArtifact in Test := false, - pomIncludeRepository := { _ => false }, - homepage := Some(url("https://geotrellis.github.io/maml")) - ) - -val commonSettings = Seq( - organization := "com.azavea", - licenses += ("Apache-2.0", url("http://www.apache.org/licenses/LICENSE-2.0.txt")), - scalaVersion := "2.11.12", - crossScalaVersions := Seq("2.11.12", "2.12.7"), - resolvers ++= Seq( - Resolver.sonatypeRepo("releases"), - "locationtech-releases" at "https://repo.locationtech.org/content/groups/releases", - "locationtech-snapshots" at "https://repo.locationtech.org/content/groups/snapshots" - ), - addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full), - scalacOptions := Seq( - "-deprecation", - "-unchecked", - "-feature", - "-language:implicitConversions", - "-language:reflectiveCalls", - "-language:higherKinds", - "-language:postfixOps", - "-language:existentials", - "-language:experimental.macros", - "-feature", - "-Ypatmat-exhaust-depth", "100" ) -) + .dependsOn(mamlJvm) diff --git a/project/build.properties b/project/build.properties index 133a8f19..c0bab049 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.17 +sbt.version=1.2.8 diff --git a/project/plugins.sbt b/project/plugins.sbt index 75c7a1ca..32d6b1ee 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,28 +1,21 @@ -resolvers ++= Seq( - Classpaths.sbtPluginReleases, - Opts.resolver.sonatypeReleases -) +addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.4.0") -resolvers += Classpaths.typesafeResolver - -addSbtPlugin("org.foundweekends" % "sbt-bintray" % "0.5.1") - -addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.1.10") - -addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.5") +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9") addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.9.2") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.25") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.27") -addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.1.0") +addSbtPlugin("io.spray" % "sbt-revolver" % "0.9.1") -addSbtPlugin("io.spray" % "sbt-revolver" % "0.8.0") +addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.19") -addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.2.0") - -addSbtPlugin("com.47deg" % "sbt-microsites" % "0.6.1") +addSbtPlugin("com.47deg" % "sbt-microsites" % "0.9.0") addSbtPlugin("com.scalapenos" % "sbt-prompt" % "1.0.2") -addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.11") +addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "2.5") + +addSbtPlugin("io.crashbox" % "sbt-gpg" % "0.2.0") + +addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "1.0.0") diff --git a/scripts/cipublish b/scripts/cipublish new file mode 100755 index 00000000..41b73c08 --- /dev/null +++ b/scripts/cipublish @@ -0,0 +1,27 @@ +#!/bin/bash + +set -e + +if [[ -n "${MAML_DEBUG}" ]]; then + set -x +fi + +function usage() { + echo -n \ + "Usage: $(basename "$0") +Publish artifacts to Sonatype. +" +} + +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + if [[ "${1:-}" == "--help" ]]; then + usage + else + echo "Publishing artifacts to Sonatype" + if [[ -n "${CIRCLE_TAG}" ]]; then + ./sbt "++${SCALA_VERSION:-2.11.12}" publish sonatypeRelease + else + ./sbt "++${SCALA_VERSION:-2.11.12}" publish + fi + fi +fi diff --git a/version.sbt b/version.sbt deleted file mode 100644 index dfd7e193..00000000 --- a/version.sbt +++ /dev/null @@ -1 +0,0 @@ -version in ThisBuild := "0.3.4-SNAPSHOT"