diff --git a/ci-build.sh b/ci-build.sh index e1093eb..8d68269 100755 --- a/ci-build.sh +++ b/ci-build.sh @@ -58,6 +58,66 @@ mavenEvaluate() { mvn -B -U -q -Denforcer.skip=true -Dexec.executable=echo -Dexec.args="$1" --non-recursive validate exec:exec 2>&1 } +# Output debugging info, for troubleshooting builds. +dump() { + if env | grep -q ^$1= + then + # Environment variable is set. + line=$(env | grep ^$1=) + if [ "$line" ] + then + # And it's non-empty. + if [ "$2" ] + then + # Secret value: emit only that it is present. + echo "$1=" + else + # Not a secret: emit in plaintext. + echo "$line" + fi + else + # But it is empty! + echo "$1=" + fi + else + # Environment variable is not set. + echo "$1=" + fi +} + +analyzeGitHubWorkflow() { + test -d .github/workflows || return + echo + echo '/--------------------------------------------------\' + echo '| ci-build.sh analysis: env vars + GitHub workflow |' + echo '\--------------------------------------------------/' + dump dir + dump platform + dump BUILD_REPOSITORY + dump NO_DEPLOY + echo '----------------------------------------------------' + dump GPG_KEY_NAME secret + dump GPG_PASSPHRASE secret + dump MAVEN_USER secret + dump MAVEN_PASS secret + dump CENTRAL_USER secret + dump CENTRAL_PASS secret + dump SIGNING_ASC secret + echo '----------------------------------------------------' + for var in \ + GPG_KEY_NAME \ + GPG_PASSPHRASE \ + MAVEN_USER \ + MAVEN_PASS \ + CENTRAL_USER \ + CENTRAL_PASS \ + SIGNING_ASC + do + grep -q "secrets.$var" .github/workflows/*.yml || + echo "Add \`$var: \${{ secrets.$var }}\` to GitHub workflow env section!" + done +} + # Build Maven projects. if [ -f pom.xml ]; then echo ::group::"= Maven build =" @@ -65,7 +125,7 @@ if [ -f pom.xml ]; then # --== MAVEN SETUP ==-- echo - echo "== Configuring Maven ==" + echo '== Configuring Maven ==' # NB: Suppress "Downloading/Downloaded" messages. # See: https://stackoverflow.com/a/35653426/1207769 @@ -75,17 +135,22 @@ if [ -f pom.xml ]; then mkdir -p "$HOME/.m2" settingsFile="$HOME/.m2/settings.xml" customSettings=.ci/settings.xml - if [ -z "$MAVEN_PASS" -a -z "$OSSRH_PASS" ]; then - echo "[WARNING] Skipping settings.xml generation (no deployment credentials)." - elif [ -f "$customSettings" ]; then + if [ "$OSSRH_USER" -o "$OSSRH_PASS" ]; then + echo '[WARNING] Obsolete OSSRH vars detected. Secrets may need updating to deploy to Maven Central.' + fi + if [ -f "$customSettings" ]; then cp "$customSettings" "$settingsFile" + elif [ -z "$BUILD_REPOSITORY" ]; then + echo 'Skipping settings.xml generation (no BUILD_REPOSITORY; assuming we are running locally)' else - if [ -z "$OSSRH_USER" ]; then - OSSRH_USER=scijava-ci - fi + # settings.xml header cat >"$settingsFile" < +EOL + # settings.xml scijava servers + if [ "$MAVEN_USER" -a "$MAVEN_PASS" ]; then + cat >>"$settingsFile" < scijava.releases $MAVEN_USER @@ -96,12 +161,28 @@ if [ -f pom.xml ]; then $MAVEN_USER $(escapeXML "$MAVEN_PASS") +EOL + else + echo '[WARNING] Skipping settings.xml scijava servers (no MAVEN deployment credentials).' + fi + # settings.xml central server + if [ "$CENTRAL_USER" -a "$CENTRAL_PASS" ]; then + cat >>"$settingsFile" < - sonatype-nexus-releases - $OSSRH_USER - $(escapeXML "$OSSRH_PASS") + central + $CENTRAL_USER + $(escapeXML "$CENTRAL_PASS") +EOL + else + echo '[WARNING] Skipping settings.xml central server (no CENTRAL deployment credentials).' + fi + cat >>"$settingsFile" < +EOL + # settings.xml GPG profile + if [ "$GPG_KEY_NAME" -a "$GPG_PASSPHRASE" ]; then + cat >>"$settingsFile" < gpg @@ -116,6 +197,12 @@ if [ -f pom.xml ]; then +EOL + else + echo '[WARNING] Skipping settings.xml gpg profile (no GPG credentials).' + fi + # settings.xml footer + cat >>"$settingsFile" < EOL fi @@ -123,64 +210,103 @@ EOL # --== DEPLOYMENT CHECKS ==-- # Determine whether deploying is both possible and warranted. - echo "Performing deployment checks" + echo 'Performing deployment checks' deployOK= scmURL=$(mavenEvaluate '${project.scm.url}') result=$? checkSuccess $result if [ $result -ne 0 ]; then - echo "No deploy -- could not extract ciManagement URL" - echo "Output of failed attempt follows:" + echo 'No deploy -- could not extract ciManagement URL' + echo 'Output of failed attempt follows:' echo "$scmURL" else scmURL=${scmURL%.git} scmURL=${scmURL%/} if [ "$NO_DEPLOY" ]; then - echo "No deploy -- the NO_DEPLOY flag is set" - elif [ ! "$SIGNING_ASC" -o ! "$GPG_KEY_NAME" -o ! "$GPG_PASSPHRASE" -o ! "$MAVEN_PASS" -o ! "$OSSRH_PASS" ]; then - echo "No deploy -- secure environment variables not available" + echo 'No deploy -- the NO_DEPLOY flag is set' elif [ "$BUILD_REPOSITORY" -a "$BUILD_REPOSITORY" != "$scmURL" ]; then echo "No deploy -- repository fork: $BUILD_REPOSITORY != $scmURL" + elif [ "$BUILD_BASE_REF" -o "$BUILD_HEAD_REF" ]; then + echo "No deploy -- proposed change: $BUILD_HEAD_REF -> $BUILD_BASE_REF" else # Are we building a snapshot version, or a release version? version=$(mavenEvaluate '${project.version}') result=$? checkSuccess $result if [ $result -ne 0 ]; then - echo "No deploy -- could not extract version string" - echo "Output of failed attempt follows:" + echo 'No deploy -- could not extract version string' + echo 'Output of failed attempt follows:' echo "$version" else case "$version" in *-SNAPSHOT) # Snapshot version -- ensure release.properties not present. if [ -f release.properties ]; then - echo "[ERROR] Spurious release.properties file is present" - echo "Remove the file from version control and try again." + echo '[ERROR] Spurious release.properties file is present' + echo 'Remove the file from version control and try again.' exit 1 fi + + # Check for SciJava Maven repository credentials. + if [ "$MAVEN_USER" -a "$MAVEN_PASS" ]; then + deployOK=1 + else + echo 'No deploy -- MAVEN environment variables not available' + analyzeGitHubWorkflow + fi ;; *) # Release version -- ensure release.properties is present. if [ ! -f release.properties ]; then - echo "[ERROR] Release version, but release.properties not found" - echo "You must use release-version.sh to release -- see https://imagej.net/develop/releasing" + echo '[ERROR] Release version, but release.properties not found' + echo 'You must use release-version.sh to release -- see https://imagej.net/develop/releasing' exit 1 fi + + # To which repository are we releasing? + releaseProfiles=$(mavenEvaluate '${releaseProfiles}') + result=$? + checkSuccess $result + if [ $result -ne 0 ]; then + echo 'No deploy -- could not extract releaseProfiles string' + echo 'Output of failed attempt follows:' + echo "$releaseProfiles" + fi + case "$releaseProfiles" in + *deploy-to-scijava*) + # Check for SciJava Maven repository credentials. + if [ "$MAVEN_USER" -a "$MAVEN_PASS" ]; then + deployOK=1 + else + echo '[ERROR] Cannot deploy: MAVEN environment variables not available' + analyzeGitHubWorkflow + exit 1 + fi + ;; + *sonatype-oss-release*) + # Check for Central Portal deployment credentials. + # Deploy to Central requires GPG-signed artifacts. + if [ "$CENTRAL_USER" -a "$CENTRAL_PASS" -a "$SIGNING_ASC" -a "$GPG_KEY_NAME" -a "$GPG_PASSPHRASE" ]; then + deployOK=1 + else + echo '[ERROR] Cannot deploy: CENTRAL environment variables not available' + analyzeGitHubWorkflow + exit 1 + fi + ;; + *) + echo 'Unknown deploy target -- attempting to deploy anyway' + deployOK=1 + ;; + esac ;; esac - if [ "$BUILD_BASE_REF" -o "$BUILD_HEAD_REF" ] - then - echo "No deploy -- proposed change: $BUILD_HEAD_REF -> $BUILD_BASE_REF" - else - deployOK=1 - fi fi fi fi if [ "$deployOK" ]; then - echo "All checks passed for artifact deployment" + echo 'All checks passed for artifact deployment' fi # --== Maven build arguments ==-- @@ -189,48 +315,52 @@ EOL # --== GPG SETUP ==-- - # Install GPG on macOS - if [ "$platform" = Darwin ]; then - HOMEBREW_NO_AUTO_UPDATE=1 brew install gnupg2 - fi - - # Avoid "signing failed: Inappropriate ioctl for device" error. - export GPG_TTY=$(tty) - - # Import the GPG signing key. - keyFile=.ci/signingkey.asc - if [ "$deployOK" ]; then - echo "== Importing GPG keypair ==" - mkdir -p .ci - echo "$SIGNING_ASC" > "$keyFile" - ls -la "$keyFile" - gpg --version - gpg --batch --fast-import "$keyFile" - checkSuccess $? - fi + if [ "$GPG_KEY_NAME" -a "$GPG_PASSPHRASE" ]; then + # Install GPG on macOS + if [ "$platform" = Darwin ]; then + HOMEBREW_NO_AUTO_UPDATE=1 brew install gnupg2 + fi - # HACK: Use maven-gpg-plugin 3.0.1+. Avoids "signing failed: No such file or directory" error. - maven_gpg_plugin_version=$(mavenEvaluate '${maven-gpg-plugin.version}') - case "$maven_gpg_plugin_version" in - 0.*|1.*|2.*|3.0.0) - echo "--> Forcing maven-gpg-plugin version from $maven_gpg_plugin_version to 3.0.1" - BUILD_ARGS="$BUILD_ARGS -Dmaven-gpg-plugin.version=3.0.1 -Darguments=-Dmaven-gpg-plugin.version=3.0.1" - ;; - *) - echo "--> maven-gpg-plugin version OK: $maven_gpg_plugin_version" - ;; - esac - - # HACK: Install pinentry helper program if missing. Avoids "signing failed: No pinentry" error. - if ! which pinentry >/dev/null 2>&1; then - echo '--> Installing missing pinentry helper for GPG' - sudo apt-get install -y pinentry-tty - # HACK: Restart the gpg agent, to notice the newly installed pinentry. - if { pgrep gpg-agent >/dev/null && which gpgconf >/dev/null 2>&1; } then - echo '--> Restarting gpg-agent' - gpgconf --reload gpg-agent + # Avoid "signing failed: Inappropriate ioctl for device" error. + export GPG_TTY=$(tty) + + # Import the GPG signing key. + keyFile=.ci/signingkey.asc + if [ "$deployOK" ]; then + echo '== Importing GPG keypair ==' + mkdir -p .ci + echo "$SIGNING_ASC" > "$keyFile" + ls -la "$keyFile" + gpg --version + gpg --batch --fast-import "$keyFile" checkSuccess $? fi + + # HACK: Use maven-gpg-plugin 3.0.1+. Avoids "signing failed: No such file or directory" error. + maven_gpg_plugin_version=$(mavenEvaluate '${maven-gpg-plugin.version}') + case "$maven_gpg_plugin_version" in + 0.*|1.*|2.*|3.0.0) + echo "--> Forcing maven-gpg-plugin version from $maven_gpg_plugin_version to 3.0.1" + BUILD_ARGS="$BUILD_ARGS -Dmaven-gpg-plugin.version=3.0.1 -Darguments=-Dmaven-gpg-plugin.version=3.0.1" + ;; + *) + echo "--> maven-gpg-plugin version OK: $maven_gpg_plugin_version" + ;; + esac + + # HACK: Install pinentry helper program if missing. Avoids "signing failed: No pinentry" error. + if ! which pinentry >/dev/null 2>&1; then + echo '--> Installing missing pinentry helper for GPG' + sudo apt-get install -y pinentry-tty + # HACK: Restart the gpg agent, to notice the newly installed pinentry. + if { pgrep gpg-agent >/dev/null && which gpgconf >/dev/null 2>&1; } then + echo '--> Restarting gpg-agent' + gpgconf --reload gpg-agent + checkSuccess $? + fi + fi + else + echo '[WARNING] Skipping gpg setup (no GPG credentials).' fi # --== BUILD EXECUTION ==-- @@ -238,19 +368,19 @@ EOL # Run the build. if [ "$deployOK" -a -f release.properties ]; then echo - echo "== Cutting and deploying release version ==" + echo '== Cutting and deploying release version ==' BUILD_ARGS="$BUILD_ARGS release:perform" elif [ "$deployOK" ]; then echo - echo "== Building and deploying main branch SNAPSHOT ==" + echo '== Building and deploying main branch SNAPSHOT ==' BUILD_ARGS="-Pdeploy-to-scijava $BUILD_ARGS deploy" else echo - echo "== Building the artifact locally only ==" + echo '== Building the artifact locally only ==' BUILD_ARGS="$BUILD_ARGS install javadoc:javadoc" fi # Check the build result. - { mvn $BUILD_ARGS; echo $? > exit-code; } | tee mvn-log + { (set -x; mvn $BUILD_ARGS); echo $? > exit-code; } | tee mvn-log checkSuccess "$(cat exit-code)" mvn-log # --== POST-BUILD ACTIONS ==-- @@ -269,20 +399,6 @@ EOL done fi - if [ "$deployOK" -a "$success" -eq 0 ]; then - echo - echo "== Invalidating SciJava Maven repository cache ==" - curl -fsLO https://raw.githubusercontent.com/scijava/scijava-scripts/bdd932af4c4816f88cb6a52cdd7449f175934634/maven-helper.sh && - gav=$(sh maven-helper.sh gav-from-pom pom.xml) && - ga=${gav%:*} && - echo "--> Artifact to invalidate = $ga" && - echo "machine maven.scijava.org" >"$HOME/.netrc" && - echo " login $MAVEN_USER" >>"$HOME/.netrc" && - echo " password $MAVEN_PASS" >>"$HOME/.netrc" && - sh maven-helper.sh invalidate-cache "$ga" - checkSuccess $? - fi - echo ::endgroup:: fi diff --git a/class-version.sh b/class-version.sh index 96ddb53..ed77a66 100755 --- a/class-version.sh +++ b/class-version.sh @@ -55,8 +55,20 @@ first_class() { for arg in "$@" do + # Resolve Maven dependency coordinates into local files. case "$arg" in - *:*:*) + *:*:*:*) # g:a:v:c + gav=${arg%:*} + g=${gav%%:*} + av=${gav#*:} + a=${av%:*} + v=${av#*:} + c=${arg##*:} + f="$HOME/.m2/repository/$(echo "$g" | tr '.' '/')/$a/$v/$a-$v-$c.jar" + test -f "$f" || mvn dependency:get -Dartifact="$g:$a:$v:jar:$c" + arg="$f" + ;; + *:*:*) # g:a:v ga=${arg%:*} g=${ga%%:*} a=${ga#*:} @@ -66,6 +78,7 @@ do arg="$f" ;; esac + # Handle the various local file cases. case "$arg" in *.class) version=$(cat "$arg" | class_version) @@ -84,6 +97,6 @@ do continue esac - # report the results + # Report the results. echo "$arg: $version" done diff --git a/github-actionify.sh b/github-actionify.sh index 6707c5f..f6c5a56 100755 --- a/github-actionify.sh +++ b/github-actionify.sh @@ -109,9 +109,9 @@ process() { # -- GitHub Action steps -- - actionCheckout="uses: actions/checkout@v2" + actionCheckout="uses: actions/checkout@v4" actionSetupJava="name: Set up Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '8' distribution: 'zulu' @@ -121,16 +121,18 @@ process() { - name: Install conda packages run: conda env update -f environment.yml -n base" actionSetupCI="name: Set up CI environment - run: $ciSetupScript" + run: $ciSetupScript + shell: bash" actionExecuteBuild="name: Execute the build - run: $ciBuildScript" + run: $ciBuildScript + shell: bash" actionSecrets="env: GPG_KEY_NAME: \${{ secrets.GPG_KEY_NAME }} GPG_PASSPHRASE: \${{ secrets.GPG_PASSPHRASE }} MAVEN_USER: \${{ secrets.MAVEN_USER }} MAVEN_PASS: \${{ secrets.MAVEN_PASS }} - OSSRH_USER: \${{ secrets.OSSRH_USER }} - OSSRH_PASS: \${{ secrets.OSSRH_PASS }} + CENTRAL_USER: \${{ secrets.CENTRAL_USER }} + CENTRAL_PASS: \${{ secrets.CENTRAL_PASS }} SIGNING_ASC: \${{ secrets.SIGNING_ASC }}" # -- Do things -- diff --git a/maven-helper.sh b/maven-helper.sh deleted file mode 100755 index 54766c2..0000000 --- a/maven-helper.sh +++ /dev/null @@ -1,567 +0,0 @@ -#!/bin/sh - -# This script uses the SciJava Maven repository at https://maven.scijava.org/ -# to fetch an artifact, or to determine the state of it. - -# error out whenever a command fails -set -e - -root_url () { - test snapshots != "$2" || { - if curl -fs https://maven.scijava.org/service/local/repositories/sonatype-snapshots/content/"$1"/maven-metadata.xml > /dev/null 2>&1 - then - echo https://maven.scijava.org/service/local/repositories/sonatype-snapshots/content - else - echo https://maven.scijava.org/content/repositories/snapshots - fi - return - } - echo https://maven.scijava.org/service/local/repo_groups/public/content -} - -die () { - echo "$*" >&2 - exit 1 -} - -# Helper (thanks, BSD!) - -get_mtime () { - stat -c %Y "$1" -} -case "$(uname -s 2> /dev/null)" in -MINGW*) - get_mtime () { - date -r "$1" +%s - } - ;; -Darwin) - get_mtime () { - stat -f %m "$1" - } - ;; -esac - -# Parse :: triplets (i.e. GAV parameters) - -groupId () { - echo "${1%%:*}" -} - -artifactId () { - result="${1#*:}" - echo "${result%%:*}" -} - -version () { - result="${1#*:}" - case "$result" in - *:*) - echo "${1##*:}" - ;; - esac -} - -# Given an xml, extract the first - -extract_tag () { - result="${2%%*}" - case "$result" in - "$2") - ;; - *) - echo "${result#*<$1>}" - ;; - esac -} - -# Given an xml, extract the last - -extract_last_tag () { - result="${2##*<$1>}" - case "$result" in - "$2") - ;; - *) - echo "${result%%*}" - ;; - esac -} - -# Given an xml, skip all sections - -skip_tag () { - result="$2" - while true - do - case "$result" in - *"<$1>"*) - result="${result%%<$1>*}${result#*}" - ;; - *) - break - ;; - esac - done - echo "$result" -} - -# Given the xml of a POM, find the parent GAV - -parent_gav_from_pom_xml () { - pom="$1" - parent="$(extract_tag parent "$pom")" - test -n "$parent" || return - groupId="$(extract_tag groupId "$parent")" - artifactId="$(extract_tag artifactId "$parent")" - version="$(extract_tag version "$parent")" - echo "$groupId:$artifactId:$version" -} - -# Given a GAV parameter, determine the base URL of the project - -project_url () { - gav="$1" - artifactId="$(artifactId "$gav")" - infix="$(groupId "$gav" | tr . /)/$artifactId" - version="$(version "$gav")" - case "$version" in - *SNAPSHOT) - echo "$(root_url $infix snapshots)/$infix" - ;; - *) - # Release could be in either releases or thirdparty; try releases first - project_url="$(root_url $infix releases)/$infix" - header=$(curl -Is "$project_url/") - case "$header" in - HTTP/1.?" 200 OK"*) - ;; - *) - project_url="$(root_url $infix thirdparty)/$infix" - ;; - esac - echo "$project_url" - ;; - esac -} - -# Given a GAV parameter, determine the URL of the .jar file - -jar_url () { - gav="$1" - artifactId="$(artifactId "$gav")" - version="$(version "$gav")" - infix="$(groupId "$gav" | tr . /)/$artifactId/$version" - case "$version" in - *-SNAPSHOT) - url="$(root_url $infix snapshots)/$infix/maven-metadata.xml" - metadata="$(curl -s "$url")" - timestamp="$(extract_tag timestamp "$metadata")" - buildNumber="$(extract_tag buildNumber "$metadata")" - version=${version%-SNAPSHOT}-$timestamp-$buildNumber - echo "$(root_url $infix snapshots)/$infix/$artifactId-$version.jar" - ;; - *) - echo "$(root_url $infix releases)/$infix/$artifactId-$version.jar" - ;; - esac -} - -# Given a GAV parameter, return the URL to the corresponding .pom file - -pom_url () { - url="$(jar_url "$1")" - echo "${url%.jar}.pom" -} - -# Given a POM file, find its GAV parameter - -gav_from_pom () { - pom="$(cat "$1")" - parent="$(extract_tag parent "$pom")" - pom="$(skip_tag parent "$pom")" - pom="$(skip_tag dependencies "$pom")" - pom="$(skip_tag profiles "$pom")" - pom="$(skip_tag build "$pom")" - groupId="$(extract_tag groupId "$pom")" - test -n "$groupId" || groupId="$(extract_tag groupId "$parent")" - artifactId="$(extract_tag artifactId "$pom")" - version="$(extract_tag version "$pom")" - test -n "$version" || version="$(extract_tag version "$parent")" - echo "$groupId:$artifactId:$version" -} - -# Given a GAV parameter, find its parent's GAV - -parent_gav () { - gav="$1" - groupId="$(groupId "$gav")" - artifactId="$(artifactId "$gav")" - version="$(version "$gav")" - test -n "$version" || version="$(latest_version "$gav")" - pom="$(read_pom "$groupId:$artifactId:$version")" - parent_gav_from_pom_xml "$pom" -} - -# Given a POM file, find its parent's GAV - -parent_gav_from_pom () { - pom="$(cat "$1")" - parent_gav_from_pom_xml "$pom" -} - -# Given a POM file, extract its packaging - -packaging_from_pom () { - pom="$(cat "$1")" - pom="$(skip_tag parent "$pom")" - pom="$(skip_tag dependencies "$pom")" - pom="$(skip_tag profiles "$pom")" - pom="$(skip_tag build "$pom")" - packaging="$(extract_tag packaging "$pom")" - echo "${packaging:-jar}" -} - -# Given a GAV parameter possibly lacking a version, determine the latest version - -latest_version () { - metadata="$(curl -s "$(project_url "$1")"/maven-metadata.xml)" - latest="$(extract_tag release "$metadata")" - test -n "$latest" || latest="$(extract_tag latest "$metadata")" - test -n "$latest" || latest="$(extract_last_tag version "$metadata")" - echo "$latest" -} - -# Given a GA parameter, invalidate the cache in SciJava's Nexus' group/public - -SONATYPE_DATA_CACHE_URL=https://maven.scijava.org/service/local/data_cache/repositories/sonatype/content -SONATYPE_SNAPSHOTS_DATA_CACHE_URL=https://maven.scijava.org/service/local/data_cache/repositories/sonatype-snapshots/content -invalidate_cache () { - ga="$1" - artifactId="$(artifactId "$ga")" - infix="$(groupId "$ga" | tr . /)/$artifactId" - curl --netrc -i -X DELETE \ - $SONATYPE_DATA_CACHE_URL/$infix/maven-metadata.xml && - curl --netrc -i -X DELETE \ - $SONATYPE_SNAPSHOTS_DATA_CACHE_URL/$infix/maven-metadata.xml && - version="$(latest_version "$ga")" && - infix="$infix/$version" && - curl --netrc -i -X DELETE \ - $SONATYPE_DATA_CACHE_URL/$infix/$artifactId-$version.pom && - if test "$artifactId" = "${artifactId#pom-}" - then - curl --netrc -i -X DELETE \ - $SONATYPE_DATA_CACHE_URL/$infix/$artifactId-$version.jar - fi -} - -# Generate a temporary file; not thread-safe - -tmpfile () { - i=1 - while test -f /tmp/precompiled.$i"$1" - do - i=$(($i+1)) - done - echo /tmp/precompiled.$i"$1" -} - -# Given a GAV or a path, read the POM - -read_pom () { - case "$1" in - pom.xml|*/pom.xml|*\\pom.xml) - cat "$1" - ;; - *) - curl -s "$(pom_url "$1")" - ;; - esac -} - -# Given a GAV parameter (or pom.xml path) and a name, resolve a property (falling back to parents) - -get_property () { - gav="$1" - key="$2" - case "$key" in - imagej1.version) - latest_version net.imagej:ij - return - ;; - project.groupId) - groupId "$gav" - return - ;; - project.version) - version "$gav" - return - ;; - esac - while test -n "$gav" - do - pom="$(read_pom "$gav")" - properties="$(extract_tag properties "$pom")" - property="$(extract_tag "$key" "$properties")" - if test -n "$property" - then - echo "$property" - return - fi - gav="$(parent_gav_from_pom_xml "$pom")" - done - die "Could not resolve \${$2} in $1" -} - -# Given a GAV parameter and a string, expand properties - -expand () { - gav="$1" - string="$2" - result= - while true - do - case "$string" in - *'${'*'}'*) - result="$result${string%%\$\{*}" - string="${string#*\$\{}" - key="${string%\}*}" - result="$result$(get_property "$gav" "$key")" - string="${string#$key\}}" - ;; - *) - echo "$result$string" - break - ;; - esac - done -} - -# Given a GAV parameter, make a list of its dependencies (as GAV parameters) - -get_dependencies () { - pom="$(read_pom "$1")" - while true - do - case "$pom" in - *''*) - dependency="$(extract_tag dependency "$pom")" - scope="$(extract_tag scope "$dependency")" - case "$scope" in - ''|compile) - groupId="$(expand "$1" "$(extract_tag groupId "$dependency")")" - artifactId="$(extract_tag artifactId "$dependency")" - version="$(expand "$1" "$(extract_tag version "$dependency")")" - echo "$groupId:$artifactId:$version" - ;; - esac - pom="${pom#*}" - ;; - *) - break; - esac - done -} - -# Given a GAV parameter and a space-delimited list of GAV parameters, expand -# the list by the first parameter and its dependencies (unless the list already -# contains said parameter) - -get_all_dependencies () { - case " $2 " in - *" $1 "*) - ;; # list already contains the depdendency - *) - gav="$1" - set "" "$2 $1" - for dependency in $(get_dependencies "$gav") - do - set "" "$(get_all_dependencies "$dependency" "$2")" - done - ;; - esac - echo "$2" -} - -# Given a GAV parameter, download the .jar file - -get_jar () { - url="$(jar_url "$1")" - tmpfile="$(tmpfile .jar)" - curl -s "$url" > "$tmpfile" - test " "$tmpfile" - test PK = "$(head -c 2 "$tmpfile")" - echo "$tmpfile" -} - -# Given a GAV parameter, get the commit from the manifest of the deployed .jar - -commit_from_gav () { - jar="$(get_jar "$1")" - unzip -p "$jar" META-INF/MANIFEST.MF | - sed -n -e 's/^Implementation-Build: *//pi' | - tr -d '\r' - rm "$jar" -} - -# Given a GAV parameter, determine whether the .jar file is already in plugins/ -# or jars/ - -is_jar_installed () { - artifactId="$(artifactId "$1")" - version="$(version "$1")" - file=$artifactId-$version.jar - test -f "$file" || file=../plugins/$file - test -f "$file" || return 1 - case "$version" in - *-SNAPSHOT) - # is the file younger than a day? - mtime="$(get_mtime "$file")" - test "$(($mtime-$(date +%s)))" -gt -86400 - ;; - esac -} - -# Given a .jar file, determine whether it is an ImageJ 1.x plugin - -is_ij1_plugin () { - unzip -l "$1" plugins.config > /dev/null 2>&1 -} - -# Given a GAV parameter, download the .jar file and its dependencies as needed -# and install them into plugins/ or jars/, respectively - -install_jar () { - for gav in $(get_all_dependencies "$1") - do - if ! is_jar_installed "$gav" - then - tmp="$(get_jar "$gav")" - name="$(artifactId "$gav")-$(version "$gav").jar" - if test -d ../plugins && is_ij1_plugin "$tmp" - then - mv "$tmp" "../plugins/$name" - else - mv "$tmp" "$name" - fi - fi - done -} - -# Determine whether a local project (specified as pom.xml) needs to be deployed - -is_deployed () { - gav="$(gav_from_pom "$1")" && - commit="$(commit_from_gav "$gav")" && - test -n "$commit" && - dir="$(dirname "$1")" && - (cd "$dir" && - git diff --quiet "$commit".. -- .) -} - -# The main part - -case "$1" in -commit) - commit_from_gav "$2" - ;; -deps|dependencies) - get_dependencies "$2" - ;; -all-deps|all-dependencies) - get_all_dependencies "$2" | - tr ' ' '\n' | - grep -v '^$' - ;; -latest-version) - latest_version "$2" - ;; -invalidate-cache) - invalidate_cache "$2" - ;; -gav-from-pom) - gav_from_pom "$2" - ;; -parent-gav) - parent_gav "$2" - ;; -pom-url) - pom_url "$2" - ;; -parent-gav-from-pom) - parent_gav_from_pom "$2" - ;; -packaging-from-pom) - packaging_from_pom "$2" - ;; -property-from-pom|get-property|property) - if test $# -lt 3 - then - get_property pom.xml "$2" - else - get_property "$2" "$3" - fi - ;; -install) - install_jar "$2" - ;; -is-deployed) - is_deployed "$2" - ;; -*) - test $# -eq 0 || echo "Unknown command: $1" >&2 - die "Usage: $0 [command] [argument...]"' - -Commands: - -commit :: - Gets the commit from which the given artifact was built. - -dependencies :: - Lists the direct dependencies of the given artifact. - -all-dependencies :: - Lists all dependencies of the given artifact, including itself and - transitive dependencies. - -latest-version :[:] - Prints the current version of the given artifact (if "SNAPSHOT" is - passed as version, it prints the current snapshot version rather - than the release one). - -invalidate-cache : - Invalidates the version cached in the SciJava Nexus from OSS Sonatype, - e.g. after releasing a new version to Sonatype. Requires appropriate - credentials in $HOME/.netrc for https://maven.scijava.org/. - -parent-gav :[:] - Prints the GAV parameter of the parent project of the given artifact. - -pom-url :: - Gets the URL of the POM describing the given artifact. - -gav-from-pom - Prints the GAV parameter described in the given pom.xml file. - -parent-gav-from-pom - Prints the GAV parameter of the parent project of the pom.xml file. - -packaging-from-pom - Prints the packaging type of the given project. - -property-from-pom - Prints the property specified in the pom.xml file (or in its parents). - -install :: - Installs the given artifact and all its dependencies; if the artifact - or dependency to install is an ImageJ 1.x plugin and the parent - directory contains a subdirectory called "plugins", it will be - installed there, otherwise into the current directory. - -is-deployed - Tests whether the specified project is deployed alright. Fails - with exit code 1 if not. -' - ;; -esac diff --git a/release-version.sh b/release-version.sh index b0eb696..4505de4 100755 --- a/release-version.sh +++ b/release-version.sh @@ -181,13 +181,10 @@ test "$SKIP_VERSION_CHECK" || { } # Check that the project extends the latest version of pom-scijava. -MAVEN_HELPER="$(cd "$(dirname "$0")" && pwd)/maven-helper.sh" -test -f "$MAVEN_HELPER" || - die "Missing helper script at '$MAVEN_HELPER' -Do you have a full clone of https://github.com/scijava/scijava-scripts?" test "$SKIP_VERSION_CHECK" -o "$parentGAV" != "${parentGAV#$}" || { debug "Checking pom-scijava parent version" - latestParentVersion=$(sh -$- "$MAVEN_HELPER" latest-version "$parentGAV") + psjMavenMetadata=https://repo1.maven.org/maven2/org/scijava/pom-scijava/maven-metadata.xml + latestParentVersion=$(curl -fsL "$psjMavenMetadata" | grep '' | sed 's;.*>\([^<]*\)<.*;\1;') currentParentVersion=${parentGAV##*:} test "$currentParentVersion" = "$latestParentVersion" || die "Newer version of parent '$parentGAV' is available: $latestParentVersion. @@ -355,6 +352,25 @@ then $DRY_RUN git commit -s -m "Bump to next development cycle" fi && +# Check that release-only files were not erroneously committed. +if test -z "$DRY_RUN" +then + debug "Verifying release-only files were not committed" + for release_file in release.properties pom.xml.releaseBackup + do + if git diff --name-only HEAD~1 HEAD | grep -qF "$release_file" + then + die "FATAL: $release_file was committed to the branch! +This likely means your IDE automatically staged these files. +Please configure your IDE to NOT auto-stage files during the release process. +You can undo this failed release with: + git reset --hard HEAD~1 + git tag -d \$(sed -n 's/^scm.tag=//p' < release.properties) +Then try the release again after disabling IDE auto-staging." + fi + done +fi && + # Extract the name of the new tag. debug "Extracting new tag name" if test -z "$DRY_RUN" diff --git a/travis-build.sh b/travis-build.sh deleted file mode 100644 index 1a759e1..0000000 --- a/travis-build.sh +++ /dev/null @@ -1,243 +0,0 @@ -#!/bin/bash - -# -# travis-build.sh - A script to build and/or release SciJava-based projects. -# - -dir="$(dirname "$0")" - -success=0 -checkSuccess() { - # Log non-zero exit code. - test $1 -eq 0 || echo "==> FAILED: EXIT CODE $1" 1>&2 - - # Record the first non-zero exit code. - test $success -eq 0 && success=$1 -} - -# Build Maven projects. -if [ -f pom.xml ] -then - echo travis_fold:start:scijava-maven - echo "= Maven build =" - echo - echo "== Configuring Maven ==" - - # NB: Suppress "Downloading/Downloaded" messages. - # See: https://stackoverflow.com/a/35653426/1207769 - export MAVEN_OPTS="$MAVEN_OPTS -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn" - - # Populate the settings.xml configuration. - mkdir -p "$HOME/.m2" - settingsFile="$HOME/.m2/settings.xml" - customSettings=.travis/settings.xml - if [ -f "$customSettings" ] - then - cp "$customSettings" "$settingsFile" - else - cat >"$settingsFile" < - - - scijava.releases - travis - \${env.MAVEN_PASS} - - - scijava.snapshots - travis - \${env.MAVEN_PASS} - - - sonatype-nexus-releases - scijava-ci - \${env.OSSRH_PASS} - - -EOL - # NB: Use maven.scijava.org instead of Central if defined in repositories. - # This hopefully avoids intermittent "ReasonPhrase:Forbidden" errors - # when the Travis build pings Maven Central; see travis-ci/travis-ci#6593. - grep -A 2 '' pom.xml | grep -q 'maven.scijava.org' && - cat >>"$settingsFile" < - - scijava-mirror - SciJava mirror - https://maven.scijava.org/content/groups/public/ - central - - -EOL - cat >>"$settingsFile" < - - gpg - - - \${env.HOME}/.gnupg - - - - \${env.GPG_KEY_NAME} - \${env.GPG_PASSPHRASE} - - - - -EOL - fi - - # Determine whether deploying will be possible. - deployOK= - ciURL=$(mvn -q -Denforcer.skip=true -Dexec.executable=echo -Dexec.args='${project.ciManagement.url}' --non-recursive validate exec:exec 2>&1) - if [ $? -ne 0 ] - then - echo "No deploy -- could not extract ciManagement URL" - echo "Output of failed attempt follows:" - echo "$ciURL" - else - ciRepo=${ciURL##*/} - ciPrefix=${ciURL%/*} - ciOrg=${ciPrefix##*/} - if [ "$TRAVIS_SECURE_ENV_VARS" != true ] - then - echo "No deploy -- secure environment variables not available" - elif [ "$TRAVIS_PULL_REQUEST" != false ] - then - echo "No deploy -- pull request detected" - elif [ "$TRAVIS_REPO_SLUG" != "$ciOrg/$ciRepo" ] - then - echo "No deploy -- repository fork: $TRAVIS_REPO_SLUG != $ciOrg/$ciRepo" - # TODO: Detect travis-ci.org versus travis-ci.com? - else - echo "All checks passed for artifact deployment" - deployOK=1 - fi - fi - - # Install GPG on OSX/macOS - if [ "$TRAVIS_OS_NAME" = osx ] - then - HOMEBREW_NO_AUTO_UPDATE=1 brew install gnupg2 - fi - - # Import the GPG signing key. - keyFile=.travis/signingkey.asc - key=$1 - iv=$2 - if [ "$key" -a "$iv" -a -f "$keyFile.enc" ] - then - # NB: Key and iv values were given as arguments. - echo - echo "== Decrypting GPG keypair ==" - openssl aes-256-cbc -K "$key" -iv "$iv" -in "$keyFile.enc" -out "$keyFile" -d - checkSuccess $? - fi - if [ "$deployOK" -a -f "$keyFile" ] - then - echo - echo "== Importing GPG keypair ==" - gpg --batch --fast-import "$keyFile" - checkSuccess $? - fi - - # Run the build. - BUILD_ARGS='-B -Djdk.tls.client.protocols="TLSv1,TLSv1.1,TLSv1.2"' - if [ "$deployOK" -a "$TRAVIS_BRANCH" = master ] - then - echo - echo "== Building and deploying master SNAPSHOT ==" - mvn -Pdeploy-to-scijava $BUILD_ARGS deploy - checkSuccess $? - elif [ "$deployOK" -a -f release.properties ] - then - echo - echo "== Cutting and deploying release version ==" - mvn -B $BUILD_ARGS release:perform - checkSuccess $? - echo "== Invalidating SciJava Maven repository cache ==" - curl -fsLO https://raw.githubusercontent.com/scijava/scijava-scripts/master/maven-helper.sh && - gav=$(sh maven-helper.sh gav-from-pom pom.xml) && - ga=${gav%:*} && - echo "--> Artifact to invalidate = $ga" && - echo "machine maven.scijava.org" > "$HOME/.netrc" && - echo " login travis" >> "$HOME/.netrc" && - echo " password $MAVEN_PASS" >> "$HOME/.netrc" && - sh maven-helper.sh invalidate-cache "$ga" - checkSuccess $? - else - echo - echo "== Building the artifact locally only ==" - mvn $BUILD_ARGS install javadoc:javadoc - checkSuccess $? - fi - echo travis_fold:end:scijava-maven -fi - -# Configure conda environment, if one is needed. -if [ -f environment.yml ] -then - echo travis_fold:start:scijava-conda - echo "= Conda setup =" - - condaDir=$HOME/miniconda - condaSh=$condaDir/etc/profile.d/conda.sh - if [ ! -f "$condaSh" ]; then - echo - echo "== Installing conda ==" - if [ "$TRAVIS_PYTHON_VERSION" = "2.7" ]; then - wget https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh -O miniconda.sh - else - wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh - fi - rm -rf "$condaDir" - bash miniconda.sh -b -p "$condaDir" - checkSuccess $? - fi - - echo - echo "== Updating conda ==" - . "$condaSh" && - conda config --set always_yes yes --set changeps1 no && - conda update -q conda && - conda info -a - checkSuccess $? - - echo - echo "== Configuring environment ==" - condaEnv=travis-scijava - test -d "$condaDir/envs/$condaEnv" && condaAction=update || condaAction=create - conda env "$condaAction" -n "$condaEnv" -f environment.yml && - conda activate "$condaEnv" - checkSuccess $? - - echo travis_fold:end:scijava-conda -fi - -# Execute Jupyter notebooks. -if which jupyter >/dev/null 2>/dev/null -then - echo travis_fold:start:scijava-jupyter - echo "= Jupyter notebooks =" - # NB: This part is fiddly. We want to loop over files even with spaces, - # so we use the "find ... -print0 | while read $'\0' ..." idiom. - # However, that runs the piped expression in a subshell, which means - # that any updates to the success variable will not persist outside - # the loop. So we suppress all stdout inside the loop, echoing only - # the final value of success upon completion, and then capture the - # echoed value back into the parent shell's success variable. - success=$(find . -name '*.ipynb' -print0 | { - while read -d $'\0' nbf - do - echo 1>&2 - echo "== $nbf ==" 1>&2 - jupyter nbconvert --execute --stdout "$nbf" >/dev/null - checkSuccess $? - done - echo $success - }) - echo travis_fold:end:scijava-jupyter -fi - -exit $success