From 55a29038143ef762bbdcd7cd3fc81c49da01fefe Mon Sep 17 00:00:00 2001 From: Mark Patton Date: Tue, 19 Dec 2023 09:30:05 -0500 Subject: [PATCH] Make workflows reusable. Implement the combined Java release workflow. Wait for artifacts to hit Maven Central after release. Create GitHub releases. --- .github/actions/maven-release/action.yml | 64 ++++++++++ .github/workflows/ci.yml | 22 ++-- .github/workflows/pass-java-release.yml | 126 ++++++++++++++++++- .github/workflows/release.yml | 150 ++++++++++++----------- .github/workflows/snapshot.yml | 62 ++++++++++ 5 files changed, 339 insertions(+), 85 deletions(-) create mode 100644 .github/actions/maven-release/action.yml create mode 100644 .github/workflows/snapshot.yml diff --git a/.github/actions/maven-release/action.yml b/.github/actions/maven-release/action.yml new file mode 100644 index 00000000..66112cf9 --- /dev/null +++ b/.github/actions/maven-release/action.yml @@ -0,0 +1,64 @@ +name: Release Maven module +description: | + Releases a Maven module and waits for it to appear in Sonatype. + +inputs: + repodir: + description: 'Directory of repository to release' + required: false + default: '.' + +runs: + using: composite + steps: + - name: Get the artifact from POM + shell: bash + working-directory: ${{ inputs.repodir }} + run: | + echo "ARTIFACT=`mvn help:evaluate -Dexpression=project.artifactId -q -DforceStdout`" >> $GITHUB_ENV + + # Build and deploy to sonatype if the release does not already exist. + # Attempt to handle case of Sonatype failing to close the repository, but still succeeding + - name: Release Maven module and push to Sonatype + shell: bash + working-directory: ${{ inputs.repodir }} + run: | + goal="deploy" + + if curl -f -s https://repo1.maven.org/maven2/org/eclipse/pass/$ARTIFACT/$RELEASE/ > /dev/null; then + echo "Release $RELEASE already exists" + goal="install" + fi + + mvn -B -U -V -ntp -P release -DstagingProgressTimeoutMinutes=15 clean $goal | tee release.log + code=${PIPESTATUS[0]} + + marker1="Remote staging repositories are being released" + marker2="Failed to execute goal org.sonatype.plugins:nexus-staging-maven-plugin" + + if [ "$code" -ne "0" ]; then + if grep -q "$marker1" release.log && grep -q "$marker2" release.log; then + echo "Failed cleaning up after pushing to Sonatype, but it may have succeeded anyway." + else + exit "$code" + fi + fi + + - name: Wait for Maven Central + shell: bash + run: | + echo "Waiting for $ARTIFACT $RELEASE to be released." + + counter=0 + until curl -f -s https://repo1.maven.org/maven2/org/eclipse/pass/$ARTIFACT/$RELEASE/ > /dev/null + do + sleep 60 + echo "." + counter=$((counter+1)) + + if [ "$counter" -gt 30 ]; then + echo "Timed out waiting for release" + exit 1 + fi + done + echo "$ARTIFACT $RELEASE has been released." diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0279c791..460f125c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,33 +1,35 @@ name: main Continuous Integration -on: [ pull_request, workflow_dispatch ] +on: [pull_request, workflow_dispatch, workflow_call] concurrency: group: ci-${{ github.head_ref || github.ref }} cancel-in-progress: true jobs: - print-workflow-description: - runs-on: ubuntu-latest - steps: - - run: echo "This is a CI build of branch ${{ github.ref }} in repository ${{ github.repository }}" - - run: echo "This job was triggered by a ${{ github.event_name }} event and is running on a ${{ runner.os }} server" - - call-tests: + test: name: Run Unit & Integration Tests runs-on: ubuntu-latest steps: + - name: Print description + run: | + echo "This is a CI build of branch ${{ github.ref }} in repository ${{ github.repository }}" + echo "This job was triggered by a ${{ github.event_name }} event and is running on a ${{ runner.os }} server" + - name: Checkout the repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: java-version: 17 distribution: 'temurin' + - name: Cache Maven packages uses: actions/cache@v2 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-m2 + - name: Run Tests - run: mvn -V -ntp verify --file pom.xml + run: mvn -U -B -V -ntp verify diff --git a/.github/workflows/pass-java-release.yml b/.github/workflows/pass-java-release.yml index 043edaf4..e7e1b224 100644 --- a/.github/workflows/pass-java-release.yml +++ b/.github/workflows/pass-java-release.yml @@ -1,10 +1,130 @@ -name: Placeholder for consolidated Java release +# Build all the Java modules by constructing a synthentic combined module. +# The main repo provides the pom for the combined module, but is kept on the side. +# The combined module is in the directory combined which contains pass-core and pass-support repos. + +name: "Publish: release all Java modules" +run-name: Release all Java modules (${{ inputs.releaseversion }} -> ${{ inputs.nextversion}}) on: workflow_dispatch: + inputs: + releaseversion: + description: 'Release version' + required: true + nextversion: + description: 'Next dev version' + required: true jobs: - test: + release: runs-on: ubuntu-latest + env: + RELEASE: ${{ inputs.releaseversion }} + NEXT: ${{ inputs.nextversion }} steps: - - run: echo "Hello Moo!" \ No newline at end of file + - name: Checkout main + uses: actions/checkout@v3 + with: + path: main + + - name: Setup Java & Maven + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'temurin' + cache: 'maven' + server-id: ossrh + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + gpg-private-key: ${{ secrets.MAVEN_GPG_KEY }} + gpg-passphrase: MAVEN_GPG_PASSPHRASE + + - name: Get the artifact from the main POM + working-directory: main + run: | + echo "ARTIFACT=`mvn help:evaluate -Dexpression=project.artifactId -q -DforceStdout`" >> $GITHUB_ENV + + - name: Configure git user + run: | + git config --global user.name ${{ github.actor }} + git config --global user.email "${{ github.actor }}@users.noreply.github.com" + + # Combine modules together so maven can do everything at once + - name: Create combined module + run: | + mkdir combined + cp main/pom.xml combined + sed -i '/<\/developers>/a pass-corepass-support' combined/pom.xml + + - name: Checkout pass-core and pass-support using PAT + run: | + git clone https://${{ secrets.JAVA_RELEASE_PAT }}@github.com/eclipse-pass/pass-core.git combined/pass-core + git clone https://${{ secrets.JAVA_RELEASE_PAT }}@github.com/eclipse-pass/pass-support.git combined/pass-support + + - name: Set the release version, commit the change, and tag it + run: | + (cd main && mvn versions:set -B -ntp -DnewVersion=$RELEASE && git commit -am "Update version to $RELEASE" && git tag $RELEASE) + (cd combined && mvn versions:set -B -ntp -DnewVersion=$RELEASE) + (cd combined/pass-core && git commit -am "Update version to $RELEASE" && git tag $RELEASE) + (cd combined/pass-support && git commit -am "Update version to $RELEASE" && git tag $RELEASE) + + - name: Release Java modules + uses: ./main/.github/actions/maven-release + with: + repodir: combined + env: + MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} + MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} + + - name: Set the next dev version and commit the change + run: | + (cd main && mvn versions:set -B -ntp -DallowSnapshots=true -DnewVersion=$NEXT && git commit -am "Update version to $NEXT") + (cd combined && mvn versions:set -B -ntp -DallowSnapshots=true -DnewVersion=$NEXT) + (cd combined/pass-core && git commit -am "Update version to $NEXT") + (cd combined/pass-support && git commit -am "Update version to $NEXT") + + - name: Release dev Java modules + working-directory: combined + run: | + mvn -B -V -ntp -P release clean deploy -DskipTests -DskipITs + env: + MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} + MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} + + - name: Login to GHCR + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.JAVA_RELEASE_PAT }} + + - name: Push Docker images to GHCR + run: | + docker push ghcr.io/eclipse-pass/pass-core-main:$RELEASE + docker push ghcr.io/eclipse-pass/pass-core-main:$NEXT + docker push ghcr.io/eclipse-pass/deposit-services-core:$RELEASE + docker push ghcr.io/eclipse-pass/deposit-services-core:$NEXT + docker push ghcr.io/eclipse-pass/pass-notification-service:$RELEASE + docker push ghcr.io/eclipse-pass/pass-notification-service:$NEXT + docker push ghcr.io/eclipse-pass/jhu-grant-loader:$RELEASE + docker push ghcr.io/eclipse-pass/jhu-grant-loader:$NEXT + docker push ghcr.io/eclipse-pass/pass-journal-loader:$RELEASE + docker push ghcr.io/eclipse-pass/pass-journal-loader:$NEXT + docker push ghcr.io/eclipse-pass/pass-nihms-loader:$RELEASE + docker push ghcr.io/eclipse-pass/pass-nihms-loader:$NEXT + + - name: Push the commits and tags + run: | + (cd main && git push origin && git push origin --tags) + (cd combined/pass-core && git push origin && git push origin --tags) + (cd combined/pass-support && git push origin && git push origin --tags) + + - name: Create GitHub main release + run: | + gh release create "$RELEASE" --repo=eclipse-pass/main --generate-notes + gh release create "$RELEASE" --repo=eclipse-pass/pass-core --generate-notes + gh release create "$RELEASE" --repo=eclipse-pass/pass-support --generate-notes + env: + GITHUB_TOKEN: ${{ secrets.JAVA_RELEASE_PAT }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 580c728c..05d9189f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,21 @@ -name: "Publish: manual full release OR automatic snapshot" +# Release artifact will be pushed to Sonatype, which is synced to Maven Central +# Build artifacts get pushed to Sonatype and non-SNAPSHOT versions are then +# auto-synced to Maven Central. +# +# If images are given (separated by spaces), then they are assumed to be produced by +# Maven and have release and dev tags. +# +# Various secrets are needed to do the release. +# +# Credentials for Sonatype +# - OSSRH_USERNAME +# - OSSRH_PASSWORD +# GPG key + passphrase to sign artifacts for Maven Central +# - MAVEN_GPG_PASSPHRASE +# - MAVEN_GPG_KEY + +name: "Publish: manual full release" +run-name: Release (${{ inputs.releaseversion }} -> ${{ inputs.nextversion}}) on: workflow_dispatch: @@ -9,36 +26,28 @@ on: nextversion: description: 'Next dev version' required: true - push: - branches: - - 'main' + workflow_call: + inputs: + releaseversion: + description: 'Release version' + required: true + type: string + nextversion: + description: 'Next dev version' + required: true + type: string + images: + description: 'Images to push' + required: false + type: string jobs: - setup: - runs-on: ubuntu-latest - outputs: - # Output project version from the POM to conditionally run dependent steps - project-version: ${{ steps.project_version.outputs.version }} - steps: - - name: Checkout latest code - uses: actions/checkout@v3 - - - name: Setup Java & Maven - uses: actions/setup-java@v3 - with: - java-version: 17 - distribution: 'temurin' - cache: 'maven' - - - name: Get project version from POM - id: project_version - run: echo "VERSION=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`" >> $GITHUB_OUTPUT - - # Run only if project POM has version ending in "-SNAPSHOT" - snapshot: - needs: setup - if: github.event_name == 'push' && endsWith(needs.setup.outputs.project-version, '-SNAPSHOT') + release: runs-on: ubuntu-latest + env: + RELEASE: ${{ inputs.releaseversion }} + NEXT: ${{ inputs.nextversion }} + IMAGES: ${{ inputs.images }} steps: - name: Checkout latest code uses: actions/checkout@v3 @@ -55,64 +64,61 @@ jobs: gpg-private-key: ${{ secrets.MAVEN_GPG_KEY }} gpg-passphrase: MAVEN_GPG_PASSPHRASE - - name: Publish SNAPSHOT - run: mvn -B --no-transfer-progress clean deploy - env: - MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} - MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} - MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} - - # Run for manual trigger (workflow dispatch), since you'll have release and next dev versions specified - # All commits will have a -SNAPSHOT project version anyway, since the releases will be handled here - release: - needs: setup - if: github.event_name == 'workflow_dispatch' - runs-on: ubuntu-latest - env: - RELEASE: ${{ inputs.releaseversion }} - NEXT: ${{ inputs.nextversion }} - steps: - - name: Checkout latest code - uses: actions/checkout@v3 - - name: Config git user run: | git config user.name ${{ github.actor }} git config user.email "${{ github.actor }}@users.noreply.github.com" - - name: Setup Java & Maven - uses: actions/setup-java@v3 - with: - java-version: 17 - distribution: 'temurin' - cache: 'maven' - server-id: ossrh - server-username: MAVEN_USERNAME - server-password: MAVEN_PASSWORD - gpg-private-key: ${{ secrets.MAVEN_GPG_KEY }} - gpg-passphrase: MAVEN_GPG_PASSPHRASE - # ============================================================================= - # Start the release - # ============================================================================= - - name: Release main POM + - name: Set the release version, commit the change, and tag it run: | - mvn -B -U -V -ntp release:prepare -DreleaseVersion=$RELEASE -Dtag=$RELEASE -DdevelopmentVersion=$NEXT -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn - mvn -B -U -V -ntp release:perform -P release -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn + mvn versions:update-parent -B -ntp -DparentVersion=$RELEASE + mvn versions:set -B -ntp -DnewVersion=$RELEASE + git commit -am "Update version to $RELEASE" + git tag $RELEASE + + - name: Release Java modules + uses: eclipse-pass/main/.github/actions/maven-release@combined-release env: MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} - - name: Build and publish new dev version - run: mvn -B -U -V -ntp deploy -P release + - name: Set the next dev version and commit the change + run: | + mvn versions:update-parent -B -ntp -DallowSnapshots=true -DparentVersion=$NEXT + mvn versions:set -B -ntp -DallowSnapshots=true -DnewVersion=$NEXT + git commit -am "Update version to $NEXT" + + - name: Release dev Java modules + run: | + mvn -B -ntp -P release clean deploy -DskipTests -DskipITs env: MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} - - name: Push release plugin commits - if: github.ref_type == 'branch' && github.ref_protected == false - run: git push origin ${{ github.ref_name }} + - name: Login to GHCR + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Push Docker images to GHCR + run: | + for name in $IMAGES + do + docker push $name:$RELEASE + docker push $name:$NEXT + done - - name: Push new release tag GH - run: git push origin --tags + - name: Push the commits and tags + run: | + git push origin + git push origin --tags + + - name: Create GitHub release + run: | + gh release create "$RELEASE" --generate-notes + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml new file mode 100644 index 00000000..7c378f2f --- /dev/null +++ b/.github/workflows/snapshot.yml @@ -0,0 +1,62 @@ +name: "Publish: manual OR automatic snapshot" + +# If images are given (separated by spaces), then they are assumed to be produced by +# Maven and have release and dev tags. + +on: + workflow_dispatch: + workflow_call: + inputs: + images: + description: 'Images to push' + required: false + type: string + push: + branches: + - 'main' + +jobs: + snapshot: + runs-on: ubuntu-latest + steps: + - name: Checkout latest code + uses: actions/checkout@v3 + + - name: Setup Java & Maven + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'temurin' + cache: 'maven' + server-id: ossrh + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + gpg-private-key: ${{ secrets.MAVEN_GPG_KEY }} + gpg-passphrase: MAVEN_GPG_PASSPHRASE + + - name: Get project version from POM + id: project_version + run: echo "VERSION=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`" >> $GITHUB_OUTPUT + + # Only execute for -SNAPSHOT versions + - name: Publish SNAPSHOT + if: ${{ endsWith(steps.project_version.outputs.version, '-SNAPSHOT') }} + run: mvn -B -ntp clean deploy + env: + MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} + MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} + + - name: Login to GHCR + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Push Docker images to GHCR + run: | + for name in ${{ inputs.images }} + do + docker push $name:${{ steps.project_version.outputs.version }} + done