From b188d73a0ce0f05a8dceec1a86dfa10b6c632cb7 Mon Sep 17 00:00:00 2001 From: Roberto Tyley Date: Wed, 24 Jan 2024 17:57:04 +0000 Subject: [PATCH] Adopt GHA-Scala-Library-Release-Workflow (Scala Maven release only) This change adopts `gha-scala-library-release-workflow` for publishing releases, replacing some of the work done in PR #229 and providing some improvements: * Automatic version-numbering based on automated compatibility-assessment by sbt-version-policy * Standardisation with the other projects across the Guardian that have also adopted the workflow * Reduced configuration (fewer sbt settings, and less workflow yaml) This specific commit only provides Scala Maven release, removing NPM release capability, but NPM release capability is returned in the next commit. --- .github/workflows/release.yml | 93 ++---------------- README.md | 15 +-- build.sbt | 173 +++++----------------------------- project/plugins.sbt | 2 +- version.sbt | 1 + 5 files changed, 35 insertions(+), 249 deletions(-) create mode 100644 version.sbt diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a5c33101..950d9a4e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,91 +1,12 @@ -name: Publish to Sonatype and NPM +name: Release on: workflow_dispatch: - release: - types: [published] jobs: - release_snapshot_sonatype: - if: "github.event.release.prerelease" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - base: main #see https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.md#pull-request-events - - uses: actions/setup-java@v3 - with: - distribution: corretto - java-version: 11 - cache: sbt - - uses: actions/setup-node@v3 - with: - node-version-file: .nvmrc - # We cannot cache our environment, as it is dynamically generated by the plugin - - name: Get tags - run: git fetch --tags origin - - name: Release pre-release version to Sonatype and NPM - run: | - VERSION=$(git describe --tags | cut -f2 -d"@") - if [[ ${VERSION:0:1} == "v" ]] ; then - VERSION=${VERSION:1} - fi - if [[ ${VERSION: -9} != "-SNAPSHOT" ]] ; then - echo "Version must end in -SNAPSHOT. Adding -SNAPSHOT suffix" - VERSION="$VERSION-SNAPSHOT" - fi - echo $PGP_SECRET | base64 --decode | gpg --batch --import - export GPG_TTY=$(tty) - echo "Releasing version $VERSION Sonatype as snapshot" - - # No need to support NPM releases – there's no direct analogue to a SNAPSHOT release. - # Beta releases are classed as 'production' releases. - sbt -J-Xss32M -DRELEASE_TYPE=snapshot "clean" "release cross release-version $VERSION with-defaults" - - env: - # We use armour formatted files for keys, but we know the newlines they contain don't survive - # passing through environment vars, so this secret should be an armour-formatted PGP key passed - # through base64. - PGP_SECRET: ${{ secrets.PGP_SECRET }} - PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} - SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} - SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} - - release_production_sonatype: - #if: "!github.event.release.prerelease" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - base: main #see https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.md#pull-request-events - - uses: actions/setup-java@v3 - with: - distribution: corretto - java-version: 11 - cache: sbt - - uses: actions/setup-node@v3 - with: - node-version-file: .nvmrc - registry-url: https://registry.npmjs.org - - name: Release Production to Sonatype - run: | - VERSION=$(git describe --tags | cut -f2 -d"@") - if [[ ${VERSION:0:1} == "v" ]] ; then - VERSION=${VERSION:1} - fi - - if [[ ${VERSION: -9} == "-SNAPSHOT" ]] ; then - echo "Version must NOT end in -SNAPSHOT." - exit 1 - fi - - echo $PGP_SECRET | base64 --decode | gpg --batch --import - export GPG_TTY=$(tty) - echo "Releasing version $VERSION Sonatype as production" - yes | sbt -J-Xss32M "clean" "release cross release-version $VERSION with-defaults" "project typescript" "releaseNpm $VERSION" - env: - PGP_SECRET: ${{ secrets.PGP_SECRET }} - PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} - SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} - SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + release: + uses: guardian/gha-scala-library-release-workflow/.github/workflows/reusable-release.yml@main + permissions: { contents: write, pull-requests: write } + secrets: + SONATYPE_PASSWORD: ${{ secrets.AUTOMATED_MAVEN_RELEASE_SONATYPE_PASSWORD }} + PGP_PRIVATE_KEY: ${{ secrets.AUTOMATED_MAVEN_RELEASE_PGP_SECRET }} diff --git a/README.md b/README.md index eb528e7e..50682ee6 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![content-api-models-scala Scala version support](https://index.scala-lang.org/guardian/content-api-models/content-api-models-scala/latest-by-scala-version.svg?platform=jvm)](https://index.scala-lang.org/guardian/content-api-models/content-api-models-scala) [![content-api-models-json Scala version support](https://index.scala-lang.org/guardian/content-api-models/content-api-models-json/latest-by-scala-version.svg?platform=jvm)](https://index.scala-lang.org/guardian/content-api-models/content-api-models-json) [![CI](https://github.com/guardian/content-api-models/actions/workflows/ci.yml/badge.svg)](https://github.com/guardian/content-api-models/actions/workflows/ci.yml) +[![Release](https://github.com/guardian/content-api-models/actions/workflows/release.yml/badge.svg)](https://github.com/guardian/content-api-models/actions/workflows/release.yml) ## Version Info @@ -11,18 +12,10 @@ # Publishing a new release -This repository has a Github Action that will create new releases for Sonatype and NPM when a new release is created in Github. +This repo uses [`gha-scala-library-release-workflow`](https://github.com/guardian/gha-scala-library-release-workflow) +to automate publishing releases (both full & preview releases) - see +[**Making a Release**](https://github.com/guardian/gha-scala-library-release-workflow/blob/main/docs/making-a-release.md). -- Push the branch with the changes you want to release to Github. -- Begin creating a new release (here's a [quick link.](https://github.com/guardian/flexible-model/releases/new)) -- Set the `Target` to your branch. -- Create a tag: -- - For a production release, the tag should be the new version number, e.g. `vX.X.X`. Beta releases are production releases – for example `v1.0.0-beta.0`. -- - For a snapshot release, the tag should ideally have the format `vX.X.X-SNAPSHOT`. -- **If you are intending to release a snapshot,** double-check that the "Set as pre-release" box is ticked. -- Click the "Publish release" button. The action will trigger, and your release should be on its way. - -To release a package from your local machine, follow the instructions for [publishing a new version to Maven Central via Sonatype](https://docs.google.com/document/d/1rNXjoZDqZMsQblOVXPAIIOMWuwUKe3KzTCttuqS7AcY/edit#). ## Information about built bundles diff --git a/build.sbt b/build.sbt index dceb29d5..0a3b8c59 100644 --- a/build.sbt +++ b/build.sbt @@ -1,7 +1,7 @@ import sbt.Keys.* import sbt.{Test, Tests} import sbtrelease.ReleaseStateTransformations.* -import sbtrelease.{Version, versionFormatError} +import sbtversionpolicy.withsbtrelease.ReleaseVersion // dependency versions val contentEntityVersion = "2.2.1" @@ -23,163 +23,47 @@ val betaReleaseSuffix = "-beta.0" val snapshotReleaseType = "snapshot" val snapshotReleaseSuffix = "-SNAPSHOT" -lazy val mavenSettings = Seq( - pomExtra := ( - https://github.com/guardian/content-api-models - - scm:git:git@github.com:guardian/content-api-models.git - scm:git:git@github.com:guardian/content-api-models.git - git@github.com:guardian/content-api-models.git - - - - cb372 - Chris Birchall - https://github.com/cb372 - - - mchv - Mariot Chauvin - https://github.com/mchv - - - LATaylor-guardian - Luke Taylor - https://github.com/LATaylor-guardian - - - regiskuckaertz - Regis Kuckaertz - https://github.com/regiskuckaertz - - - annebyrne - Anne Byrne - https://github.com/annebyrne - - - justinpinner - Justin Pinner - https://github.com/justinpinner - - - ), - publishTo := sonatypePublishToBundle.value, - publishConfiguration := publishConfiguration.value.withOverwrite(true), - publishMavenStyle := true, - Test / publishArtifact := false, - pomIncludeRepository := { _ => false } -) - -lazy val versionSettingsMaybe = { - sys.props.get("RELEASE_TYPE").map { - case v if v == betaReleaseType => betaReleaseSuffix - case v if v == snapshotReleaseType => snapshotReleaseSuffix - }.map { suffix => - releaseVersion := { - ver => Version(ver).map(_.withoutQualifier.string).map(_.concat(suffix)).getOrElse(versionFormatError(ver)) - } - }.toSeq -} -lazy val commonSettings = Seq( +lazy val artifactProductionSettings = Seq( scalaVersion := "2.13.12", - // downgrade scrooge reserved word clashes to warnings + // This old attempt to downgrade scrooge reserved word clashes is now insufficient... https://github.com/twitter/scrooge/issues/259#issuecomment-1900743695 Compile / scroogeDisableStrict := true, // scrooge 21.3.0: Builds are now only supported for Scala 2.12+ // https://twitter.github.io/scrooge/changelog.html#id11 crossScalaVersions := Seq("2.12.18", scalaVersion.value), - releasePublishArtifactsAction := PgpKeys.publishSigned.value, organization := "com.gu", licenses := Seq("Apache v2" -> url("http://www.apache.org/licenses/LICENSE-2.0.html")), resolvers ++= Resolver.sonatypeOssRepos("public"), Test / testOptions += Tests.Argument(TestFrameworks.ScalaTest, "-u", s"test-results/scala-${scalaVersion.value}", "-o") -) ++ mavenSettings ++ versionSettingsMaybe - -/* - Trialling being able to release snapshot versions from WIP branch without updating back to git - e.g. $ sbt [-DRELEASE_TYPE=snapshot|candidate] release cross - or - $ sbt [-DRELEASE_TYPE=snapshot|candidate] - sbt> release cross - sbt> project typeScript - sbt> releaseNpm - - One downside here is that you'd have to (I think) exit and re-start sbt without the -D when you - want to run a non-snapshot release. This is probably fine because we only run releases from - our main/master branch when changes are merged in normal circumstances. -*/ - -lazy val checkReleaseType: ReleaseStep = ReleaseStep({ st: State => - val releaseType = sys.props.get("RELEASE_TYPE").map { - case v if v == betaReleaseType => betaReleaseType.toUpperCase - case v if v == snapshotReleaseType => snapshotReleaseType.toUpperCase - }.getOrElse("PRODUCTION") - - SimpleReader.readLine(s"This will be a $releaseType release. Continue? [y/N]: ") match { - case Some(v) if Seq("Y", "YES").contains(v.toUpperCase) => // we don't care about the value - it's a flow control mechanism - case _ => sys.error(s"Release aborted by user!") - } - // we haven't changed state, just pass it on if we haven't thrown an error from above - st -}) - -lazy val releaseProcessSteps: Seq[ReleaseStep] = { - val commonSteps:Seq[ReleaseStep] = Seq( - checkSnapshotDependencies, - inquireVersions, - runClean, - runTest, - setReleaseVersion, - ) - - val localExtraSteps:Seq[ReleaseStep] = Seq( - commitReleaseVersion, - tagRelease, - publishArtifacts, - setNextVersion, - commitNextVersion - ) - - val snapshotSteps:Seq[ReleaseStep] = Seq( - publishArtifacts, - releaseStepCommand("sonatypeReleaseAll") - ) - - val prodSteps:Seq[ReleaseStep] = Seq( - releaseStepCommandAndRemaining("+publishSigned"), - releaseStepCommand("sonatypeBundleRelease") - ) - - val localPostRelease:Seq[ReleaseStep] = Seq( - pushChanges, - ) - - (sys.props.get("RELEASE_TYPE"), sys.env.get("CI")) match { - case (Some(v), None) if v == snapshotReleaseType => commonSteps ++ localExtraSteps ++ snapshotSteps ++ localPostRelease - case (_, None) => commonSteps ++ localExtraSteps ++ prodSteps ++ localPostRelease - case (Some(v), _) if v == snapshotReleaseType => commonSteps ++ snapshotSteps - case (_, _)=> commonSteps ++ prodSteps - } -} +) /** * Root project */ lazy val root = Project(id = "root", base = file(".")) - .settings(commonSettings) .aggregate(models, json, scala) .settings( - publishArtifact := false, - releaseProcess := releaseProcessSteps + publish / skip := true, + releaseVersion := ReleaseVersion.fromAggregatedAssessedCompatibilityWithLatestRelease().value, + releaseProcess := Seq[ReleaseStep]( + checkSnapshotDependencies, + inquireVersions, + runClean, + runTest, + setReleaseVersion, + commitReleaseVersion, + tagRelease, + setNextVersion, + commitNextVersion + ) ) /** * Thrift models project */ lazy val models = Project(id = "content-api-models", base = file("models")) - .settings(commonSettings) + .settings(artifactProductionSettings) .disablePlugins(ScroogeSBT) .settings( description := "Scala models for the Guardian's Content API", @@ -195,7 +79,7 @@ lazy val models = Project(id = "content-api-models", base = file("models")) */ lazy val scala = Project(id = "content-api-models-scala", base = file("scala")) .dependsOn(models) - .settings(commonSettings) + .settings(artifactProductionSettings) .settings( description := "Generated classes of the Scala models for the Guardian's Content API", scalacOptions ++= Seq("-deprecation", "-unchecked"), @@ -225,7 +109,7 @@ lazy val scala = Project(id = "content-api-models-scala", base = file("scala")) */ lazy val json = Project(id = "content-api-models-json", base = file("json")) .dependsOn(scala % "provided") - .settings(commonSettings) + .settings(artifactProductionSettings) .settings( description := "Json parser for the Guardian's Content API models", libraryDependencies ++= Seq( @@ -243,7 +127,7 @@ lazy val json = Project(id = "content-api-models-json", base = file("json")) lazy val benchmarks = Project(id = "benchmarks", base = file("benchmarks")) .dependsOn(json, scala) - .settings(commonSettings) + .settings(artifactProductionSettings) .enablePlugins(JmhPlugin) .settings( libraryDependencies += "com.google.guava" % "guava" % "19.0", @@ -251,22 +135,9 @@ lazy val benchmarks = Project(id = "benchmarks", base = file("benchmarks")) publishArtifact := false ) -lazy val npmBetaReleaseTagMaybe = - sys.props.get("RELEASE_TYPE").map { - case v if v == betaReleaseType => - // Why hard-code "beta" instead of using the value of the variable? That's to ensure it's always presented as - // --tag beta to the npm release process provided by the ScroogeTypescriptGen plugin regardless of how we identify - // a beta release here - scroogeTypescriptPublishTag := "beta" - - case v if v == snapshotReleaseType => - scroogeTypescriptPublishTag := "snapshot" - }.toSeq - lazy val typescript = (project in file("ts")) .enablePlugins(ScroogeTypescriptGen) - .settings(commonSettings) - .settings(npmBetaReleaseTagMaybe) + .settings(artifactProductionSettings) .settings( name := "content-api-models-typescript", scroogeTypescriptNpmPackageName := "@guardian/content-api-models", diff --git a/project/plugins.sbt b/project/plugins.sbt index c7913800..3ba8af9c 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,5 +1,5 @@ addSbtPlugin("com.github.sbt" % "sbt-release" % "1.1.0") -addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2") +addSbtPlugin("ch.epfl.scala" % "sbt-version-policy" % "3.2.0") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.10") addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.3.5") diff --git a/version.sbt b/version.sbt new file mode 100644 index 00000000..57e42cd8 --- /dev/null +++ b/version.sbt @@ -0,0 +1 @@ +ThisBuild / version := "18.0.1-SNAPSHOT"