Skip to content

Commit

Permalink
Adopt GHA-Scala-Library-Release-Workflow (Scala Maven release only)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
rtyley committed Jan 24, 2024
1 parent 09fbc2f commit b188d73
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 249 deletions.
93 changes: 7 additions & 86 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -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 }}
15 changes: 4 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down
173 changes: 22 additions & 151 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -23,163 +23,47 @@ val betaReleaseSuffix = "-beta.0"
val snapshotReleaseType = "snapshot"
val snapshotReleaseSuffix = "-SNAPSHOT"

lazy val mavenSettings = Seq(
pomExtra := (
<url>https://github.com/guardian/content-api-models</url>
<scm>
<connection>scm:git:git@github.com:guardian/content-api-models.git</connection>
<developerConnection>scm:git:git@github.com:guardian/content-api-models.git</developerConnection>
<url>git@github.com:guardian/content-api-models.git</url>
</scm>
<developers>
<developer>
<id>cb372</id>
<name>Chris Birchall</name>
<url>https://github.com/cb372</url>
</developer>
<developer>
<id>mchv</id>
<name>Mariot Chauvin</name>
<url>https://github.com/mchv</url>
</developer>
<developer>
<id>LATaylor-guardian</id>
<name>Luke Taylor</name>
<url>https://github.com/LATaylor-guardian</url>
</developer>
<developer>
<id>regiskuckaertz</id>
<name>Regis Kuckaertz</name>
<url>https://github.com/regiskuckaertz</url>
</developer>
<developer>
<id>annebyrne</id>
<name>Anne Byrne</name>
<url>https://github.com/annebyrne</url>
</developer>
<developer>
<id>justinpinner</id>
<name>Justin Pinner</name>
<url>https://github.com/justinpinner</url>
</developer>
</developers>
),
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 <version>
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",
Expand All @@ -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"),
Expand Down Expand Up @@ -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(
Expand All @@ -243,30 +127,17 @@ 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",
Jmh / javaOptions ++= Seq("-server", "-Xms4G", "-Xmx4G", "-XX:+UseG1GC", "-XX:-UseBiasedLocking"),
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",
Expand Down
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -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")

Expand Down
1 change: 1 addition & 0 deletions version.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ThisBuild / version := "18.0.1-SNAPSHOT"

0 comments on commit b188d73

Please sign in to comment.