#!/bin/bash -e
# Script Overview
# - determine scala version
# - determine module versions
# - build minimal core (aka locker) of Scala, use the determined version number, publish to scala-integration
# - build those modules where a binary compatible version doesn't exist, publish to scala-integration
# - build Scala using the previously built core and bootstrap modules, publish to scala-integration
# - for releases
# - stage Scala on sonatype
# - rebuild modules that needed a rebuild with this Scala build, and stage them on sonatype
# - the Scala version is serialized to jenkins.properties, which is passed downstream to scala-release jobs
# Specifying the Scala version:
# - To build a release (this enables publishing to sonatype):
# - Either specify SCALA_VER_BASE. You may also specify SCALA_VER_SUFFIX, the Scala version is SCALA_VER=$SCALA_VER_BASE$SCALA_VER_SUFFIX.
# - Or have the current HEAD tagged as v$base$suffix
# - To prevent staging on sonatype (for testing), set publishToSonatype to anything but "yes"
# - Note: After building a release, the jenkins job provides an updated versions.properties file as artifact.
# Put this file in the Scala repo and create a pull request, also update `baseVersion in Global` in build.sbt.
#
# - Otherwise, an integration build is performed:
# - version number is read from the build.sbt, extended with -[bin|pre]-$sha
# Specifying module versions. We use release versions for modules.
# - Module versions are read from the versions.properties file.
# - Set <MODULE>_VER to override the default, e.g. XML_VER="1.0.4".
# - The git revision is set to <MODULE>_REF="v$<MODULE>_VER". Make sure the tag exists (you can't override <MODULE>_REF).
# Modules are automatically built if necessary.
# - A module is built if it doesn't exist in the maven repository. Note that the lookup uses two versions:
# - The version of the module (see below how it's determined)
# - The binary version of of the SCALA_VER release that is being built
# - sbt computes the binary version when looking up / building modules (*). Examples:
# - 2.12.0-M1, 2.12.0-RC3: the full version is used
# - 2.12.0, 2.12.1-M1, 2.12.1-RC3, 2.12.1: the binary version 2.12 is used
#
# - Example: assume that `scala-xml_2.11 % 1.0.3` and `scala-xml_2.12.0-M1 % 1.0.3` both exists
# - XML_VER=1.0.3 and SCALA_VER=2.11.7 => no rebuild (binary version remains 2.11)
# - XML_VER=1.0.3 and SCALA_VER=2.12.0-M2 => rebuild (new binary version 2.12.0-M2)
# - XML_VER=1.0.4 and SCALA_VER=2.11.7 => rebuild (new version for the module, not yet on maven)
# NOTE: this is not the recommended way of publishing a module. Instead, prefer to release `scala-xml_2.11 % 1.0.4`
# using the existing scala 2.11.6 compiler before releasing 2.11.7. Sometimes it's necessary though. One
# example was 2.11.1, which contained a fix in the backend (SerialVersionUID was ignored). All modules needed
# to be re-built using the 2.11.1 release, we could not use 2.11.0. We could also not release the modules
# after 2.11.1 was out, because that way the scala-library-all pom of 2.11.1 would depend on the old modules.
#
# (*) https://github.com/sbt/sbt/blob/v0.13.13/util/cross/src/main/input_sources/CrossVersionUtil.scala#L41
# Binary incompatible changes in Modules: example with Scala 2.11 / 2.12 and scala-parser-combinators
# - The 1.0.x branch on scala-parser-combinators remains binary compatible with 1.0.0
# - Scala 2.11 will always use 1.0.x releases: we ship scala-parser-combinators with the distribution,
# so we cannot introduce incompatible changes in a minor release.
# - The master branch of scala-parser-combinators contains binary incompatible changes, versioned 1.1.x
# - Scala 2.12 will use 1.1.x releases
# - No changes to the build script required: just put the 1.1.x version number into versions.properties
#
# Note: It's still OK for a module to release a binary incompatible version to maven, for example
# scala-parser-combinators_2.11 % 1.1.0. Users can depend on this in their sbt build. But for the
# distribution (tar/zip archives, scala-library-all) we have to stay on the binary compatible version.
# Requirements
# - SBT_CMD must point to sbt from sbt-extras
# - ~/.sonatype-curl, ~/.m2/settings.xml, ~/.credentials, ~/.credentials-sonatype, ~/.credentials-private-repo
# as defined by https://github.com/scala/scala-jenkins-infra/tree/master/templates/default
# - ~/.sbt/0.13/plugins/gpg.sbt with:
# addSbtPlugin("com.typesafe.sbt" % "sbt-pgp" % "0.8.1")
# Note: private-repo used to be private-repo.typesafe.com. now we're running artifactory on scala-ci.typesafe.com/artifactory
publishPrivateTask=${publishPrivateTask-"publish"}
publishSonatypeTaskCore=${publishSonatypeTaskCore-"publishSigned"}
publishSonatypeTaskModules=${publishSonatypeTaskModules-"publishSigned"}
forceRebuild=${forceRebuild-no}
sbtBuildTask=${sbtBuildTask-"testAll"} # TESTING leave empty to avoid the sanity check
testStability=${testStability-yes}
clean="clean" # TESTING leave empty to speed up testing
baseDir=${WORKSPACE-`pwd`}
scriptsDir="$baseDir/scripts"
. $scriptsDir/common
# we must change ivy home to get a fresh ivy cache, otherwise we get half-bootstrapped scala
# rm it in case it existed (and there's no ivy2-shadow, which indicates we're running in a TESTING environment)...
# we don't nuke the whole ws since that clobbers the git clones needlessly
[[ -d $baseDir/ivy2-shadow ]] || rm -rf $baseDir/ivy2
mkdir -p $baseDir/ivy2
rm -rf $baseDir/resolutionScratch_
mkdir -p $baseDir/resolutionScratch_
# repo to publish builds
integrationRepoUrl=${integrationRepoUrl-"https://scala-ci.typesafe.com/artifactory/scala-integration/"}
generateRepositoriesConfig $integrationRepoUrl
# ARGH trying to get this to work on multiple versions of sbt-extras...
# the old version (on jenkins, and I don't want to upgrade for risk of breaking other builds) honors -sbt-dir
# the new version of sbt-extras ignores sbt-dir, so we pass it in as -Dsbt.global.base
# need to set sbt-dir to one that has the gpg.sbt plugin config
sbtArgs="-ivy $baseDir/ivy2 -Dsbt.override.build.repos=true -Dsbt.repository.config=$sbtRepositoryConfig -Dsbt.global.base=$HOME/.sbt/0.13 -sbt-dir $HOME/.sbt/0.13"
##### git
gfxd() {
git clean -fxd # TESTING
}
update() {
[[ -d $baseDir ]] || mkdir -p $baseDir
cd $baseDir
if [ ! -d $baseDir/$2 ]; then git clone "https://github.com/$1/$2.git"; fi
cd $2
git fetch --tags "https://github.com/$1/$2.git"
(git fetch "https://github.com/$1/$2.git" $3 && git checkout -fq FETCH_HEAD) #|| git checkout -fq $3 # || fallback is for local testing on tag
git reset --hard
}
##### sonatype interface
stApi="https://oss.sonatype.org/service/local"
function st_curl(){
curl -H "Content-Type: application/json" -H "accept: application/json,application/vnd.siesta-error-v1+json,application/vnd.siesta-validation-errors-v1+json" -K ~/.sonatype-curl -s -o - $@
}
function st_stagingReposOpen() {
st_curl "$stApi/staging/profile_repositories" | jq '.data[] | select(.profileName == "org.scala-lang") | select(.type == "open")'
}
function st_stagingRepoDrop() {
repo=$1
message=$2
echo "{\"data\":{\"description\":\"$message\",\"stagedRepositoryIds\":[\"$repo\"]}}" | st_curl -X POST -d @- "$stApi/staging/bulk/drop"
}
function st_stagingRepoClose() {
repo=$1
message=$2
echo "{\"data\":{\"description\":\"$message\",\"stagedRepositoryIds\":[\"$repo\"]}}" | st_curl -X POST -d @- "$stApi/staging/bulk/close"
}
#### sbt tools
sbtBuild() {
echo "### sbtBuild: "$SBT_CMD -no-colors $sbtArgs "${scalaVersionTasks[@]}" "${publishTasks[@]}" "$@"
$SBT_CMD -no-colors $sbtArgs "${scalaVersionTasks[@]}" "${publishTasks[@]}" "$@" >> $baseDir/logs/builds 2>&1
}
sbtResolve() {
cd $baseDir/resolutionScratch_
touch build.sbt
# Can be set to `full` if a module requires cross-versioning against the full Scala version, like the continuations plugin used to.
cross=${4-binary}
echo "### sbtResolve: $SBT_CMD -no-colors $sbtArgs " "${scalaVersionTasks[@]}" "\"$1\" % \"$2\" % \"$3\" cross CrossVersion.$cross"
$SBT_CMD -no-colors $sbtArgs "${scalaVersionTasks[@]}" \
"set libraryDependencies := Seq(\"$1\" % \"$2\" % \"$3\" cross CrossVersion.$cross)" \
'show update' >> $baseDir/logs/resolution 2>&1
}
# Oh boy... can't use scaladoc to document scala-xml if scaladoc depends on the same version of scala-xml.
# Even if that version is available through the project's resolvers, sbt won't look past this project.
# SOOOOO, we set the version to a dummy (-DOC), generate documentation,
# then set the version to the right one and publish (which won't re-gen the docs).
# Also tried publish-local without docs using 'set publishArtifact in (Compile, packageDoc) := false' and republishing, no dice.
# Each buildModule() function is invoked twice: first to build against locker and publish to artifactory, then
# to build against the release and publish to sonatype (or publish-local if publishToSonatype is not "yes").
# In the second round, sbtResolve is always true: the module will be found in the artifactory!
# Therefore, if MODULE_BUILT is "yes" (in the second round), we know that we need to build (and publish) the
# module again.
#
# Note: we tried an alternative solution in which sbtResolve would not look at artifactory, but that fails. For example,
# scala-xml depends on scala-library, so sbt tries to find the scala-library of the version that we are currently building,
# which exists only in artifactory.
docTask() {
if [[ "$STARR_REF" != "" && "$1" != "yes" ]]; then
# Don't build module docs on the first round of module builds when bootstrapping
# a binary incompatible compiler change to avoid linkage errors with using the old Scaladoc
echo set publishArtifact in packageDoc in Compile := false
else
echo doc
fi
}
buildXML() {
if [ "$XML_BUILT" != "yes" ] && [ "$forceRebuild" != "yes" ] && ( sbtResolve "org.scala-lang.modules" "scala-xml" $XML_VER )
then echo "Found scala-xml $XML_VER; not building."
else
update scala scala-xml "$XML_REF" && gfxd
doc="$(docTask $XML_BUILT)"
sbtBuild 'set version := "'$XML_VER'-DOC"' $clean "$doc" 'set version := "'$XML_VER'"' test "${buildTasks[@]}"
XML_BUILT="yes" # ensure the module is built and published when buildXML is invoked for the second time, see comment above
fi
}
buildPartest() {
if [ "$PARTEST_BUILT" != "yes" ] && [ "$forceRebuild" != "yes" ] && ( sbtResolve "org.scala-lang.modules" "scala-partest" $PARTEST_VER )
then echo "Found scala-partest $PARTEST_VER; not building."
else
update scala scala-partest "$PARTEST_REF" && gfxd
doc="$(docTask $PARTEST_BUILT)"
sbtBuild 'set version :="'$PARTEST_VER'"' 'set VersionKeys.scalaXmlVersion := "'$XML_VER'"' $clean "$doc" test "${buildTasks[@]}"
PARTEST_BUILT="yes"
fi
}
# should only be called with publishTasks publishing to artifactory
buildScalaCheck(){
if [ "$SCALACHECK_BUILT" != "yes" ] && [ "$forceRebuild" != "yes" ] && ( sbtResolve "org.scalacheck" "scalacheck" $SCALACHECK_VER )
then echo "Found scalacheck $SCALACHECK_VER; not building."
else
update rickynils scalacheck $SCALACHECK_REF && gfxd
doc="$(docTask $SCALACHECK_BUILT)"
sbtBuild 'set version := "'$SCALACHECK_VER'"' 'set VersionKeys.scalaParserCombinatorsVersion := "'$PARSERS_VER'"' $clean "$doc" publish # test times out NOTE: never published to sonatype
SCALACHECK_BUILT="yes"
fi
}
# build modules, using ${buildTasks[@]} (except for ScalaCheck, which is hard-coded to publish to artifactory)
buildModules() {
publishTasks=('set credentials += Credentials(Path.userHome / ".credentials-private-repo")' "set every publishTo := Some(\"publish-repo\" at \"$integrationRepoUrl\")")
buildTasks=($publishPrivateTask)
buildXML
# buildScalaCheck
buildPartest
}
buildPublishedModules() {
publishTasks=('set credentials += Credentials(Path.userHome / ".credentials-sonatype")' "set pgpPassphrase := Some(Array.empty)")
buildTasks=($publishSonatypeTaskModules)
buildXML
buildPartest
}
## BUILD STEPS:
scalaVerToBinary() {
# $1 = SCALA_VER
# $2 = SCALA_VER_BASE
# $3 = SCALA_VER_SUFFIX
local RE='\([0-9]*\)[.]\([0-9]*\)[.]\([0-9]*\)'
local majMin="$(echo $2 | sed -e "s#$RE#\1.\2#")"
local patch="$(echo $2 | sed -e "s#$RE#\3#")"
# The binary version is majMin (e.g. "2.12") if
# - there's no suffix : 2.12.0, 2.12.1
# - the suffix starts with "-bin" : 2.12.1-bin-sha, 2.12.1-bin-sha-custom, 2.12.1-bin-SNAPSHOT
# - the suffix is \w+ and patch version is > 0: 2.12.1-M1, 2.12.1-RC2 (also 2.12.1-sha, 2.12.1-SNAPSHOT, which we don't use)
#
# Otherwise, the binary version is the full version: 2.12.0-M1, 2.12.0-RC2, 2.12.0-pre-sha, 2.12.0-pre-SNAPSHOT
# (also 2.12.0-sha, 2.12.0-SNAPSHOT, which we don't use)
#
# Adapted from sbt: https://github.com/sbt/sbt/blob/v0.13.13/util/cross/src/main/input_sources/CrossVersionUtil.scala#L42
#
# During the pre-release cycle of a major release (e.g. before 2.12.0), the SCALA_BINARY_VER of integration / SNAPSHOT
# versions is the full version, e.g. 2.12.0-pre-sha, so modules are always re-built.
if [[ "$3" == "" || "${3:0:4}" == "-bin" || ("$patch" != "0" && "$3" =~ ^-[a-zA-Z0-9_]+$) ]]; then
echo "$majMin"
else
echo "$1"
fi
}
determineScalaVersion() {
cd $WORKSPACE
parseScalaProperties "versions.properties"
# each of the branches below defines the following vars: SCALA_VER_BASE, SCALA_VER_SUFFIX, publishToSonatype
if [ -z "$SCALA_VER_BASE" ]; then
echo "No SCALA_VER_BASE specified."
scalaTag=$(git describe --tag --exact-match ||:)
if [ -z "$scalaTag" ]
then
echo "No tag found, running an integration build."
$SBT_CMD $sbtArgs 'set baseVersionSuffix in Global := "SHA"' generateBuildCharacterPropertiesFile
parseScalaProperties "buildcharacter.properties"
SCALA_VER_BASE="$maven_version_base"
SCALA_VER_SUFFIX="$maven_version_suffix"
# TODO: publish nightly snapshot using this script - currently it's a separate jenkins job still running at EPFL.
publishToSonatype="no"
else
echo "HEAD is tagged as $scalaTag."
# borrowed from https://github.com/cloudflare/semver_bash/blob/master/semver.sh
local RE='v*\([0-9]*\)[.]\([0-9]*\)[.]\([0-9]*\)\([0-9A-Za-z-]*\)' # don't change this to make it more accurate, it's not worth it
SCALA_VER_BASE="$(echo $scalaTag | sed -e "s#$RE#\1.\2.\3#")"
SCALA_VER_SUFFIX="$(echo $scalaTag | sed -e "s#$RE#\4#")"
if [ "$SCALA_VER_BASE" == "$scalaTag" ]; then
echo "Could not parse version $scalaTag"
exit 1
fi
publishToSonatype=${publishToSonatype-"yes"} # unless forced previously, publish
fi
else
publishToSonatype=${publishToSonatype-"yes"} # unless forced previously, publish
fi
SCALA_VER="$SCALA_VER_BASE$SCALA_VER_SUFFIX"
SCALA_BINARY_VER=$(scalaVerToBinary $SCALA_VER $SCALA_VER_BASE $SCALA_VER_SUFFIX)
echo "version=$SCALA_VER" >> $baseDir/jenkins.properties
echo "sbtDistVersionOverride=-Dproject.version=$SCALA_VER" >> $baseDir/jenkins.properties
scalaVersionTasks=('set every scalaVersion := "'$SCALA_VER'"')
echo "Building Scala $SCALA_VER."
}
# determineScalaVersion must have been called (versions.properties is parsed to env vars)
deriveModuleVersions() {
XML_VER=${XML_VER-$scala_xml_version_number}
PARTEST_VER=${PARTEST_VER-$partest_version_number}
SCALACHECK_VER=${SCALACHECK_VER-$scalacheck_version_number}
XML_REF="v$XML_VER"
PARTEST_REF="v$PARTEST_VER"
SCALACHECK_REF="$SCALACHECK_VER" # no `v` in their tags
echo "PARTEST = $PARTEST_VER at $PARTEST_REF"
# echo "SCALACHECK = $SCALACHECK_VER at $SCALACHECK_REF"
echo "XML = $XML_VER at $XML_REF"
}
createNetrcFile() {
local netrcFile=$HOME/`basename $1`-netrc
grep 'host=' $1 | sed 's/host=\(.*\)/machine \1/' > $netrcFile
grep 'user=' $1 | sed 's/user=\(.*\)/login \1/' >> $netrcFile
grep 'password=' $1 | sed 's/password=\(.*\)/password \1/' >> $netrcFile
}
# deletes existing artifacts (core and modules) matching the $SCALA_VER from the repository passed as argument
removeExistingBuilds() {
local repoUrl=$1
local repoPrefix="https://scala-ci.typesafe.com/artifactory/"
if [[ $repoUrl == "$repoPrefix"* ]]; then
local repoId=${1#$repoPrefix}
local storageApiUrl="${repoPrefix}api/storage/$repoId"
createNetrcFile "$HOME/.credentials-private-repo"
local netrcFile="$HOME/.credentials-private-repo-netrc"
# "module" is not a scala module (like scala-xml), but an artifact of a boostrap build. the variable
# contains: "org/scala-lang/modules", "org/scala-lang/scala-compiler", "org/scala-lang/scala-library", ...
local scalaLangModules=`curl -s $storageApiUrl/org/scala-lang | jq -r '.children | .[] | "org/scala-lang" + .uri' | grep -v actors-migration`
for module in $scalaLangModules; do
local artifacts=`curl -s $storageApiUrl/$module | jq -r ".children | .[] | select(.uri | endswith(\"$SCALA_VER\")) | .uri"`
for artifact in $artifacts; do
echo "Deleting $repoUrl$module$artifact"
curl -s --netrc-file $netrcFile -X DELETE $repoUrl$module$artifact
done
done
else
echo "Unknown repo, not deleting anything: $repoUrl"
fi
}
constructUpdatedModuleVersions() {
updatedModuleVersions=()
# force the new module versions for building the core. these may be different from the values in versions.properties
# if the variables (XML_VER) were provided. in the common case, the values are the same as in versions.properties.
updatedModuleVersions=("${updatedModuleVersions[@]}" "-Dscala-xml.version.number=$XML_VER")
updatedModuleVersions=("${updatedModuleVersions[@]}" "-Dpartest.version.number=$PARTEST_VER")
# updatedModuleVersions=("${updatedModuleVersions[@]}" "-Dscalacheck.version.number=$SCALACHECK_VER")
# allow overriding the jline version using a jenkins build parameter
if [ ! -z "$JLINE_VER" ] ; then updatedModuleVersions=("${updatedModuleVersions[@]}" "-Djline.version=$JLINE_VER"); fi
if [ ! -z "$SCALA_BINARY_VER" ]; then updatedModuleVersions=("${updatedModuleVersions[@]}" "-Dscala.binary.version=$SCALA_BINARY_VER"); fi
}
# build locker (scala + modules) and quick, publishing everything to artifactory
bootstrap() {
echo "### Bootstrapping"
cd $WORKSPACE
#### (Optional) STARR.
if [ ! -z "$STARR_REF" ]; then
echo "### Building STARR"
STARR_DIR=./scala-starr
STARR_VER_SUFFIX="-$(git rev-parse --short $STARR_REF)-starr"
STARR_VER=$SCALA_VER_BASE$STARR_VER_SUFFIX
rm -rf "$STARR_DIR"
(
git clone --reference $WORKSPACE/.git $WORKSPACE/.git $STARR_DIR
cd $STARR_DIR
git co $STARR_REF
$SBT_CMD -no-colors $sbtArgs --warn "setupBootstrapStarr $integrationRepoUrl $STARR_VER" $clean publish >> $baseDir/logs/builds 2>&1
)
fi
#### LOCKER
echo "### Building locker"
# for bootstrapping, publish core (or at least smallest subset we can get away with)
# so that we can build modules with this version of Scala and publish them locally
# must publish under $SCALA_VER so that the modules will depend on this (binary) version of Scala
# publish more than just core: partest needs scalap
# in sabbus lingo, the resulting Scala build will be used as starr to build the released Scala compiler
if [ ! -z "$STARR_VER" ]; then SET_STARR=-Dstarr.version=$STARR_VER; fi
$SBT_CMD -no-colors $sbtArgs $SET_STARR --warn "setupBootstrapLocker $integrationRepoUrl $SCALA_VER" $clean publish >> $baseDir/logs/builds 2>&1
echo "### Building modules using locker"
# build, test and publish modules with this core
# publish to our internal repo (so we can resolve the modules in the scala build below)
# we only need to build the modules necessary to build Scala itself
# since the version of locker and quick are the same
buildModules
constructUpdatedModuleVersions
#### QUICK
echo "### Bootstrapping Scala using locker"
# # TODO: close all open staging repos so that we can be reasonably sure the only open one we see after publishing below is ours
# # the sbt call will create a new one
#
# Rebuild Scala with these modules so that all binary versions are consistent.
# Update versions.properties to new modules.
# Sanity check: make sure the Scala test suite passes / docs can be generated with these modules.
cd $baseDir
rm -rf build/
$SBT_CMD $sbtArgs \
--warn \
-Dstarr.version=$SCALA_VER \
${updatedModuleVersions[@]} \
"setupBootstrapQuick $integrationRepoUrl $SCALA_VER" \
$clean \
$sbtBuildTask \
dist/mkQuick \
publish
# clear ivy cache (and to be sure, local as well), so the next round of sbt builds sees the fresh scala
rm -rf $baseDir/ivy2
# TODO: create PR with following commit (note that release will have been tagged already)
# git commit versions.properties -m"Bump versions.properties for $SCALA_VER."
}
testStability() {
echo "### Testing stability"
cd $baseDir
# Run stability tests using the just built version as "quick" and a new version as "strap"
mv build/quick quick1
rm -rf build/
$SBT_CMD $sbtArgs \
--warn \
-Dstarr.version=$SCALA_VER \
${updatedModuleVersions[@]} \
"setupBootstrapQuick $integrationRepoUrl $SCALA_VER" \
$clean \
dist/mkQuick
mv build/quick build/strap
mv quick1 build/quick
$scriptsDir/stability-test.sh
}
# assumes we just bootstrapped, and current directory is $baseDir
# publishes locker to sonatype, then builds modules again (those for which version numbers were provided),
# and publishes those to sonatype as well
# finally, the staging repos are closed
publishSonatype() {
# stage to sonatype, along with all modules -Dmaven.version.suffix/-Dbuild.release not necessary,
# since we're just publishing an existing build
echo "### Publishing core to sonatype"
$SBT_CMD $sbtArgs \
--warn \
-Dstarr.version=$SCALA_VER \
${updatedModuleVersions[@]} \
"setupBootstrapPublish $integrationRepoUrl $SCALA_VER" \
$publishSonatypeTaskCore
echo "### Publishing modules to sonatype"
# build/test/publish scala core modules to sonatype (this will start a new staging repo)
# (was hoping we could make everything go to the same staging repo, but it's not timing that causes two staging repos to be opened)
# NOTE: only publish those for which versions are set
# test and publish to sonatype, assuming you have ~/.sbt/0.13/sonatype.sbt and ~/.sbt/0.13/plugin/gpg.sbt
buildPublishedModules
open=$(st_stagingReposOpen)
allOpenUrls=$(echo $open | jq '.repositoryURI' | tr -d \")
allOpen=$(echo $open | jq '.repositoryId' | tr -d \")
echo "Closing open repos: $allOpen"
for repo in $allOpen; do st_stagingRepoClose $repo; done
echo "Closed sonatype staging repos: $allOpenUrls."
}
#### MAIN
determineScalaVersion
deriveModuleVersions
removeExistingBuilds $integrationRepoUrl
bootstrap
if [ "$testStability" == "yes" ]
then testStability
fi
if [ "$publishToSonatype" == "yes" ]
then publishSonatype
fi