Skip to content

Instantly share code, notes, and snippets.

@sunix
Last active August 31, 2023 15:26
Show Gist options
  • Save sunix/8d1d598463a4b79c99b109136aae9f67 to your computer and use it in GitHub Desktop.
Save sunix/8d1d598463a4b79c99b109136aae9f67 to your computer and use it in GitHub Desktop.
release script for jkube
///usr/bin/env jbang "$0" "$@" ; exit $?
// //DEPS <dependency1> <dependency2>
//DEPS org.jline:jline:3.16.0
//DEPS de.codeshelf.consoleui:consoleui:0.0.13
//DEPS org.fusesource.jansi:jansi:2.3.4
//DEPS info.picocli:picocli:4.6.1
//DEPS org.apache.maven:maven-model:3.8.1
//DEPS org.apache.commons:commons-exec:1.3
//DEPS org.yaml:snakeyaml:1.29
import static java.lang.System.*;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.ProcessBuilder.Redirect;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import de.codeshelf.consoleui.prompt.ConsolePrompt;
import de.codeshelf.consoleui.prompt.InputResult;
import de.codeshelf.consoleui.prompt.ListResult;
import de.codeshelf.consoleui.prompt.PromtResultItemIF;
import de.codeshelf.consoleui.prompt.builder.ListPromptBuilder;
import de.codeshelf.consoleui.prompt.builder.PromptBuilder;
import jline.TerminalFactory;
import picocli.CommandLine;
public class jrelease {
public static void main(String... args) throws InterruptedException, IOException, XmlPullParserException {
print("Welcome to @|green JKube release|@ script");
String currentVersion = retrieveCurrentVersionFromPomFile();
print("Current version is @|bold,green " + currentVersion + "|@\n");
// guess the next maven release version from the current version
String nextReleaseVersion = guessNextReleaseVersion(currentVersion);
// prompt the user for the release version
nextReleaseVersion = promptWithSuggestion("Enter the release version to use", nextReleaseVersion);
print("Chosen release version is @|bold,green " + nextReleaseVersion + "|@");
// guess the next X.Y-SNAPSHOT version from the next release version by
// incrementing the minor version
String nextSnapshotVersion = guessNextSnapshotVersion(nextReleaseVersion);
// prompt the user for the next snapshot version
nextSnapshotVersion = promptWithSuggestion("Enter the next snapshot version to use", nextSnapshotVersion);
print("Chosen next snapshot version is @|bold,green " + nextSnapshotVersion + "|@");
// display the git remotes
print("Git remotes are:");
executeInteractiveCommand("bash", "-c", "git remote -v");
// choose the git remote to use as a main repository
String gitUpstreamRemote = promptWithSuggestion("Enter the git remote to use as the upstream", "origin");
// choose the git remote to use as a fork repository
String gitForkRemote = promptWithSuggestion("Enter the git remote to use as a fork", "fork");
// choose the branch name to use for the release
String releaseBranchName = promptWithSuggestion("Enter the branch name to use for the release", "release-"
+ nextReleaseVersion.replace(".", "-"));
// 1. Make sure the branch to release is up to date on your fork (master by
// default)
printStep(1, "Make sure the branch to release is up to date on your fork (master by default)");
executeStep("git fetch --all && git checkout master && git reset --hard " + gitUpstreamRemote + "/master");
executeStep("git checkout -b " + releaseBranchName);
print("Check that the branch is up to date with the remote");
executeStep("git --no-pager log -n 5 --pretty=oneline --abbrev-commit --graph --decorate");
// 2. Set release version
// ./scripts/release.sh setReleaseVersion 1.13.1
// Make sure project.build.outputTimestamp property gets updated
printStep(2, "Set release version");
executeStep("./scripts/release.sh setReleaseVersion " + nextReleaseVersion);
// 3. mvn clean install -DskipTests
printStep(3, "mvn clean install -DskipTests");
print("Skipping this step for now");
//executeStep("mvn clean install -DskipTests");
// 4. Update Quickstarts version
// ./scripts/quickstarts.sh version
printStep(4, "Update Quickstarts version");
executeStep("./scripts/quickstarts.sh version");
// 5. Replace ### 1.13-SNAPSHOT with ### 1.13.0 (2023-06-14) in CHANGELOG.md
// get the current snapshot version
printStep(5, "Replace ### x.x-SNAPSHOT with ### x.y.z (202x-xx-xx) in CHANGELOG.md");
String changelogNewReleaseHeader = "### " + nextReleaseVersion + " ("
+ new SimpleDateFormat("yyyy-MM-dd").format(new java.util.Date()) + ")";
executeStep("sed -i 's/### " + currentVersion + "/" + changelogNewReleaseHeader + "/g' CHANGELOG.md");
// 6. Update bug templates to add versions:
// .github/ISSUE_TEMPLATE/bug_report.yml
printStep(6, "Update bug templates .github/ISSUE_TEMPLATE/bug_report.yml to add versions");
executeStep("sed -E '/(- )\"SNAPSHOT\".*/{p;s//\\1\""
+ nextReleaseVersion
+ "\"/}' -i .github/ISSUE_TEMPLATE/bug_report.yml");
// 7. Send a message to Gitter channel `Starting release process for v1.13.1`
printStep(7, "Send a message to Gitter channel `Starting release process for v" + nextReleaseVersion + "`");
print("Press enter to continue");
// wait for the user to press enter
System.in.read();
// 8. Git add and Commit ‘[RELEASE] Updated project version to 1.13.1’
printStep(8, "Git add and Commit ‘[RELEASE] Updated project version to " + nextReleaseVersion + "’");
executeStep("git add . && git commit -m \"[RELEASE] Updated project version to " + nextReleaseVersion + "\"");
// 9. Set snapshot version
// ./scripts/release.sh setReleaseVersion 1.14-SNAPSHOT
printStep(9, "Set snapshot version");
executeStep("./scripts/release.sh setReleaseVersion " + nextSnapshotVersion);
// 10. Add ### 1.14-SNAPSHOT line to CHANGELOG.md
printStep(10, "Add ### " + nextSnapshotVersion + " line to CHANGELOG.md");
executeStep("sed -E '/" + escape(changelogNewReleaseHeader) + "/{s//### " + nextSnapshotVersion
+ "\\n\\n\\0/}' -i CHANGELOG.md");
// 11. Git add and commit ‘[RELEASE] Prepare for next development iteration’
printStep(11, "Git add and commit ‘[RELEASE] Prepare for next development iteration’");
executeStep("git add . && git commit -m \"[RELEASE] Prepare for next development iteration\"");
// 12. Create Pull Request
printStep(12, "Create Pull Request");
executeStep("git push " + gitForkRemote + " " + releaseBranchName);
print("Create the PR in github using the branch " + releaseBranchName + " in the " + gitForkRemote
+ " repository");
print("Press enter to continue");
System.in.read();
// 13. After rebase and merge create tag for release in first commit
printStep(13, "After rebase and merge create tag for release in first commit");
executeStep("git fetch --all && git checkout master && git reset --hard " + gitUpstreamRemote + "/master && git --no-pager log -n 5 --pretty=oneline --abbrev-commit --graph --decorate");
String gitRevToTag = promptWithSuggestion("Enter the git rev of the commit [RELEASE] Updated project version to "+ nextReleaseVersion, "xxxxxxx");
executeStep("git tag -a v" + nextReleaseVersion + " " + gitRevToTag + " -m \"" + nextReleaseVersion + " ("+ new SimpleDateFormat("yyyy-MM-dd").format(new java.util.Date()) + ")\"");
executeStep("git push " + gitUpstreamRemote + " v" + nextReleaseVersion);
// 14. https://ci.eclipse.org/jkube
printStep(14, "https://ci.eclipse.org/jkube");
// - Run build simplified-maven-central-release with tag as RELEASE_TAG build parameter
print("- Run build https://ci.eclipse.org/jkube/job/ReleasePipelines/job/simplified-maven-central-release/ with tag v"+ nextReleaseVersion+" as RELEASE_TAG build parameter");
print("Press enter to continue");
System.in.read();
// - Run simplified-downloads-eclipse-release with tag as RELEASE_TAG build parameter -> check https://download.eclipse.org/jkube/ https://ci.eclipse.org/jkube/job/simplified-maven-central-release/
print("- Run https://ci.eclipse.org/jkube/job/ReleasePipelines/job/simplified-downloads-eclipse-release/ with tag v"+ nextReleaseVersion+" as RELEASE_TAG build parameter");
print("Press enter to continue");
System.in.read();
// 15. Wait for maven central (check https://repo1.maven.org/maven2/org/eclipse/jkube/kubernetes-maven-plugin-doc/ )
printStep(15, "Wait for maven central, check");
print("- Check https://download.eclipse.org/jkube/");
print("- Check https://repo1.maven.org/maven2/org/eclipse/jkube/kubernetes-maven-plugin-doc/");
print("Press enter to continue");
System.in.read();
// 16. Create GitHub release from previously pushed tag to upstream, enable `discussions`
// Add changelog
// ./scripts/changelog.sh extract 1.13.1
printStep(16, "Create GitHub release from previously pushed tag to upstream, enable `discussions`");
print("Add changelog this to the release description:");
executeInteractiveCommand("bash", "-c", "./scripts/changelog.sh extract " + nextReleaseVersion);
// 17. Tag new release in https://github.com/jkubeio/jkube-website (keep empty title/description)
printStep(17, "Tag new release in https://github.com/jkubeio/jkube-website (keep empty title/description)");
print("Press enter to continue");
System.in.read();
// 18. Publish tweet in jkubeio profile
printStep(18, "Publish tweet in jkubeio profile");
// print the message to tweet:
// 🔥 @ECDTools #JKube new release 🚀
// 📦 v1.14.0
// ✨ Helidon support
// ✨ several improvements for helm
// ✨ CustomResource fragment improvements
// 🐞 Many bug fixes & improvements
//
//📢 Please help us spread the word & share your experience @jkubeio
//
// https://github.com/eclipse/jkube/releases/tag/v1.14.0
print("🔥 @ECDTools #JKube new release 🚀");
print("📦 v" + nextReleaseVersion);
print("✨ new feat");
print("✨ another great feature");
print("✨ some other improvements");
print("🐞 Many bug fixes & improvements");
print("");
print("📢 Please help us spread the word & share your experience @jkubeio");
print("");
print("https://github.com/eclipse/jkube/releases/tag/v" + nextReleaseVersion);
print("");
print("Press enter to continue");
System.in.read();
// 19. Announce the release in Gitter
printStep(19, "Announce the release in Gitter");
print("🔥 Eclipse JKube new release 🚀");
print("📦 v" + nextReleaseVersion);
print("✨ new feat");
print("✨ another great feature");
print("✨ some other improvements");
print("🐞 Many bug fixes & improvements");
print("");
print("📢 Please help us spread the word & share your experience https://twitter.com/jkubeio");
print("");
print("https://github.com/eclipse/jkube/releases/tag/v" + nextReleaseVersion);
print("");
print("Press enter to continue");
System.in.read();
// 20. Publish release in https://projects.eclipse.org/projects/ecd.jkube
printStep(20, "Publish release in https://projects.eclipse.org/projects/ecd.jkube");
print("Press enter to continue");
System.in.read();
// 21. Run the Gradle verification test:
// https://github.com/jkubeio/jkube-integration-tests/actions/workflows/smoke-tests.yml
printStep(21, "Run the Gradle verification test: https://github.com/jkubeio/jkube-integration-tests/actions/workflows/smoke-tests.yml");
print("Press enter to continue");
System.in.read();
}
private static String escape(String changelogNewReleaseHeader) {
return changelogNewReleaseHeader.replace("(", "\\(").replace(")", "\\)");
}
private static String retrieveCurrentVersionFromPomFile() {
String currentVersion = "1.14-SNAPSHOT";
try {
MavenXpp3Reader reader = new MavenXpp3Reader();
Model model = reader.read(new FileReader("pom.xml"));
currentVersion = model.getVersion();
} catch (Exception e) {
// just use the default version
}
return currentVersion;
}
private static void print(String cmd) {
out.println(text(cmd));
}
public static String text(String txt) {
return CommandLine.Help.Ansi.AUTO.text(txt).toString();
}
private static String guessNextSnapshotVersion(String nextReleaseVersion) {
String nextSnapshotVersion = "";
String[] nextReleaseVersionParts = nextReleaseVersion.split("\\.");
if (nextReleaseVersionParts.length == 3) {
int minorVersion = Integer.parseInt(nextReleaseVersionParts[1]);
nextSnapshotVersion = nextReleaseVersionParts[0] + "." + (minorVersion + 1) + "-SNAPSHOT";
} else {
String[] minorVersionParts = nextReleaseVersionParts[1].split("-");
int minorVersion = Integer.parseInt(minorVersionParts[0]);
nextSnapshotVersion = nextReleaseVersionParts[0] + "." + (minorVersion + 1) + "-SNAPSHOT";
}
return nextSnapshotVersion;
}
private static String promptWithSuggestion(String message, String defaultValue) throws IOException {
try {
ConsolePrompt prompt = new ConsolePrompt();
PromptBuilder promptBuilder = prompt.getPromptBuilder();
promptBuilder.createInputPrompt().name("name").message(text(message)).defaultValue(defaultValue)
.addPrompt();
// create a PromptBuilder with editable suggestion
HashMap<String, ? extends PromtResultItemIF> result = prompt.prompt(promptBuilder.build());
return ((InputResult) result.get("name")).getInput();
} finally {
try {
TerminalFactory.get().restore();
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static String guessNextReleaseVersion(String currentVersion) {
String nextReleaseVersion = "";
String[] currentVersionParts = currentVersion.split("\\.");
if (currentVersionParts.length == 3) {
String[] patchVersionParts = currentVersionParts[2].split("-");
int patchVersion = Integer.parseInt(patchVersionParts[0]);
nextReleaseVersion = currentVersionParts[0] + "." + currentVersionParts[1] + "." + (patchVersion + 1);
} else {
String[] minorVersionParts = currentVersionParts[1].split("-");
int minorVersion = Integer.parseInt(minorVersionParts[0]);
nextReleaseVersion = currentVersionParts[0] + "." + minorVersion + ".0";
}
return nextReleaseVersion;
}
private static void printStep(int step, String desc) {
out.println(text(
"\n🔵 @|bold,green Step " + step + ". |@ " + desc));
}
private static void executeInteractiveCommand(String... command) throws IOException, InterruptedException {
// convert the unix command to an array taking into account the quotes
ProcessBuilder p = new ProcessBuilder(command);
p.redirectInput(Redirect.INHERIT);
p.redirectOutput(Redirect.INHERIT);
p.redirectError(Redirect.INHERIT);
Process process = p.start();
process.waitFor();
}
private static void executeStep(String cmd)
throws IOException, InterruptedException {
String selectedId = "";
try {
ConsolePrompt prompt = new ConsolePrompt();
PromptBuilder promptBuilder = prompt.getPromptBuilder();
ListPromptBuilder chooserBuilder = promptBuilder.createListPrompt()
.name("idList")
.message(text("Choose an action"));
chooserBuilder
.newItem().name("1").text("Execute command: " + cmd).add();
chooserBuilder
.newItem().name("2").text("Start interactive shell").add();
chooserBuilder
.newItem().name("3").text("Skip").add();
chooserBuilder.addPrompt();
HashMap<String, ? extends PromtResultItemIF> result = prompt.prompt(promptBuilder.build());
selectedId = ((ListResult) result.get("idList")).getSelectedId();
} finally {
try {
TerminalFactory.get().restore();
} catch (Exception e) {
e.printStackTrace();
}
}
switch (selectedId) {
case "1":
executeInteractiveCommand("bash", "-c", "set -x; " + cmd);
break;
case "2":
print("Default command is: \n" + cmd);
print("Type `exit` to continue");
executeInteractiveCommand("bash", "--norc");
break;
case "3":
print("Skipping");
break;
default:
print("None of the options was selected, skipping");
break;
}
}
private static String promptList(String message, Stream<String> optionList) throws IOException {
try {
ConsolePrompt prompt = new ConsolePrompt();
PromptBuilder promptBuilder = prompt.getPromptBuilder();
ListPromptBuilder chooserBuilder = promptBuilder.createListPrompt()
.name("idList")
.message(text(message));
optionList.forEach(
(s) -> chooserBuilder.newItem().text(s).add());
chooserBuilder.addPrompt();
HashMap<String, ? extends PromtResultItemIF> result = prompt.prompt(promptBuilder.build());
return ((ListResult) result.get("idList")).getSelectedId();
} finally {
try {
TerminalFactory.get().restore();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void step6AddVersionToBugReportYml(String versioString) throws IOException {
out.println(text(
"\n🔵 @|bold,green Step 6. |@ Update bug templates .github/ISSUE_TEMPLATE/bug_report.yml to add versions"));
DumperOptions dumperOptions = new DumperOptions();
dumperOptions.setIndent(2);
dumperOptions.setPrettyFlow(true);
dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
Yaml yaml = new Yaml(dumperOptions);
Map<String, Object> bugReport = yaml
.load(new FileReader(".github/ISSUE_TEMPLATE/bug_report.yml"));
ArrayList<Map<String, Object>> body = (ArrayList) bugReport.get("body");
// get the item where id is equal to version
Map<String, Object> versionItem = body.stream().filter(
item -> "version".equals(item.get("id"))).findFirst()
.orElse(null);
if (versionItem != null) {
// get the options
Map<String, Object> attributes = (Map<String, Object>) versionItem.get("attributes");
ArrayList<String> options = (ArrayList) attributes.get("options");
// add the new version
options.add(versioString);
// save the file
yaml.dump(bugReport,
new FileWriter(".github/ISSUE_TEMPLATE/bug_report.yml"));
}
}
private static String executeCommandAndReturnOutput(String command) throws IOException {
ProcessBuilder p = new ProcessBuilder("bash", "-c", command);
// start the process and return the output
p.redirectInput(Redirect.INHERIT);
p.redirectOutput(Redirect.PIPE);
p.redirectError(Redirect.INHERIT);
Process process = p.start();
try {
process.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
return process.getOutputStream().toString();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment