diff --git a/.github/CompatibilityUtils.java b/.build/CompatibilityUtils.java similarity index 100% rename from .github/CompatibilityUtils.java rename to .build/CompatibilityUtils.java diff --git a/.github/Helper.java b/.build/Helper.java similarity index 100% rename from .github/Helper.java rename to .build/Helper.java diff --git a/.github/PreRelease.java b/.build/PreRelease.java similarity index 100% rename from .github/PreRelease.java rename to .build/PreRelease.java diff --git a/.github/PulsarConfigDoc.java b/.build/PulsarConfigDoc.java similarity index 98% rename from .github/PulsarConfigDoc.java rename to .build/PulsarConfigDoc.java index 78c394e96e..c3b5a52998 100755 --- a/.github/PulsarConfigDoc.java +++ b/.build/PulsarConfigDoc.java @@ -34,7 +34,7 @@ * - Config file (whether the property is settable from a configuration file * - Default value *

- * Run with `.github/PulsarConfigDoc.java -d documentation/src/main/docs/pulsar/config` + * Run with `.build/PulsarConfigDoc.java -d documentation/src/main/docs/pulsar/config` *

*/ @CommandLine.Command(name = "pulsar-config-doc", mixinStandardHelpOptions = true, version = "0.1", diff --git a/.github/ci-maven-settings.xml b/.build/ci-maven-settings.xml similarity index 100% rename from .github/ci-maven-settings.xml rename to .build/ci-maven-settings.xml diff --git a/.github/maven-settings.xml.gpg b/.build/maven-settings.xml.gpg similarity index 100% rename from .github/maven-settings.xml.gpg rename to .build/maven-settings.xml.gpg diff --git a/.github/smallrye-sign.asc.gpg b/.build/smallrye-sign.asc.gpg similarity index 100% rename from .github/smallrye-sign.asc.gpg rename to .build/smallrye-sign.asc.gpg diff --git a/.github/PostRelease.java b/.github/PostRelease.java deleted file mode 100755 index 9d70f27989..0000000000 --- a/.github/PostRelease.java +++ /dev/null @@ -1,164 +0,0 @@ -///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS org.kohsuke:github-api:1.117 -//DEPS info.picocli:picocli:4.5.0 -//SOURCES Helper.java - -import org.kohsuke.github.*; -import picocli.CommandLine; - -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.Callable; -import java.util.stream.Collectors; - -import static helpers.Helper.*; - -/** - * Script run after the release. - * The script does the following action in this order: - * - checks that the previous milestone is closed, close it if not - * - checks that the next milestone exists, or create it if not - * - creates the Github release and compute the release notes - *

- * Run with `./PostRelease.java --token=GITHUB_TOKEN --release-version=version - *

- * 1. The github token is mandatory. - *

- * The version is taken from the last tag if not set. - */ -@CommandLine.Command(name = "post-release", mixinStandardHelpOptions = true, version = "0.1", - description = "Post-Release Check") -public class PostRelease implements Callable { - - @CommandLine.Option(names = "--token", description = "The Github Token", required = true) - private String token; - - @CommandLine.Option(names = "--release-version", description = "Set the released version", required = true) - private String releaseVersion; - - private static final String REPO = "smallrye/smallrye-reactive-messaging"; - - public static void main(String... args) { - int exitCode = new CommandLine(new PostRelease()).execute(args); - System.exit(exitCode); - } - - @Override - public Integer call() throws Exception { - GitHub gitHub = new GitHubBuilder().withOAuthToken(token).build(); - GHRepository repository = gitHub.getRepository(REPO); - - List tags = repository.listTags().toList(); - List milestones = repository.listMilestones(GHIssueState.ALL).toList(); - - info("Running post-release checks for release %s", releaseVersion); - - // Check that the tag associated with the release version exists - GHTag tag = getTag(releaseVersion, tags); - - assert tag != null; - - // Check that the milestone exists (this check has already been done during the pre-release checks, just to be double sure) - GHMilestone milestone = milestones.stream().filter(m -> m.getTitle().equals(releaseVersion)).findFirst() - .orElse(null); - failIfTrue(() -> milestone == null, "Unable to find the milestone %s", releaseVersion); - assert milestone != null; - success("Milestone %s found (%s)", milestone.getTitle(), milestone.getHtmlUrl()); - - success("Post-release check successful"); - info("Starting post-release tasks"); - - // Close milestone - if (milestone.getState() != GHMilestoneState.CLOSED) { - milestone.close(); - success("Milestone %s closed (%s)", milestone.getTitle(), milestone.getHtmlUrl()); - } else { - success("Milestone %s already closed (%s)", milestone.getTitle(), milestone.getHtmlUrl()); - } - - // Compute next version - String nextVersion = getNextVersion(releaseVersion); - success("Next version will be %s", nextVersion); - - // Create new milestone if it does not already exist - GHMilestone nextMilestone = milestones.stream().filter(m -> m.getTitle().equals(nextVersion)).findFirst() - .orElse(null); - if (nextMilestone != null) { - success("Next milestone (%s) already exists: %s", nextMilestone.getTitle(), nextMilestone.getHtmlUrl()); - } else { - nextMilestone = repository.createMilestone(nextVersion, null); - success("Next milestone (%s) created: %s", nextMilestone.getTitle(), nextMilestone.getHtmlUrl()); - } - - // Compute the release notes and create releases - List issues = repository.getIssues(GHIssueState.CLOSED, milestone); - String description = createReleaseDescription(issues); - // Check if release already exists - GHRelease existingRelease = repository.getReleaseByTagName(tag.getName()); - if (existingRelease != null) { - info("Release %s already exists (%s) - skip release note generation", existingRelease.getName(), existingRelease.getHtmlUrl()); - info("Generated release notes:\n%s", description); - - if (existingRelease.isDraft()) { - existingRelease.update().draft(false); - success("Marked release %s as non-draft", existingRelease.getName()); - } - } else { - GHRelease release = repository.createRelease(releaseVersion) - .name(releaseVersion) - .body(description) - .create(); - success("Release %s created: %s", releaseVersion, release.getHtmlUrl()); - } - - completed("Post-Release done!"); - - return 0; - } - - private GHTag getTag(String releaseVersion, List tags) { - failIfTrue(tags::isEmpty, "No tags found in repository"); - Optional first = tags.stream().filter(tag -> tag.getName().equals(releaseVersion)).findFirst(); - if (first.isPresent()) { - success("Tag %s found", releaseVersion); - return first.get(); - } - fail("Unable to find the tag %s in the repository", releaseVersion); - return null; - } - - private String getNextVersion(String v) { - String[] segments = v.split("\\."); - if (segments.length < 3) { - fail("Invalid version %s, number of segments must be at least 3, found %d", v, segments.length); - } - - return String.format("%s.%d.0", segments[0], Integer.parseInt(segments[1]) + 1); - } - - private String createReleaseDescription(List issues) throws IOException { - File file = new File("target/differences.md"); - String compatibility = ""; - if (file.isFile()) { - compatibility += new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); - } - - StringBuilder desc = new StringBuilder(); - desc.append("### Changelog\n\n"); - List list = issues.stream().map(this::line).collect(Collectors.toList()); - desc.append(String.join("\n", list)); - desc.append("\n"); - - desc.append(compatibility).append("\n"); - return desc.toString(); - } - - private String line(GHIssue issue) { - return String.format(" * [#%d](%s) - %s", issue.getNumber(), issue.getHtmlUrl(), issue.getTitle()); - } - -} diff --git a/.github/decrypt-secrets.sh b/.github/decrypt-secrets.sh deleted file mode 100644 index f8159b76e1..0000000000 --- a/.github/decrypt-secrets.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash - -echo "Decrypting smallrye signature" -gpg --quiet --batch --yes --decrypt --passphrase="${SECRET_FILES_PASSPHRASE}" \ - --output smallrye-sign.asc .github/smallrye-sign.asc.gpg - -echo "Decrypting Maven settings" -gpg --quiet --batch --yes --decrypt --passphrase="${SECRET_FILES_PASSPHRASE}" \ - --output maven-settings.xml .github/maven-settings.xml.gpg diff --git a/.github/deploy-doc.sh b/.github/deploy-doc.sh deleted file mode 100755 index cc0dbb9279..0000000000 --- a/.github/deploy-doc.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash -set -e - -VERSION=${1:-"$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)"} -REMOTE=${2:-"origin"} -REMOTE_URL=$(git remote get-url "${REMOTE}") - -echo "Deploying documentation version ${VERSION} to remote ${REMOTE} (${REMOTE_URL})" - -echo "Configuring environment" -cd documentation -pipenv install - -echo "Publishing" -mvn -B clean compile -pipenv run mike deploy --update-aliases --push --remote "${REMOTE}" "${VERSION}" "latest" -pipenv --rm - -echo "Done" diff --git a/.github/deploy.sh b/.github/deploy.sh deleted file mode 100644 index 5e121c36c8..0000000000 --- a/.github/deploy.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env bash -set -e - -export TARGET=$1 - -init_gpg() { - gpg2 --fast-import --no-tty --batch --yes smallrye-sign.asc -} - -init_git() { - git config --global user.name "${GITHUB_ACTOR}" - git config --global user.email "smallrye@googlegroups.com" - - git update-index --assume-unchanged .github/deploy.sh - git update-index --assume-unchanged .github/decrypt-secrets.sh - git update-index --assume-unchanged .github/deploy-doc.sh -} - -compatibility_extract() { - echo "Extracting compatibility report" - jbang .github/CompatibilityUtils.java extract -} - -compatibility_clear() { - echo "Clearing difference justifications" - jbang .github/CompatibilityUtils.java clear - if [[ $(git diff --stat) != '' ]]; then - git add -A - git status - git commit -m "[POST-RELEASE] - Clearing breaking change justifications" - git push origin main - else - echo "No justifications cleared" - fi -} - -deploy_release() { - export RELEASE_VERSION="" - export BRANCH="HEAD" - export NEXT_VERSION="" - - if [ -f /tmp/release-version ]; then - RELEASE_VERSION=$(cat /tmp/release-version) - else - echo "'/tmp/release-version' required" - exit 1 - fi - - echo "Cutting release ${RELEASE_VERSION}" - mvn -B -fn clean - git checkout ${BRANCH} - HASH=$(git rev-parse --verify $BRANCH) - echo "Last commit is ${HASH} - creating detached branch" - git checkout -b "r${RELEASE_VERSION}" "${HASH}" - - echo "Update version to ${RELEASE_VERSION}" - mvn -B versions:set -DnewVersion="${RELEASE_VERSION}" -DgenerateBackupPoms=false -s maven-settings.xml - mvn -B clean verify -DskipTests -Prelease -s maven-settings.xml - - git commit -am "[RELEASE] - Bump version to ${RELEASE_VERSION}" - git tag "${RELEASE_VERSION}" - echo "Pushing tag to origin" - git push origin "${RELEASE_VERSION}" - - echo "Deploying release artifacts" - mvn -B deploy -DskipTests -Prelease -s maven-settings.xml - - echo "Building documentation" - .github/deploy-doc.sh - - if [ -f /tmp/next-version ]; then - NEXT_VERSION=$(cat /tmp/next-version) - echo "Setting main version to ${NEXT_VERSION}-SNAPSHOT" - git reset --hard - git checkout main - mvn -B versions:set -DnewVersion="${NEXT_VERSION}-SNAPSHOT" -DgenerateBackupPoms=false -s maven-settings.xml - git commit -am "[RELEASE] - Bump main branch to ${NEXT_VERSION}-SNAPSHOT" - git push origin main - echo "Main branch updated" - else - echo "No next version - skip updating the main version" - fi -} - -init_git -init_gpg - -if [[ ${TARGET} == "release" ]]; then - echo "Checking release prerequisites" - echo "Milestone set to ${MILESTONE}" - .github/Prelease.java --token="${GITHUB_TOKEN}" --release-version="${MILESTONE}" - - deploy_release - - echo "Executing post-release" - compatibility_extract - .github/PostRelease.java --token="${GITHUB_TOKEN}" - compatibility_clear -else - echo "Unknown environment: ${TARGET}" -fi diff --git a/.github/workflows/build-main-branches.yml b/.github/workflows/build-main-branches.yml index dfd4079f34..80c1c644de 100644 --- a/.github/workflows/build-main-branches.yml +++ b/.github/workflows/build-main-branches.yml @@ -53,7 +53,7 @@ jobs: env: MAVEN_OPTS: ${{ matrix.java.opts }} run: | - mvn -s .github/ci-maven-settings.xml -Dmaven.resolver.transport=wagon -B \ + mvn -s .build/ci-maven-settings.xml -Dmaven.resolver.transport=wagon -B \ clean install -Dtest-containers=true ${{ matrix.java.build_opts }} quality: @@ -76,7 +76,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_LOGIN }} run: | - mvn -s .github/ci-maven-settings.xml -B \ + mvn -s .build/ci-maven-settings.xml -B \ clean install sonar:sonar -Pcoverage \ -Dmaven.resolver.transport=wagon \ -Drevapi.skip=true \ diff --git a/.github/workflows/build-podman.yml b/.github/workflows/build-podman.yml index d39f480099..51704eed1e 100644 --- a/.github/workflows/build-podman.yml +++ b/.github/workflows/build-podman.yml @@ -72,5 +72,5 @@ jobs: env: MAVEN_OPTS: ${{ matrix.java.opts }} run: | - mvn -s .github/ci-maven-settings.xml -Dmaven.resolver.transport=wagon -B \ + mvn -s .build/ci-maven-settings.xml -Dmaven.resolver.transport=wagon -B \ clean install -Dtest-containers=true ${{ matrix.java.build_opts }} diff --git a/.github/workflows/build-pull.yml b/.github/workflows/build-pull.yml index 73532f91e1..64d7e3a451 100644 --- a/.github/workflows/build-pull.yml +++ b/.github/workflows/build-pull.yml @@ -52,7 +52,7 @@ jobs: env: MAVEN_OPTS: ${{ matrix.java.opts }} run: | - mvn -s .github/ci-maven-settings.xml -Dmaven.resolver.transport=wagon \ + mvn -s .build/ci-maven-settings.xml -Dmaven.resolver.transport=wagon \ -B clean install -Pcoverage -Dtest-containers=true ${{ matrix.java.build_opts }} - name: Codecov uses: codecov/codecov-action@v1.0.13 diff --git a/.github/workflows/push-release-to-maven-central.yml b/.github/workflows/push-release-to-maven-central.yml new file mode 100644 index 0000000000..b5d3382f5a --- /dev/null +++ b/.github/workflows/push-release-to-maven-central.yml @@ -0,0 +1,25 @@ +name: Push a release to Maven Central + +on: + push: + tags: + - '4.*' + +jobs: + deploy: + runs-on: ubuntu-latest + env: + SECRET_FILES_PASSPHRASE: ${{ secrets.SECRET_FILES_PASSPHRASE }} + steps: + - name: Git checkout + uses: actions/checkout@v4 + - name: Java setup + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + - name: Install just + uses: taiki-e/install-action@just + - name: Deploy to Maven Central + run: just deploy-to-maven-central diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..0c5465a052 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,58 @@ +name: Release Smallrye Reactive Messaging + +on: + workflow_dispatch: + inputs: + previousVersion: + description: 'Previous version' + required: true + version: + description: 'Release version' + required: true + deployWebsite: + description: 'Shall we deploy the website?' + required: true + default: 'true' + clearRevAPI: + description: 'Shall we clear RevAPI justifications?' + required: true + default: 'true' + +jobs: + release: + runs-on: ubuntu-latest + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} + RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} + SECRET_FILES_PASSPHRASE: ${{ secrets.SECRET_FILES_PASSPHRASE }} + RELEASE_VERSION: ${{ github.event.inputs.version }} + DEPLOY_WEBSITE: ${{ github.event.inputs.deployWebsite }} + CLEAR_REVAPI: ${{ github.event.inputs.clearRevAPI }} + JRELEASER_TAG_NAME: ${{ github.event.inputs.version }} + JRELEASER_PREVIOUS_TAG_NAME: ${{ github.event.inputs.previousVersion }} + JRELEASER_GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} + + steps: + - name: Git checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.RELEASE_TOKEN }} + - name: Java setup + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + - name: Install just + uses: taiki-e/install-action@just + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: 3.x + - name: Install JBang with sdkman + uses: sdkman/sdkman-action@v1 + with: + candidate: jbang + - name: Perform the release steps + run: just perform-release diff --git a/justfile b/justfile new file mode 100755 index 0000000000..0720e12605 --- /dev/null +++ b/justfile @@ -0,0 +1,116 @@ +set shell := ["bash", "-uc"] + +# Just echo the purpose of this file +_default: + @echo "This file is used to automate some release tasks" + @echo "(running in `pwd`)" + @just --list + +# Build locally without tests +build: + @echo "Building locally without tests" + ./mvnw clean install -DskipTests -T1C + +# Build locally with tests +test: + @echo "Testing locally" + ./mvnw clean verify + +# Build on CI without tests +build-ci: + ./mvnw -B -ntp -s .build/ci-maven-settings.xml clean verify -DskipTests + +# Test on CI with tests +test-ci: + ./mvnw -B -ntp -s .build/ci-maven-settings.xml clean verify + +# Perform a release +perform-release: pre-release release post-release + @echo "🎉 Successfully released Smallrye Reactive Messaging ${RELEASE_VERSION} 🚀" + +# Decrypt secrets +decrypt-secrets: + @if [[ -z "${SECRET_FILES_PASSPHRASE}" ]]; then exit 1; fi + @echo "🔐 Decrypting smallrye signature" + gpg --quiet --batch --yes --decrypt --passphrase="${SECRET_FILES_PASSPHRASE}" \ + --output smallrye-sign.asc .build/smallrye-sign.asc.gpg + @echo "🔐 Decrypting Maven settings" + gpg --quiet --batch --yes --decrypt --passphrase="${SECRET_FILES_PASSPHRASE}" \ + --output maven-settings.xml .build/maven-settings.xml.gpg + +# Initialize GnuPG +init-gpg: + @echo "🔐 GnuPG setup" + gpg --fast-import --no-tty --batch --yes smallrye-sign.asc + +# Initialize Git +init-git: + @echo "🔀 Git setup" + git config --global user.name "smallrye-ci" + git config --global user.email "smallrye@googlegroups.com" + +# Steps before releasing +pre-release: decrypt-secrets init-gpg init-git + @echo "🚀 Pre-release steps..." + @if [[ -z "${RELEASE_TOKEN}" ]]; then exit 1; fi + @if [[ -z "${RELEASE_VERSION}" ]]; then exit 1; fi + @echo "Pre-release verifications" + jbang .build/PreRelease.java --token=${RELEASE_TOKEN} --release-version=${RELEASE_VERSION} + @echo "Bump project version to ${RELEASE_VERSION}" + ./mvnw -B -ntp versions:set -DnewVersion=${RELEASE_VERSION} -DgenerateBackupPoms=false -s .build/ci-maven-settings.xml + @echo "Check that the project builds (no tests)" + ./mvnw -B -ntp clean install -Prelease -DskipTests -s maven-settings.xml + @echo "Check that the website builds" + -[[ ${DEPLOY_WEBSITE} == "true" ]] && cd documentation && pipenv install && pipenv run mkdocs build + +# Steps to release +release: pre-release + @echo "🚀 Release steps..." + @if [[ -z "${JRELEASER_TAG_NAME}" ]]; then exit 1; fi + @if [[ -z "${JRELEASER_PREVIOUS_TAG_NAME}" ]]; then exit 1; fi + @if [[ -z "${JRELEASER_GITHUB_TOKEN}" ]]; then exit 1; fi + @echo "Commit release version and push upstream" + git commit -am "[RELEASE] - Bump version to ${RELEASE_VERSION}" + git push + jbang .build/CompatibilityUtils.java extract + @echo "Call JReleaser" + ./mvnw -B -ntp jreleaser:full-release -Pjreleaser -pl :smallrye-reactive-messaging -s .build/ci-maven-settings.xml + @echo "Bump to 999-SNAPSHOT and push upstream" + ./mvnw -B -ntp versions:set -DnewVersion=999-SNAPSHOT -DgenerateBackupPoms=false -s .build/ci-maven-settings.xml + git commit -am "[RELEASE] - Next development version: 999-SNAPSHOT" + git push + +# Deploy to Maven Central +deploy-to-maven-central: decrypt-secrets init-gpg + @echo "🔖 Deploy to Maven Central" + ./mvnw -B -ntp deploy -Prelease -DskipTests -s maven-settings.xml + +# Steps post-release +post-release: + @echo "🚀 Post-release steps..." + -[[ ${CLEAR_REVAPI} == "true" ]] && just clear-revapi + -[[ ${DEPLOY_WEBSITE} == "true" ]] && just deploy-docs + +# Update Pulsar Connector Configuration Documentation +update-pulsar-config-docs: + @echo "📝 Updating Pulsar connector configuration docs" + jbang .build/PulsarConfigDoc.java -d documentation/src/main/docs/pulsar/config + +# Deploy documentation +deploy-docs: + @echo "📝 Deploying documentation to GitHub" + @if [[ -z "${RELEASE_VERSION}" ]]; then exit 1; fi + ./mvnw -B -ntp clean compile -f documentation && cd documentation && pipenv install && pipenv run mike deploy --update-aliases --push --remote origin "${RELEASE_VERSION}" "latest" + +# Clear RevAPI justifications +clear-revapi: + #!/usr/bin/env bash + jbang .build/CompatibilityUtils.java clear + if [[ $(git diff --stat) != '' ]]; then + git add -A + git status + git commit -m "[POST-RELEASE] - Clearing breaking change justifications" + git push + else + echo "No justifications cleared" + fi