From cf64c709e3ed7efe08c02f51624447b903d9cdac Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Fri, 18 Dec 2020 10:06:48 +0100 Subject: [PATCH 1/7] Modernize release process (related to #404): * Switch from KScript to JBang * Update deploy.sh * Use Java 11 for the release build --- .build/Helper.java | 69 ++++++++++++++ .build/PostRelease.java | 148 +++++++++++++++++++++++++++++++ .build/PreRelease.java | 119 +++++++++++++++++++++++++ .build/deploy-site.sh | 2 +- .build/deploy.sh | 51 +++++------ .build/post-release.kts | 119 ------------------------- .build/pre-release.kts | 101 --------------------- .github/workflows/deployment.yml | 8 +- 8 files changed, 367 insertions(+), 250 deletions(-) create mode 100644 .build/Helper.java create mode 100644 .build/PostRelease.java create mode 100644 .build/PreRelease.java delete mode 100755 .build/post-release.kts delete mode 100755 .build/pre-release.kts diff --git a/.build/Helper.java b/.build/Helper.java new file mode 100644 index 000000000..408cc0a4d --- /dev/null +++ b/.build/Helper.java @@ -0,0 +1,69 @@ +package helpers; + +import org.kohsuke.github.GHRelease; +import org.kohsuke.github.GHRepository; +import org.kohsuke.github.GHTag; + +import java.io.IOException; +import java.util.Collection; +import java.util.function.Supplier; + + +public class Helper { + + public static void info(String msg, Object... params) { + System.out.println("\uD83D\uDCAC " + String.format(msg, params)); + } + + public static void completed(String msg, Object... params) { + System.out.println("\uD83C\uDF7A " + String.format(msg, params)); + } + + public static void success(String msg, Object... params) { + System.out.println("\uD83C\uDD97 " + String.format(msg, params)); + } + + public static void warn(String msg, Object... params) { + System.out.println("⚠️️ " + String.format(msg, params)); + } + + public static void fail(String msg, Object... params) { + fail(String.format(msg, params), 2); + } + + public static void fail(String msg, int code) { + System.out.println("☠️ " + msg); + System.exit(code); + } + + public static void failIfTrue(Supplier predicate, String message, Object... params) { + if (predicate.get()) { + fail(message, params); + } + } + + public static T first(Collection collection) { + if (collection.isEmpty()) { + fail("Cannot retrieve the first item from an empty collection"); + } + return collection.iterator().next(); + } + + public static String computeNextVersion(GHRepository repository, GHTag tag, boolean micro) throws IOException { + info("Retrieving release associated with tag %s", tag.getName()); + GHRelease name = repository.getReleaseByTagName(tag.getName()); + if (name == null) { + warn("No release associated with tag %s", tag.getName()); + } + String[] segments = tag.getName().split("\\."); + if (segments.length < 3) { + fail("Invalid version %s, number of segments must be at least 3, found %d", tag.getName(), segments.length); + } + + if (micro) { + return String.format("%s.%s.%d", segments[0], segments[1], Integer.parseInt(segments[2]) + 1); + } else { + return String.format("%s.%d.0", segments[0], Integer.parseInt(segments[1]) + 1); + } + } +} \ No newline at end of file diff --git a/.build/PostRelease.java b/.build/PostRelease.java new file mode 100644 index 000000000..d8ff999b2 --- /dev/null +++ b/.build/PostRelease.java @@ -0,0 +1,148 @@ +///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.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 [--username=cescoffier] [--micro] --release-version=version + *

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

+ * The version is taken from the last tag. + */ +@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 = "--username", description = "The Github username associated with the token", defaultValue = "cescoffier") + private String username; + + @CommandLine.Option(names = "--release-version", description = "Set the released version", required = true) + private String releaseVersion; + + private static final String REPO = "smallrye/smallrye-mutiny"; + + 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, username).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 = getLastTag(tags); + + failIfTrue(() -> tag.getName().equals(releaseVersion), "Unable to find the tag matching the release version %s", + releaseVersion); + success("Tag %s found", tag.getName()); + + // 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 + milestone.close(); + success("Milestone %s 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 + List issues = repository.getIssues(GHIssueState.CLOSED, milestone); + GHRelease release = repository.createRelease(releaseVersion) + .name(releaseVersion) + .body(createReleaseDescription(issues)) + .create(); + success("Release %s created: %s", releaseVersion, release.getHtmlUrl()); + + completed("Post-Release done!"); + + return 0; + } + + private GHTag getLastTag(List tags) { + failIfTrue(tags::isEmpty, "No tags found in repository"); + GHTag tag = first(tags); + assert tag != null; + success("Last tag retrieved: %s", tag.getName()); + return tag; + } + + 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/.build/PreRelease.java b/.build/PreRelease.java new file mode 100644 index 000000000..2ee26f41d --- /dev/null +++ b/.build/PreRelease.java @@ -0,0 +1,119 @@ +///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.nio.file.Files; +import java.util.List; +import java.util.concurrent.Callable; + +import static helpers.Helper.*; + +/** + * Script checking that the repository can be released. + * It checks that: + * - the new version target an existing milestone + * - the milestone has no opened issues + * - the tag does not exist + * + * Run with `./PreRelease.java --token=GITHUB_TOKEN [--username=cescoffier] [--micro] [--release-version=version] + */ +@CommandLine.Command(name = "pre-release", mixinStandardHelpOptions = true, version = "0.1", + description = "Pre-Release Check") +public class PreRelease implements Callable { + + @CommandLine.Option(names = "--token", description = "The Github Token", required = true) + private String token; + + @CommandLine.Option(names = "--username", description = "The Github username associated with the token", defaultValue = "cescoffier") + private String username; + + @CommandLine.Option(names = "--micro", description = "To set the release to be a micro release", defaultValue = "false") + private boolean micro; + + @CommandLine.Option(names = "--release-version", description = "Set the released version - if not set, the version is computed") + private String target; + + private static final String REPO = "smallrye/smallrye-mutiny"; + + public static void main(String... args) { + int exitCode = new CommandLine(new PreRelease()).execute(args); + System.exit(exitCode); + } + + @Override + public Integer call() throws Exception { + if (micro) { + info("Preparing micro release"); + } else { + info("Preparing release"); + } + + GitHub gitHub = new GitHubBuilder().withOAuthToken(token, username).build(); + GHRepository repository = gitHub.getRepository(REPO); + + List tags = repository.listTags().toList(); + List milestones = repository.listMilestones(GHIssueState.ALL).toList(); + + String newVersion; + if (target != null && ! target.trim().isEmpty()) { + newVersion = target; + } else { + info("No version passed, computing new version from the last tag"); + GHTag tag = getLastTag(tags); + newVersion = computeNextVersion(repository, tag, micro); + } + info("Released version would be %s", newVersion); + + info("Running pre-checks..."); + failIfTrue(milestones::isEmpty, "No milestones in repository %s", repository.getName()); + + // Check that the is no tag with the name newVersion + if (tags.stream().anyMatch(t -> t.getName().equals(newVersion))) { + fail("Cannot cut release %s - there is already a tag with this version", newVersion); + } + success("No existing tag named %s", newVersion); + + // Check that there is a milestone named newVersion + GHMilestone milestone = milestones.stream().filter(m -> m.getTitle().equals(newVersion)).findFirst().orElse(null); + if (milestone == null) { + fail("No milestone named %s in repository %s - you need to create one", newVersion, repository.getName()); + } else { + success("Milestone %s found with %d closed issues", + milestone.getTitle(), + milestone.getClosedIssues()); + } + + assert milestone != null; + // Check that all the issues associated with the milestone are closed + failIfTrue(() -> milestone.getOpenIssues() != 0, "The milestone %s has %d opened issues, all the issues " + + "must be closed or moved to another milestone before being able to cut the release. Visit %s for details", + milestone.getTitle(), milestone.getOpenIssues(), milestone.getHtmlUrl().toExternalForm()); + success("No remaining (open) issue associated with the milestone %s", milestone.getTitle()); + + success("Release pre-checks successful!"); + info("Writing release version in the /tmp/release-version file"); + Files.write(new File("/tmp/release-version").toPath(), newVersion.getBytes()); + if (micro) { + //noinspection ResultOfMethodCallIgnored + new File("/tmp/micro").createNewFile(); + } + + completed("Pre-release checks completed!"); + + return 0; + } + + private GHTag getLastTag(List tags) { + failIfTrue(tags::isEmpty, "No tags found in repository"); + GHTag tag = first(tags); + assert tag != null; + success("Last tag retrieved: %s", tag.getName()); + return tag; + } + +} diff --git a/.build/deploy-site.sh b/.build/deploy-site.sh index 6a7d88124..cb02afd8d 100755 --- a/.build/deploy-site.sh +++ b/.build/deploy-site.sh @@ -6,7 +6,7 @@ echo "🍺 Site generated in 'target/_site'" echo "🚧 Cloning web site in target/site" cd target || exit -git clone -b gh-pages "https://cescoffier:${GITHUB_TOKEN}@github.com/smallrye/smallrye-mutiny.git" site +git clone -b gh-pages "https://${GITHUB_ACTOR:-cescoffier}:${GITHUB_TOKEN}@github.com/smallrye/smallrye-mutiny.git" site echo "🚧 Copy content" # shellcheck disable=SC2216 rm -rf site/* diff --git a/.build/deploy.sh b/.build/deploy.sh index 220d79647..df70ed53b 100644 --- a/.build/deploy.sh +++ b/.build/deploy.sh @@ -29,7 +29,8 @@ compatibility_extract() { compatibility_clear() { echo "Clearing difference justifications" jbang .build/CompatibilityUtils.java clear - git commit -a -m "[POST-RELEASE] - Clearing breaking change justifications" + git add -A + git commit -m "[POST-RELEASE] - Clearing breaking change justifications" git push origin master } @@ -67,44 +68,44 @@ deploy_release() { mvn -B clean deploy -DskipTests -Prelease -s maven-settings.xml } +# -------- SCRIPT START HERE ----------------- + init_git init_gpg -export EXTRA_ARGS="" +export EXTRA_PRE_ARGS="" +export EXTRA_POST_ARGS="" if [[ ${MICRO_RELEASE} == "true" ]]; then - EXTRA_ARGS="--micro" + EXTRA_PRE_ARGS="--micro" +fi + +if [[ ! -z "${USER_NAME}" ]]; then + EXTRA_PRE_ARGS="${EXTRA_PRE_ARGS} --username=${USER_NAME}" + EXTRA_POST_ARGS="--username=${USER_NAME}" +fi + +if [[ ! -z "${RELEASE_VERSION}" ]]; then + EXTRA_PRE_ARGS="${EXTRA_PRE_ARGS} --release-version=${RELEASE_VERSION}" fi if [[ ${TARGET} == "snapshot" ]]; then deploy_snapshot elif [[ ${TARGET} == "release" ]]; then - echo "Checking release prerequisites with ${EXTRA_ARGS}" - .build/pre-release.kts "${GITHUB_TOKEN}" "${EXTRA_ARGS}" + jbang .build/PreRelease.java --token="${GITHUB_TOKEN}" "${EXTRA_PRE_ARGS}" - deploy_release + export RELEASE_VERSION="" + if [ -f /tmp/release-version ]; then + RELEASE_VERSION=$(cat /tmp/release-version) + else + echo "'/tmp/release-version' expected after pre-release" + exit 1 + fi - # FIXME This is dirty, we need to revamp the CI https://github.com/smallrye/smallrye-mutiny/issues/404 - # =-------------------------- - echo "Install sdkman JDKs" - source ~/.sdkman/bin/sdkman-init.sh - sed -ie 's/sdkman_auto_answer=false/sdkman_auto_answer=true/g' ~/.sdkman/etc/config - sdk install java 11.0.9.hs-adpt - sdk install java 8.0.275.hs-adpt + deploy_release - echo "Run compatibility_extract" - sdk use java 11.0.9.hs-adpt compatibility_extract - - sdk use java 8.0.275.hs-adpt - echo "Executing post-release" - .build/post-release.kts "${GITHUB_TOKEN}" - - echo "Run compatibility_clear" - sdk use java 11.0.9.hs-adpt + jbang .build/PostRelease.java --token="${GITHUB_TOKEN}" --release-version="${RELEASE_VERSION}" "${EXTRA_POST_ARGS}" compatibility_clear - - sdk use java 8.0.275.hs-adpt - # =-------------------------- else echo "Unknown environment: ${TARGET}" fi diff --git a/.build/post-release.kts b/.build/post-release.kts deleted file mode 100755 index e42e75038..000000000 --- a/.build/post-release.kts +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env kscript -@file:MavenRepository("jcenter","https://jcenter.bintray.com/") -@file:MavenRepository("maven-central","https://repo1.maven.org/maven2/") -@file:DependsOn("org.kohsuke:github-api:1.101") - -/** - * 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 `./post-release.kts ${GIHUB_TOKEN} - * - * 1. The github token is mandatory. - * - * The version is taken from the last tag. - */ - -import org.kohsuke.github.* -import java.io.File - -val token = args[0]; // Fail if the first argument is not there - -val github = GitHubBuilder().withOAuthToken(token).build() -val repository = github.getRepository("smallrye/smallrye-mutiny") - -println("Listing tags of ${repository.getName()}") -val tags = repository.listTags().asList() -failIfPredicate({ tags.isNullOrEmpty() }, "No tags in repository ${repository.getName()}") -val tag = tags.first() -println("Last tag is " + tag.getName()) - -val version = tag.getName() -val nextVersion = nextVersion(tag) -println("Next version would be ${nextVersion}") - -val milestones = repository.listMilestones(GHIssueState.ALL).asList() -failIfPredicate({ milestones.isNullOrEmpty() }, "No milestones in repository ${repository.getName()}") - -println("Retrieving milestone ${version}") -val milestone = milestones.find { it.getTitle() == version} -val nextMilestone = milestones.find { it.getTitle() == nextVersion} - -failIfPredicate({ milestone == null }, "Unable to find milestone ${version}") - -println("Closing milestone ${version}") -milestone!!.close(); -println("Milestone ${version} closed - ${milestone?.getHtmlUrl()}") - -if (nextMilestone != null) { - println("Milestone ${nextVersion} already created - ${nextMilestone?.getHtmlUrl()}") -} else { - val newMilestone = repository.createMilestone(nextVersion, null) - println("Milestone ${nextVersion} created - ${newMilestone.getHtmlUrl()}") -} - -val issues = repository.getIssues(GHIssueState.CLOSED, milestone) - -val release = repository.createRelease(version) - .name(version) - .body(createReleaseDescription(issues)) - .create() -println("Release ${version} created - ${release.getHtmlUrl()}") - -fun GHIssue.line() : String { - var title = this.getTitle(); - return "[${this.getNumber()}] ${title}" -} - -fun GHIssue.markdown() : String { - var title = this.getTitle(); - return "[#${this.getNumber()}](${this.getHtmlUrl()}) - ${title}" -} - -fun createReleaseDescription(issues : List) : String { - val file = File("target/differences.md") - var compatibility = "" - if (file.isFile) { - compatibility = file.readText(Charsets.UTF_8) - } - var desc = """### Changelog - | - |${issues.joinToString(separator="\n", transform={ " * ${it.markdown()}" })} - | - | - """.trimMargin("|") - - desc = desc.plus(compatibility).plus("\n") - - return desc -} - -fun nextVersion(tag: GHTag) : String { - return computeNewVersion(tag.getName()) -} - -fun fail(message: String, code: Int = 2) { - println(message) - kotlin.system.exitProcess(code) -} - -fun failIfPredicate(predicate: () -> Boolean, message: String, code: Int = 2) { - val success = predicate.invoke() - if (success) { - fail(message, code) - } -} - -fun computeNewVersion(last : String) : String { - val segments = last.split(".") - if (segments.size < 3) { - fail("Invalid version ${last}, number of segments must be at least 3, found: ${segments.size}") - } - var newVersion = "${segments[0]}.${segments[1].toInt() + 1}.0" - return newVersion -} - - diff --git a/.build/pre-release.kts b/.build/pre-release.kts deleted file mode 100755 index 24c87a619..000000000 --- a/.build/pre-release.kts +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env kscript -@file:MavenRepository("jcenter","https://jcenter.bintray.com/") -@file:MavenRepository("maven-central","https://repo1.maven.org/maven2/") -@file:DependsOn("org.kohsuke:github-api:1.101") - -/** - * Script checking that the repository can be released. - * It checks that: - * - the new version target an existing milestone - * - the milestone has no opened issues - * - the tag does not exist - * - * Run with `./pre-release.kts ${GIHUB_TOKEN} [--micro] - * - * 1. The github token is mandatory. - * 2. The `--micro` is used to compute the increment. If omitted, the minor digit is incremented. - */ - -import org.kohsuke.github.* -import java.io.File - -val REPO = "smallrye/smallrye-mutiny" - -val token = args[0]; // Fail if the first argument is not there -var micro = false -for (arg in args) { - if (arg == "--micro") { - micro = true; - } -} - -if (micro) { - println("Micro enabled") -} - -val github = GitHubBuilder().withOAuthToken(token).build() -val repository = github.getRepository(REPO) -println("Listing tags of ${repository.getName()}") -val tags = repository.listTags().asList() -failIfPredicate({ tags.isNullOrEmpty() }, "No tags in repository ${repository.getName()}") -val tag = tags.first() -println("Last tag is " + tag.getName()) - -val newVersion = nextVersion(tag); -println("New version would be ${newVersion}") -val milestones = repository.listMilestones(GHIssueState.ALL).asList() -failIfPredicate({ milestones.isNullOrEmpty() }, "No milestones in repository ${repository.getName()}") - -println("Checking the absence of the ${newVersion} tag") -val existingTag = tags.find { it.getName() == newVersion} -failIfPredicate( { existingTag != null }, "There is a tag with name ${newVersion}, invalid increment") - -println("Checking the existence of the ${newVersion} milestone") -val existingMilestone = milestones.find { it.getTitle() == newVersion} -failIfPredicate( { existingMilestone == null }, "There is no milestone with name ${newVersion}, invalid increment") - -println("Checking the number of open issues in the milestone ${newVersion}") -failIfPredicate( { existingMilestone?.getOpenIssues() != 0 }, "The milestone ${newVersion} has ${existingMilestone?.getOpenIssues()} issues opened, check check ${existingMilestone?.getHtmlUrl()}") - -println("Write computed version to /tmp/release-version") -File("/tmp/release-version").writeText(newVersion) - -if (micro) { - File("/tmp/micro").createNewFile() -} - -fun nextVersion(tag: GHTag) : String { - // Retrieve the associated release - val release = repository.getReleaseByTagName(tag.getName()); - if (release == null) { - println("[WARNING] No release associated with tag ${tag.getName()}") - } - // All good, compute new version. - return computeNewVersion(tag.getName(), micro) -} - -fun fail(message: String, code: Int = 2) { - println(message) - kotlin.system.exitProcess(code) -} - -fun failIfPredicate(predicate: () -> Boolean, message: String, code: Int = 2) { - val success = predicate.invoke() - if (success) { - fail(message, code) - } -} - -fun computeNewVersion(last : String, micro: Boolean) : String { - val segments = last.split(".") - if (segments.size < 3) { - fail("Invalid version ${last}, number of segments must be at least 3, found: ${segments.size}") - } - var newVersion = "${segments[0]}.${segments[1].toInt() + 1}.0" - if (micro) { - newVersion = "${segments[0]}.${segments[1]}.${segments[2].toInt() + 1}" - } - return newVersion -} - - diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 9e128de7c..d6504e9b6 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -14,16 +14,18 @@ jobs: with: ref: 'master' token: ${{ secrets.GITHUB_API_TOKEN }} - - name: Install JDK 1.8 + - name: Install JDK 11 uses: AdoptOpenJDK/install-jdk@v1 with: - version: 8 + version: 11 - name: 'Run deployment for ${{ github.event.deployment.environment }}' env: GITHUB_TOKEN: ${{ secrets.GITHUB_API_TOKEN }} SECRET_FILES_PASSPHRASE: ${{ secrets.SECRET_FILES_PASSPHRASE }} MICRO_RELEASE: ${{ github.event.deployment.payload.micro }} + RELEASE_VERSION: ${{ github.event.deployment.payload.release-version }} + USER_NAME: ${{ github.event.deployment.payload.username }} run: | sudo apt-get update -o Dir::Etc::sourcelist="sources.list" \ -o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0" @@ -32,8 +34,6 @@ jobs: echo Installing SDKMAN curl -s "https://get.sdkman.io" | bash source ~/.sdkman/bin/sdkman-init.sh && \ - sdk install kotlin 1.3.61 && \ - sdk install kscript 2.9.0 && \ sdk install jbang chmod +x .build/deploy.sh .build/decrypt-secrets.sh From 7889883f97b80092b5f5993001c3bcc4e759fce8 Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Fri, 18 Dec 2020 10:18:36 +0100 Subject: [PATCH 2/7] Make sure the output classes are using Java 8 bytecode --- pom.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pom.xml b/pom.xml index 8d9916325..25d51d205 100644 --- a/pom.xml +++ b/pom.xml @@ -88,6 +88,9 @@ 0.12.1 true + + 1.8 + 1.8 From 189bea60e93a56dd2045cb53a8d6bbb46a2639af Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Fri, 18 Dec 2020 10:35:27 +0100 Subject: [PATCH 3/7] Deploy snapshots after successful master build --- .github/workflows/build-master.yml | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-master.yml b/.github/workflows/build-master.yml index 05609e616..79af0b001 100644 --- a/.github/workflows/build-master.yml +++ b/.github/workflows/build-master.yml @@ -14,9 +14,9 @@ jobs: strategy: matrix: java: [ - {'version': '8', 'source': 'releases'}, - {'version': '11', 'source': 'releases'}, - {'version': '14', 'source': 'releases'} + {'version': '8', 'source': 'releases'}, + {'version': '11', 'source': 'releases'}, + {'version': '14', 'source': 'releases'} ] name: Build with Java ${{ matrix.java.version }} (OpenJDK) steps: @@ -49,6 +49,27 @@ jobs: - name: Build with Maven run: mvn -B clean verify + snapshot: + needs: build + runs-on: ubuntu-20.04 + steps: + - uses: actions/cache@v2 + with: + path: ~/.m2/repository + key: maven-11 + - uses: actions/checkout@v2 + - name: Install JDK 11 + uses: AdoptOpenJDK/install-jdk@v1 + with: + version: 11 + - name: Deploy snapshots + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + chmod +x .build/deploy.sh .build/decrypt-secrets.sh + .build/decrypt-secrets.sh + .build/deploy.sh snapshot + quality: needs: build runs-on: ubuntu-20.04 @@ -93,4 +114,4 @@ jobs: version: 11 source: releases - name: Compatibility Check - run: mvn -B install revapi:check@check-compatibility -DskipTests -fae \ No newline at end of file + run: mvn -B install revapi:check@check-compatibility -DskipTests -fae From f0fe990417386644a6243b1f230b5bcdccf77ae2 Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Fri, 18 Dec 2020 15:03:24 +0100 Subject: [PATCH 4/7] New release-prepare workflow to cut a release. The workflow runs the pre-checks, bump the version and push the tag. --- .build/Helper.java | 10 +++---- .build/cut-release.sh | 69 +++++++++++++++++++++++++++++++++++++++++++ pom.xml | 44 +++++++++++++++++---------- 3 files changed, 103 insertions(+), 20 deletions(-) create mode 100755 .build/cut-release.sh diff --git a/.build/Helper.java b/.build/Helper.java index 408cc0a4d..91400e7e3 100644 --- a/.build/Helper.java +++ b/.build/Helper.java @@ -12,19 +12,19 @@ public class Helper { public static void info(String msg, Object... params) { - System.out.println("\uD83D\uDCAC " + String.format(msg, params)); + System.out.println("[INFO] " + String.format(msg, params)); } public static void completed(String msg, Object... params) { - System.out.println("\uD83C\uDF7A " + String.format(msg, params)); + System.out.println("[DONE] " + String.format(msg, params)); } public static void success(String msg, Object... params) { - System.out.println("\uD83C\uDD97 " + String.format(msg, params)); + System.out.println("[WARN] " + String.format(msg, params)); } public static void warn(String msg, Object... params) { - System.out.println("⚠️️ " + String.format(msg, params)); + System.out.println("[WARN] " + String.format(msg, params)); } public static void fail(String msg, Object... params) { @@ -32,7 +32,7 @@ public static void fail(String msg, Object... params) { } public static void fail(String msg, int code) { - System.out.println("☠️ " + msg); + System.out.println("[FAIL] " + msg); System.exit(code); } diff --git a/.build/cut-release.sh b/.build/cut-release.sh new file mode 100755 index 000000000..e38791ebe --- /dev/null +++ b/.build/cut-release.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +set -e + +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 .build/deploy.sh + git update-index --assume-unchanged .build/decrypt-secrets.sh +} + +# -------- SCRIPT START HERE ----------------- + +init_git +init_gpg + +export VERSION="" +export BRANCH="HEAD" +export EXTRA_PRE_ARGS="" + +if [[ ${MICRO_RELEASE} == "true" ]]; then + EXTRA_PRE_ARGS="--micro" +fi + +if [[ ! -z "${USER_NAME}" ]]; then + EXTRA_PRE_ARGS="${EXTRA_PRE_ARGS} --username=${USER_NAME}" +fi + +if [[ ! -z "${RELEASE_VERSION}" ]]; then + EXTRA_PRE_ARGS="${EXTRA_PRE_ARGS} --release-version=${RELEASE_VERSION}" +fi + +jbang .build/PreRelease.java --token="${GITHUB_TOKEN}" ${EXTRA_PRE_ARGS} + +export VERSION="" +if [ -f /tmp/release-version ]; then + VERSION=$(cat /tmp/release-version) +else + echo "'/tmp/release-version' expected after pre-release" + exit 1 +fi + +echo "Cutting 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${VERSION}" "${HASH}" + +echo "Update version to ${VERSION}" +mvn -B versions:set -DnewVersion="${VERSION}" -DgenerateBackupPoms=false -s maven-settings.xml + +if [[ ${SKIP_TESTS} == "true" ]]; then + mvn -B clean verify -Prelease -DskipTests -s maven-settings.xml +else + mvn -B clean verify -Prelease -s maven-settings.xml +fi + +echo "Update website version to ${VERSION}" +sed -ie "s/mutiny_version: .*/mutiny_version: ${VERSION}/g" documentation/src/main/jekyll/_data/versions.yml + +git commit -am "[RELEASE] - Bump version to ${VERSION}" +git tag "${VERSION}" +echo "Pushing tag to origin" +# git push origin "${VERSION}" \ No newline at end of file diff --git a/pom.xml b/pom.xml index 25d51d205..d596d9fc4 100644 --- a/pom.xml +++ b/pom.xml @@ -337,21 +337,6 @@ org.apache.maven.plugins maven-gpg-plugin - - - sign-artifacts - verify - - sign - - - - --pinentry-mode - loopback - - - - @@ -372,5 +357,34 @@ + + + + [9,) + + java-9+ + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + default-cli + + aggregate + + + + + + 8 + 8 + false + + + + + From f592a2cb7f99d5a8a1ab82bc5644e11a41375741 Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Sat, 19 Dec 2020 08:43:29 +0100 Subject: [PATCH 5/7] Configure the maven javadoc plugin to work with java 9+ Yeah, the wonderful of modules... --- implementation/pom.xml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/implementation/pom.xml b/implementation/pom.xml index 15611f398..450dc0273 100644 --- a/implementation/pom.xml +++ b/implementation/pom.xml @@ -117,4 +117,35 @@ + + + + + [9,) + + java-9+ + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + attach-javadocs + + jar + + + + + + 8 + 8 + false + + + + + + \ No newline at end of file From 2a69d5355efa2f0bf34543588b158af24e171d0a Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Sat, 19 Dec 2020 18:45:54 +0100 Subject: [PATCH 6/7] Provides the _after tag_ workflows to finalize the release. This includes: * post-release task (Github release, release notes...) * website deployment * deployment of the artifacts to Maven Central --- .build/PostRelease.java | 62 ++++++++------ .build/PreRelease.java | 7 +- .build/cut-release.sh | 17 ++-- .build/decrypt-secrets.sh | 0 .build/deploy-release.sh | 22 +++++ .build/deploy-site.sh | 2 +- .build/deploy-snapshot.sh | 22 +++++ .build/deploy.sh | 111 -------------------------- .build/tag-creation-event.json | 5 ++ .github/workflows/build-master.yml | 3 +- .github/workflows/deploy-release.yml | 41 ++++++++++ .github/workflows/deploy-site.yml | 46 +++++++++++ .github/workflows/deployment.yml | 58 -------------- .github/workflows/post-release.yml | 48 +++++++++++ .github/workflows/prepare-release.yml | 89 +++++++++++++++++++++ 15 files changed, 325 insertions(+), 208 deletions(-) mode change 100644 => 100755 .build/decrypt-secrets.sh create mode 100755 .build/deploy-release.sh create mode 100755 .build/deploy-snapshot.sh delete mode 100644 .build/deploy.sh create mode 100644 .build/tag-creation-event.json create mode 100644 .github/workflows/deploy-release.yml create mode 100644 .github/workflows/deploy-site.yml delete mode 100644 .github/workflows/deployment.yml create mode 100644 .github/workflows/post-release.yml create mode 100644 .github/workflows/prepare-release.yml diff --git a/.build/PostRelease.java b/.build/PostRelease.java index d8ff999b2..b3c59d53b 100644 --- a/.build/PostRelease.java +++ b/.build/PostRelease.java @@ -11,6 +11,7 @@ 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; @@ -23,11 +24,11 @@ * - 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 [--username=cescoffier] [--micro] --release-version=version + * Run with `./PostRelease.java --token=GITHUB_TOKEN --release-version=version *

* 1. The github token is mandatory. *

- * The version is taken from the last tag. + * 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") @@ -36,9 +37,6 @@ public class PostRelease implements Callable { @CommandLine.Option(names = "--token", description = "The Github Token", required = true) private String token; - @CommandLine.Option(names = "--username", description = "The Github username associated with the token", defaultValue = "cescoffier") - private String username; - @CommandLine.Option(names = "--release-version", description = "Set the released version", required = true) private String releaseVersion; @@ -51,7 +49,7 @@ public static void main(String... args) { @Override public Integer call() throws Exception { - GitHub gitHub = new GitHubBuilder().withOAuthToken(token, username).build(); + GitHub gitHub = new GitHubBuilder().withOAuthToken(token).build(); GHRepository repository = gitHub.getRepository(REPO); List tags = repository.listTags().toList(); @@ -60,11 +58,9 @@ public Integer call() throws Exception { info("Running post-release checks for release %s", releaseVersion); // Check that the tag associated with the release version exists - GHTag tag = getLastTag(tags); + GHTag tag = getTag(releaseVersion, tags); - failIfTrue(() -> tag.getName().equals(releaseVersion), "Unable to find the tag matching the release version %s", - releaseVersion); - success("Tag %s found", tag.getName()); + 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() @@ -77,8 +73,12 @@ public Integer call() throws Exception { info("Starting post-release tasks"); // Close milestone - milestone.close(); - success("Milestone %s closed (%s)", milestone.getTitle(), milestone.getHtmlUrl()); + 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); @@ -94,25 +94,41 @@ public Integer call() throws Exception { success("Next milestone (%s) created: %s", nextMilestone.getTitle(), nextMilestone.getHtmlUrl()); } - // Compute the release notes + // Compute the release notes and create releases List issues = repository.getIssues(GHIssueState.CLOSED, milestone); - GHRelease release = repository.createRelease(releaseVersion) - .name(releaseVersion) - .body(createReleaseDescription(issues)) - .create(); - success("Release %s created: %s", releaseVersion, release.getHtmlUrl()); + 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 getLastTag(List tags) { + private GHTag getTag(String releaseVersion, List tags) { failIfTrue(tags::isEmpty, "No tags found in repository"); - GHTag tag = first(tags); - assert tag != null; - success("Last tag retrieved: %s", tag.getName()); - return tag; + 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) { diff --git a/.build/PreRelease.java b/.build/PreRelease.java index 2ee26f41d..bcd573f0f 100644 --- a/.build/PreRelease.java +++ b/.build/PreRelease.java @@ -20,7 +20,7 @@ * - the milestone has no opened issues * - the tag does not exist * - * Run with `./PreRelease.java --token=GITHUB_TOKEN [--username=cescoffier] [--micro] [--release-version=version] + * Run with `./PreRelease.java --token=GITHUB_TOKEN [--micro] [--release-version=version] */ @CommandLine.Command(name = "pre-release", mixinStandardHelpOptions = true, version = "0.1", description = "Pre-Release Check") @@ -29,9 +29,6 @@ public class PreRelease implements Callable { @CommandLine.Option(names = "--token", description = "The Github Token", required = true) private String token; - @CommandLine.Option(names = "--username", description = "The Github username associated with the token", defaultValue = "cescoffier") - private String username; - @CommandLine.Option(names = "--micro", description = "To set the release to be a micro release", defaultValue = "false") private boolean micro; @@ -53,7 +50,7 @@ public Integer call() throws Exception { info("Preparing release"); } - GitHub gitHub = new GitHubBuilder().withOAuthToken(token, username).build(); + GitHub gitHub = new GitHubBuilder().withOAuthToken(token).build(); GHRepository repository = gitHub.getRepository(REPO); List tags = repository.listTags().toList(); diff --git a/.build/cut-release.sh b/.build/cut-release.sh index e38791ebe..e9e234920 100755 --- a/.build/cut-release.sh +++ b/.build/cut-release.sh @@ -8,9 +8,6 @@ init_gpg() { init_git() { git config --global user.name "${GITHUB_ACTOR}" git config --global user.email "smallrye@googlegroups.com" - - git update-index --assume-unchanged .build/deploy.sh - git update-index --assume-unchanged .build/decrypt-secrets.sh } # -------- SCRIPT START HERE ----------------- @@ -22,12 +19,12 @@ export VERSION="" export BRANCH="HEAD" export EXTRA_PRE_ARGS="" -if [[ ${MICRO_RELEASE} == "true" ]]; then - EXTRA_PRE_ARGS="--micro" +if [[ ${DRY_RUN} == "true" ]]; then + echo "[DRY RUN] - Dry Run Enabled - Git push will be skipped." fi -if [[ ! -z "${USER_NAME}" ]]; then - EXTRA_PRE_ARGS="${EXTRA_PRE_ARGS} --username=${USER_NAME}" +if [[ ${MICRO_RELEASE} == "true" ]]; then + EXTRA_PRE_ARGS="--micro" fi if [[ ! -z "${RELEASE_VERSION}" ]]; then @@ -66,4 +63,8 @@ sed -ie "s/mutiny_version: .*/mutiny_version: ${VERSION}/g" documentation/src/ma git commit -am "[RELEASE] - Bump version to ${VERSION}" git tag "${VERSION}" echo "Pushing tag to origin" -# git push origin "${VERSION}" \ No newline at end of file +if [[ ${DRY_RUN} == "true" ]]; then + echo "[DRY RUN] - Skipping push: git push origin ${VERSION}" +else + git push origin "${VERSION}" +fi \ No newline at end of file diff --git a/.build/decrypt-secrets.sh b/.build/decrypt-secrets.sh old mode 100644 new mode 100755 diff --git a/.build/deploy-release.sh b/.build/deploy-release.sh new file mode 100755 index 000000000..abacbcafb --- /dev/null +++ b/.build/deploy-release.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -e + +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" +} + +# -------- SCRIPT START HERE ----------------- + +init_git +init_gpg + +git fetch origin --tags +git reset --hard +git checkout "${REF}" +mvn -B clean deploy -DskipTests -Prelease -s maven-settings.xml + diff --git a/.build/deploy-site.sh b/.build/deploy-site.sh index cb02afd8d..c226f67bc 100755 --- a/.build/deploy-site.sh +++ b/.build/deploy-site.sh @@ -6,7 +6,7 @@ echo "🍺 Site generated in 'target/_site'" echo "🚧 Cloning web site in target/site" cd target || exit -git clone -b gh-pages "https://${GITHUB_ACTOR:-cescoffier}:${GITHUB_TOKEN}@github.com/smallrye/smallrye-mutiny.git" site +git clone -b gh-pages "https://${GITHUB_ACTOR}:${GITHUB_TOKEN}@github.com/smallrye/smallrye-mutiny.git" site echo "🚧 Copy content" # shellcheck disable=SC2216 rm -rf site/* diff --git a/.build/deploy-snapshot.sh b/.build/deploy-snapshot.sh new file mode 100755 index 000000000..70d76d9c2 --- /dev/null +++ b/.build/deploy-snapshot.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -e + +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" +} + +# -------- SCRIPT START HERE ----------------- + +init_git +init_gpg + +git fetch origin --tags +git reset --hard +git checkout master +mvn -B clean deploy -DskipTests -Prelease -s maven-settings.xml + diff --git a/.build/deploy.sh b/.build/deploy.sh deleted file mode 100644 index df70ed53b..000000000 --- a/.build/deploy.sh +++ /dev/null @@ -1,111 +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 .build/deploy.sh - git update-index --assume-unchanged .build/decrypt-secrets.sh -} - -deploy_snapshot() { - echo "Deploying snapshot" - mvn -B -fn clean - mvn -B deploy -DskipTests -s maven-settings.xml -} - -compatibility_extract() { - echo "Extracting compatibility report" - jbang .build/CompatibilityUtils.java extract -} - -compatibility_clear() { - echo "Clearing difference justifications" - jbang .build/CompatibilityUtils.java clear - git add -A - git commit -m "[POST-RELEASE] - Clearing breaking change justifications" - git push origin master -} - -deploy_release() { - export RELEASE_VERSION="" - export BRANCH="HEAD" - - 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 - - echo "Update website version to ${RELEASE_VERSION}" - sed -ie "s/mutiny_version: .*/mutiny_version: ${RELEASE_VERSION}/g" documentation/src/main/jekyll/_data/versions.yml - - 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 clean deploy -DskipTests -Prelease -s maven-settings.xml -} - -# -------- SCRIPT START HERE ----------------- - -init_git -init_gpg - -export EXTRA_PRE_ARGS="" -export EXTRA_POST_ARGS="" -if [[ ${MICRO_RELEASE} == "true" ]]; then - EXTRA_PRE_ARGS="--micro" -fi - -if [[ ! -z "${USER_NAME}" ]]; then - EXTRA_PRE_ARGS="${EXTRA_PRE_ARGS} --username=${USER_NAME}" - EXTRA_POST_ARGS="--username=${USER_NAME}" -fi - -if [[ ! -z "${RELEASE_VERSION}" ]]; then - EXTRA_PRE_ARGS="${EXTRA_PRE_ARGS} --release-version=${RELEASE_VERSION}" -fi - -if [[ ${TARGET} == "snapshot" ]]; then - deploy_snapshot -elif [[ ${TARGET} == "release" ]]; then - jbang .build/PreRelease.java --token="${GITHUB_TOKEN}" "${EXTRA_PRE_ARGS}" - - export RELEASE_VERSION="" - if [ -f /tmp/release-version ]; then - RELEASE_VERSION=$(cat /tmp/release-version) - else - echo "'/tmp/release-version' expected after pre-release" - exit 1 - fi - - deploy_release - - compatibility_extract - jbang .build/PostRelease.java --token="${GITHUB_TOKEN}" --release-version="${RELEASE_VERSION}" "${EXTRA_POST_ARGS}" - compatibility_clear -else - echo "Unknown environment: ${TARGET}" -fi diff --git a/.build/tag-creation-event.json b/.build/tag-creation-event.json new file mode 100644 index 000000000..c8428a2a2 --- /dev/null +++ b/.build/tag-creation-event.json @@ -0,0 +1,5 @@ +{ + "ref": "0.12.5", + "ref_type": "tag", + "master_branch": "master" +} \ No newline at end of file diff --git a/.github/workflows/build-master.yml b/.github/workflows/build-master.yml index 79af0b001..4a1972478 100644 --- a/.github/workflows/build-master.yml +++ b/.github/workflows/build-master.yml @@ -66,9 +66,8 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - chmod +x .build/deploy.sh .build/decrypt-secrets.sh .build/decrypt-secrets.sh - .build/deploy.sh snapshot + .build/deploy-snapshot.sh quality: needs: build diff --git a/.github/workflows/deploy-release.yml b/.github/workflows/deploy-release.yml new file mode 100644 index 000000000..81fa8c5f8 --- /dev/null +++ b/.github/workflows/deploy-release.yml @@ -0,0 +1,41 @@ +--- +name: Maven Central +on: + create: + branches: + - "!*" + tags: + - '[0-9]+.[0-9]+.[0-9]+*' + +jobs: + perform-release: + runs-on: ubuntu-latest + steps: + - uses: actions/cache@v2 + with: + path: ~/.m2/repository + key: maven-11 + - uses: actions/checkout@v2 + with: + ref: ${{ github.event.ref }} + token: ${{ secrets.GITHUB_API_TOKEN }} + - name: Install JDK 11 + uses: AdoptOpenJDK/install-jdk@v1 + with: + version: 11 + - name: 'Deploy' + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + GITHUB_TOKEN: ${{ secrets.GITHUB_API_TOKEN }} + REF: ${{ github.event.ref }} + SECRET_FILES_PASSPHRASE: ${{ secrets.SECRET_FILES_PASSPHRASE }} + run: | + + echo "Cutting release from ${REF}" + + sudo apt-get update -o Dir::Etc::sourcelist="sources.list" \ + -o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0" + sudo apt-get install -y gnupg2 gnupg-agent + + .build/decrypt-secrets.sh + .build/deploy-release.sh diff --git a/.github/workflows/deploy-site.yml b/.github/workflows/deploy-site.yml new file mode 100644 index 000000000..a9fd3bd7b --- /dev/null +++ b/.github/workflows/deploy-site.yml @@ -0,0 +1,46 @@ +--- +name: Web Site +on: + create: + branches: + - "!*" + tags: + - '[0-9]+.[0-9]+.[0-9]+*' + +jobs: + deploy-web-site: + runs-on: ubuntu-latest + steps: + - uses: actions/cache@v2 + with: + path: ~/.m2/repository + key: maven-11 + - uses: actions/checkout@v2 + with: + ref: ${{ github.event.ref }} + token: ${{ secrets.GITHUB_API_TOKEN }} + - name: Install JDK 11 + uses: AdoptOpenJDK/install-jdk@v1 + with: + version: 11 + - name: 'Setup Ruby' + uses: actions/setup-ruby@v1 + with: + ruby-version: '2.6' + - name: 'Web Site' + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + GITHUB_TOKEN: ${{ secrets.GITHUB_API_TOKEN }} + REF: ${{ github.event.ref }} + SECRET_FILES_PASSPHRASE: ${{ secrets.SECRET_FILES_PASSPHRASE }} + run: | + + echo "Deploying web site for tag ${REF}" + + sudo apt-get update -o Dir::Etc::sourcelist="sources.list" \ + -o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0" + sudo apt-get install -y gnupg2 gnupg-agent + + git config --global user.name "${GITHUB_ACTOR}" + git config --global user.email "smallrye@googlegroups.com" + .build/deploy-site.sh diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml deleted file mode 100644 index d6504e9b6..000000000 --- a/.github/workflows/deployment.yml +++ /dev/null @@ -1,58 +0,0 @@ ---- -name: Deployment -on: [deployment] - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/cache@v2 - with: - path: ~/.m2/repository - key: maven-8 - - uses: actions/checkout@v2 - with: - ref: 'master' - token: ${{ secrets.GITHUB_API_TOKEN }} - - name: Install JDK 11 - uses: AdoptOpenJDK/install-jdk@v1 - with: - version: 11 - - - name: 'Run deployment for ${{ github.event.deployment.environment }}' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_API_TOKEN }} - SECRET_FILES_PASSPHRASE: ${{ secrets.SECRET_FILES_PASSPHRASE }} - MICRO_RELEASE: ${{ github.event.deployment.payload.micro }} - RELEASE_VERSION: ${{ github.event.deployment.payload.release-version }} - USER_NAME: ${{ github.event.deployment.payload.username }} - run: | - sudo apt-get update -o Dir::Etc::sourcelist="sources.list" \ - -o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0" - sudo apt-get install -y gnupg2 gnupg-agent - - echo Installing SDKMAN - curl -s "https://get.sdkman.io" | bash - source ~/.sdkman/bin/sdkman-init.sh && \ - sdk install jbang - - chmod +x .build/deploy.sh .build/decrypt-secrets.sh - - .build/decrypt-secrets.sh - - .build/deploy.sh ${{ github.event.deployment.environment }} - - - name: 'Setup Ruby' - uses: actions/setup-ruby@v1 - with: - ruby-version: '2.6' - - - name: 'Deploy the website' - run: .build/deploy-site.sh - - - name: update deploy status - if: always() - uses: unacast/actions-github-deployment-status@0.2 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - status: ${{ job.status }} diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml new file mode 100644 index 000000000..a5eb0d824 --- /dev/null +++ b/.github/workflows/post-release.yml @@ -0,0 +1,48 @@ +--- +name: Post-Release +on: + create: + branches: + - "!*" + tags: + - '[0-9]+.[0-9]+.[0-9]+*' + +jobs: + post-release: + runs-on: ubuntu-latest + steps: + - uses: actions/cache@v2 + with: + path: ~/.m2/repository + key: maven-11 + - uses: actions/checkout@v2 + with: + ref: ${{ github.event.ref }} + token: ${{ secrets.GITHUB_API_TOKEN }} + - name: Install JDK 11 + uses: AdoptOpenJDK/install-jdk@v1 + with: + version: 11 + - name: 'Collect Compatibility Justification' + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + GITHUB_TOKEN: ${{ secrets.GITHUB_API_TOKEN }} + SECRET_FILES_PASSPHRASE: ${{ secrets.SECRET_FILES_PASSPHRASE }} + run: | + sudo apt-get update -o Dir::Etc::sourcelist="sources.list" \ + -o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0" + sudo apt-get install -y gnupg2 gnupg-agent + + curl -s "https://get.sdkman.io" | bash + source ~/.sdkman/bin/sdkman-init.sh && \ + sdk install jbang + + jbang .build/CompatibilityUtils.java extract + - name: 'Post-Release Tasks' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_API_TOKEN }} + REF: ${{ github.event.ref }} + run: | + git checkout new-release-script # TODO Change it back to master + source ~/.sdkman/bin/sdkman-init.sh && \ + jbang .build/PostRelease.java --release-version=${REF} --token=${GITHUB_TOKEN} diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml new file mode 100644 index 000000000..757533851 --- /dev/null +++ b/.github/workflows/prepare-release.yml @@ -0,0 +1,89 @@ +--- +name: Release +on: + workflow_dispatch: + inputs: + micro: + description: 'Is it a micro release (used to compute release version)' + required: false + default: false + release-version: + description: 'The release version, if not set it computes the version automatically' + required: false + skip-tests: + description: 'Whether to skip the tests before pushing the tag' + required: false + default: false + username: + description: 'The Github username (default is the github actor)' + required: false + dry-run: + description: 'Skip Git push' + required: false + default: false + +jobs: + prepare-release: + runs-on: ubuntu-latest + steps: + - uses: actions/cache@v2 + with: + path: ~/.m2/repository + key: maven-11 + - uses: actions/checkout@v2 + with: + ref: master + token: ${{ secrets.GITHUB_API_TOKEN }} + - name: Install JDK 11 + uses: AdoptOpenJDK/install-jdk@v1 + with: + version: 11 + - name: 'Prepare' + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + GITHUB_TOKEN: ${{ secrets.GITHUB_API_TOKEN }} + SECRET_FILES_PASSPHRASE: ${{ secrets.SECRET_FILES_PASSPHRASE }} + MICRO_RELEASE: ${{ github.event.inputs.micro }} + RELEASE_VERSION: ${{ github.event.inputs.release-version }} + SKIP_TESTS: ${{ github.event.inputs.skip-tests }} + DRY_RUN: ${{ github.event.inputs.dry-run }} + run: | + + sudo apt-get update -o Dir::Etc::sourcelist="sources.list" \ + -o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0" + sudo apt-get install -y gnupg2 gnupg-agent + + curl -s "https://get.sdkman.io" | bash + source ~/.sdkman/bin/sdkman-init.sh && \ + sdk install jbang + + chmod +x .build/deploy.sh .build/decrypt-secrets.sh + + .build/decrypt-secrets.sh + .build/cut-release.sh + - name: 'Clear compatibility justifications' + env: + DRY_RUN: ${{ github.event.inputs.dry-run }} + run: | + source ~/.sdkman/bin/sdkman-init.sh + + git config --global user.name "${GITHUB_ACTOR}" + git config --global user.email "smallrye@googlegroups.com" + + git fetch origin + git reset --hard + git checkout master + echo "Clearing difference justifications" + jbang .build/CompatibilityUtils.java clear + if [[ $(git diff --stat) != '' ]]; then + git add -A + git status + git commit -m "[POST-RELEASE] - Clearing breaking change justifications" + if [[ ${DRY_RUN} == "true" ]]; then + echo "[DRY-RUN] - Skipping push" + else + git push origin master + fi + else + echo "No justifications cleared" + fi From a92c3c350933e3de094053b9c084e315d674cc69 Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Wed, 23 Dec 2020 14:16:05 +0100 Subject: [PATCH 7/7] Document new release process --- .build/mock-events/README.md | 23 ++++ .../mock-events/release-workflow-event.json | 7 ++ .../{ => mock-events}/tag-creation-event.json | 0 .github/workflows/deploy-release.yml | 4 +- .github/workflows/post-release.yml | 3 +- .github/workflows/prepare-release.yml | 16 +-- CONTRIBUTING.md | 102 ++++++++++++++---- 7 files changed, 122 insertions(+), 33 deletions(-) create mode 100644 .build/mock-events/README.md create mode 100644 .build/mock-events/release-workflow-event.json rename .build/{ => mock-events}/tag-creation-event.json (100%) diff --git a/.build/mock-events/README.md b/.build/mock-events/README.md new file mode 100644 index 000000000..fed5ebc39 --- /dev/null +++ b/.build/mock-events/README.md @@ -0,0 +1,23 @@ +This directory contains "mock" versions of Github events to test the release process: + +The following command triggers a "dry run" of a release preparation: + +```bash +act workflow_dispatch -e .build/mock-events/release-workflow-event.json \ + -s GITHUB_API_TOKEN=${GITHUB_TOKEN} \ + -s SECRET_FILES_PASSPHRASE="..." \ + -P ubuntu-latest=nektos/act-environments-ubuntu:18.04 \ + -r -a cescoffier +``` + +The following command triggers a tag creation event (which triggers the post-release, artifact deployment and web site update): + +```bash +act create -e .build/mock-events/tag-creation-event.json \ + -s GITHUB_API_TOKEN=${GITHUB_TOKEN} \ + -s SECRET_FILES_PASSPHRASE="..." \ + -P ubuntu-latest=nektos/act-environments-ubuntu:18.04 \ + -r -a cescoffier +``` +**IMPORTANT:** Comment the `deploy-release.sh` line in the `deploy-release.yaml` to avoid pushing artifacts to Maven central. + diff --git a/.build/mock-events/release-workflow-event.json b/.build/mock-events/release-workflow-event.json new file mode 100644 index 000000000..c2183f7a8 --- /dev/null +++ b/.build/mock-events/release-workflow-event.json @@ -0,0 +1,7 @@ +{ + "inputs": { + "skip-tests": true, + "dry-run": true, + "branch": "new-release-script" + } +} \ No newline at end of file diff --git a/.build/tag-creation-event.json b/.build/mock-events/tag-creation-event.json similarity index 100% rename from .build/tag-creation-event.json rename to .build/mock-events/tag-creation-event.json diff --git a/.github/workflows/deploy-release.yml b/.github/workflows/deploy-release.yml index 81fa8c5f8..3390ac41c 100644 --- a/.github/workflows/deploy-release.yml +++ b/.github/workflows/deploy-release.yml @@ -37,5 +37,5 @@ jobs: -o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0" sudo apt-get install -y gnupg2 gnupg-agent - .build/decrypt-secrets.sh - .build/deploy-release.sh + sh .build/decrypt-secrets.sh + # .build/deploy-release.sh diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml index a5eb0d824..6573a07be 100644 --- a/.github/workflows/post-release.yml +++ b/.github/workflows/post-release.yml @@ -37,12 +37,13 @@ jobs: source ~/.sdkman/bin/sdkman-init.sh && \ sdk install jbang + mkdir target jbang .build/CompatibilityUtils.java extract - name: 'Post-Release Tasks' env: GITHUB_TOKEN: ${{ secrets.GITHUB_API_TOKEN }} REF: ${{ github.event.ref }} run: | - git checkout new-release-script # TODO Change it back to master + git checkout master source ~/.sdkman/bin/sdkman-init.sh && \ jbang .build/PostRelease.java --release-version=${REF} --token=${GITHUB_TOKEN} diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 757533851..51ba072d8 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -14,9 +14,10 @@ on: description: 'Whether to skip the tests before pushing the tag' required: false default: false - username: - description: 'The Github username (default is the github actor)' + branch: + description: 'The branch from which the release is cut' required: false + default: 'master' dry-run: description: 'Skip Git push' required: false @@ -32,7 +33,7 @@ jobs: key: maven-11 - uses: actions/checkout@v2 with: - ref: master + ref: ${{ github.event.inputs.branch }} token: ${{ secrets.GITHUB_API_TOKEN }} - name: Install JDK 11 uses: AdoptOpenJDK/install-jdk@v1 @@ -57,13 +58,12 @@ jobs: source ~/.sdkman/bin/sdkman-init.sh && \ sdk install jbang - chmod +x .build/deploy.sh .build/decrypt-secrets.sh - - .build/decrypt-secrets.sh + sh .build/decrypt-secrets.sh .build/cut-release.sh - name: 'Clear compatibility justifications' env: DRY_RUN: ${{ github.event.inputs.dry-run }} + BRANCH: ${{ github.event.inputs.branch }} run: | source ~/.sdkman/bin/sdkman-init.sh @@ -72,7 +72,7 @@ jobs: git fetch origin git reset --hard - git checkout master + git checkout ${BRANCH} echo "Clearing difference justifications" jbang .build/CompatibilityUtils.java clear if [[ $(git diff --stat) != '' ]]; then @@ -82,7 +82,7 @@ jobs: if [[ ${DRY_RUN} == "true" ]]; then echo "[DRY-RUN] - Skipping push" else - git push origin master + git push origin ${BRANCH} fi else echo "No justifications cleared" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d6349a6ee..3f7cce435 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -79,36 +79,94 @@ Also you need to check that: * there are no build in progress of the `ref` branch (`master`) * the last build of the `ref` branch (`master`) has been successful -Snapshot deployment is triggered using: +Multiple steps compose the release process -```bash -curl -X POST -H "Authorization: token $GITHUB_TOKEN" \ - -H "Accept: application/vnd.github.ant-man-preview+json" \ - -H "Content-Type: application/json" \ - https://api.github.com/repos/smallrye/smallrye-mutiny/deployments \ - --data '{"ref": "master", "environment": "snapshot"}' -``` +### Release preparation + +The "prepare-release" workflow verifies that a release can be done. +Typically, it computes the target version if not set, and verifies that: + +1. a milestone with the release version as name exists and as no more open issues +2. a tag with the release version as name does not already exist + +Then, it bumps the version, builds the project and pushed a tag. +Finally, it clears the breaking change justifications. + +You can trigger a release using a _workflow dispatch_ events or directly from the Github Actions page. +Parameters are the following: + +- `dry-run` - if `true` to not push anything to the remote git repository (default: `false`). +- `release-version` - the target release version. If not set, it computes the release version by bumping the _minor_ version digit, or the _micro_ version digit is `micro` is set to `true`. +The last version is the last pushed tag. +- `micro` - when the `release-version` is not set, indicates that the version is a _micro_ (default: `false`). +- `skip-tests` - if `true`, skips the tests during the project build (default: `false`) +- `branch` - the branch from which the release must be cut (default: `master`) + +Check https://github.com/smallrye/smallrye-mutiny/actions to follow the progress. + +The workflow triggers the other steps automatically (upon the tag creation). + +### Web Site deployment + +When the "prepare-release" workflows pushes the tag, the "deploy-site" workflows starts (upon the tag creation event), and builds the website from the tag. +It pushes the website, so once completed, the website is up to date. + +### Artifact deployment to Maven Central -For releases, run: +When the "prepare-release" workflows pushes the tag, the "deploy-release" workflows starts (upon the tag creation event), and builds the artifacts from the tag. +It also deploys them to Maven Central. + +### Post-Release + +When the "prepare-release" workflows pushes the tag, the "post-release" workflows starts (upon the tag creation event), and creates the Github Release. +It computes the changelog, collects the breaking changes and creates the release (if it does not exist). +It also creates the next milestone (if it does not exist yet) and closes the previous one. + +The next milestone name is the current version with an increment to the minor version digit. + +The "post-release" workflow is idempotent. + +### Running the release process locally. + +It is possible to runs the release process locally using [https://github.com/nektos/act](act). +In addition to `act`, you need a Github Token with push permission to the repository (stored in the `GITHUB_TOKEN` env variable), and the SmallRye GPG Key passphrase (stored in the `PASSPHRASE` env variable). + +Then, edit the `.build/mock-events/release-workflow-event.json` to adapt the execution: + +```json +{ + "inputs": { + "skip-tests": true, + "branch": "master", + "release-version": "0.12.5" + } +} +``` + +Then, from the project root, runs: ```bash -curl -X POST -H "Authorization: token $GITHUB_TOKEN" \ - -H "Accept: application/vnd.github.ant-man-preview+json" \ - -H "Content-Type: application/json" \ - https://api.github.com/repos/smallrye/smallrye-mutiny/deployments \ - --data '{"ref": "master", "environment": "release"}' +act workflow_dispatch -e .build/mock-events/release-workflow-event.json \ + -s GITHUB_API_TOKEN=${GITHUB_TOKEN} \ + -s SECRET_FILES_PASSPHRASE=${PASSPHRASE} \ + -P ubuntu-latest=nektos/act-environments-ubuntu:18.04 \ + -r -a YOUR_GITHUB_NAME ``` + +This would execute the release preparation. +Once completed, because it creates the tag, the other steps will start. -For micro-releases, use: +If you need to run the other steps manually, edit the `.build/mock-events/tag-creation-event.json` to adapt it with the target tag. +Then, runs: ```bash -curl -X POST -H "Authorization: token $GITHUB_TOKEN" \ - -H "Accept: application/vnd.github.ant-man-preview+json" \ - -H "Content-Type: application/json" \ - https://api.github.com/repos/smallrye/smallrye-mutiny/deployments \ - --data '{"ref": "master", "environment": "release", "payload": {"micro": true}}' +act create -e .build/mock-events/tag-creation-event.json \ + -s GITHUB_API_TOKEN=${GITHUB_TOKEN} \ + -s SECRET_FILES_PASSPHRASE=${PASSPHRASE} \ + -P ubuntu-latest=nektos/act-environments-ubuntu:18.04 \ + -r -a YOUR_GITHUB_NAME ``` -All these commands trigger GitHub _deployment_. -Check https://github.com/smallrye/smallrye-mutiny/actions to follow the progress. + +