From 7ce8192065fee0444ffa606cd5eb41e99ea20bf9 Mon Sep 17 00:00:00 2001 From: Michael Street <5597260+MStreet3@users.noreply.github.com> Date: Thu, 30 Oct 2025 14:55:31 -0400 Subject: [PATCH] feat(scripts): adds scripts for managing releases --- Makefile | 11 +- {script => scripts}/install-protoc.sh | 0 scripts/release/README.md | 25 ++++ scripts/release/fix_release.sh | 64 ++++++++++ scripts/release/helpers.sh | 35 ++++++ scripts/release/init.sh | 74 +++++++++++ scripts/release/print.sh | 9 ++ scripts/release/publish.sh | 175 ++++++++++++++++++++++++++ 8 files changed, 392 insertions(+), 1 deletion(-) rename {script => scripts}/install-protoc.sh (100%) create mode 100644 scripts/release/README.md create mode 100644 scripts/release/fix_release.sh create mode 100644 scripts/release/helpers.sh create mode 100644 scripts/release/init.sh create mode 100644 scripts/release/print.sh create mode 100644 scripts/release/publish.sh diff --git a/Makefile b/Makefile index c78a810c..c4bcdceb 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ gomodtidy: gomods .PHONY: install-protoc install-protoc: - script/install-protoc.sh 29.3 / + scripts/install-protoc.sh 29.3 / go install google.golang.org/protobuf/cmd/protoc-gen-go@`go list -m -json google.golang.org/protobuf | jq -r .Version` go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.5.1 @@ -87,3 +87,12 @@ standard_tests: ( cd $$mod_dir/pkg/workflows/wasm/host && go test -c -o $$abs_dir/host.test . ); \ echo "Running standard tests"; \ $$abs_dir/host.test -test.v -test.run ^TestStandard -path=standard_tests + + +.PHONY: init_release +init_release: + cd ./scripts/release && bash init.sh + +.PHONE: publish_release +publish_release: + cd ./scripts/release && bash publish.sh \ No newline at end of file diff --git a/script/install-protoc.sh b/scripts/install-protoc.sh similarity index 100% rename from script/install-protoc.sh rename to scripts/install-protoc.sh diff --git a/scripts/release/README.md b/scripts/release/README.md new file mode 100644 index 00000000..940e6a52 --- /dev/null +++ b/scripts/release/README.md @@ -0,0 +1,25 @@ +### Release Process + +Releases are created from a candidate branch. Create a candidate branch by running: + +```bash +$ make init_release +``` + +Follow the prompts. + +Once the release is ready to be tagged execute the publish flow: + +```bash +$ make publish_release +``` + +Follow the prompts to tag either a `beta` pre-release set of tags or a `stable` tag without a pre-release suffix. + +The following packages will be tagged: +- generator/protoc-gen-cre +- capabilities/scheduler/cron +- capabilities/networking/http +- capabilities/blockchain/evm + +Merge and delete the release branch once tags are accepted and verified. \ No newline at end of file diff --git a/scripts/release/fix_release.sh b/scripts/release/fix_release.sh new file mode 100644 index 00000000..aa1e9947 --- /dev/null +++ b/scripts/release/fix_release.sh @@ -0,0 +1,64 @@ +#!/bin/bash + +source ./print.sh + +describe "Fixing release branch" +read -p "Release Version (X.Y.Z): " version + +if [[ ! $version =~ ^[0-9]+.[0-9]+.[0-9]+$ ]] +then + error "Release version must be in semantic versioning format (X.Y.Z)" + exit 1 +fi + +# Get the cherry-pick commit hash +read -p "Enter the commit hash to cherry pick: " commitHash + +git show $commitHash + +current_branch=$(git rev-parse --abbrev-ref HEAD) +release_branch="release/v$version" +fix_branch="fix/v$version/$commitHash" + +# Check that the branch exists +describe "Checking remote origin for release branch..." +if git ls-remote --exit-code --heads origin $release_branch > /dev/null; then + echo "Found release branch" +else + error "Failed to find release branch" + exit 1 +fi + +# Checkout the release branch +describe "Updating remote tracking branches..." +git fetch origin + +# Create a new branch from the release branch +describe "Creating fix branch..." +git checkout -B $fix_branch origin/$release_branch + +# Cherry pick the commit +describe "Cherry picking commit onto fix branch..." +if git cherry-pick -S $commitHash > /dev/null; then + echo "Cherry pick successful" +else + error "Failed to cherry pick commit. Rolling back..." + + git cherry-pick --abort + git checkout $current_branch + git branch -D $fix_branch + + exit 1 +fi + +# Push the fix branch +describe "Pushing fix branch to origin..." +git push origin $fix_branch + +# Create a pull request +describe "Creating pull request to merge fix branch into release branch..." +gh pr create --fill --base $release_branch --head $fix_branch --label "release-fix" + +# Reset the branch back to the user's current branch +describe "Reset local branch back to original branch..." +git checkout $current_branch \ No newline at end of file diff --git a/scripts/release/helpers.sh b/scripts/release/helpers.sh new file mode 100644 index 00000000..42e30d3d --- /dev/null +++ b/scripts/release/helpers.sh @@ -0,0 +1,35 @@ +source ./print.sh + +# Fetches remote branches from origin +function fetch_remote_branches() { + # Update remote tracking branches + describe "Updating remote tracking branches..." + git fetch origin +} + +# Verify that the branch exists +function verify_remote_release_branch() { + release_branch=$1 + + describe "Checking remote origin for release branch..." + if git ls-remote --exit-code --heads origin $release_branch > /dev/null; then + echo "Found release branch" + else + error "Failed to find release branch" + exit 1 + fi +} + +function ask_version { + question=$1 + + read -p "$question " version + + if [[ ! $version =~ ^[0-9]+.[0-9]+.[0-9]+$ ]] + then + echo "Failed to give a proper semver version number, exiting" + exit 1 + fi + + echo $version +} \ No newline at end of file diff --git a/scripts/release/init.sh b/scripts/release/init.sh new file mode 100644 index 00000000..63c17f65 --- /dev/null +++ b/scripts/release/init.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +# Initializes a new release branch from the trunk branch + +readonly TRUNK_BRANCH="main" + +source ./print.sh +source ./helpers.sh + +# accept a version from the user +describe "Initializing a new release branch..." +read -p "Release Version (X.Y.Z): " version + +if [[ ! $version =~ ^[0-9]+.[0-9]+.[0-9]+$ ]] +then + error "Release version must be in semantic versioning format (X.Y.Z)" + exit 1 +fi + +# Ensure we don't have any tags which match the provided version +describe "Checking for existing tags..." +if git ls-remote --tags origin | grep -q "v$version"; then + error "A tag for v$version already exists. Please use a different version number" + exit 1 +fi + +current_branch=$(git rev-parse --abbrev-ref HEAD) +release_branch="release/v$version" +remote_trunk_branch="origin/$TRUNK_BRANCH" + +# Define the target branch for the release. By default we use the remote trunk, but if the hotfix +# flag is provided, we prompt for a tag to base the release on. +release_target=$remote_trunk_branch +while [[ $# -gt 0 ]]; do + case "$1" in + --hotfix) + describe "Hotfix flag detected. Please provide a tag to base the release on" + read -p "Tag: " tag + release_target="tags/v$tag" + + describe "Fetching all remote tags" + git fetch --all --tags + ;; + esac + shift +done + +# Update remote tracking branches +fetch_remote_branches + +# Create a release branch from the tip of the remote main branch +describe "Creating $release_branch targeting $release_target..." +git checkout -b $release_branch $release_target +if [ $? -ne 0 ]; then + error "Failed to create release branch $release_branch" + exit 1 +fi + +# Push the release branch to origin +describe "Pushing $release_branch to origin..." +git push -u origin $release_branch +if [ $? -ne 0 ]; then + error "Failed to publish $release_branch. Rolling back..." + + git checkout $current_branch; + git branch -D $release_branch; + exit 1 +fi + +# Reset the branch back to the user's current branch +describe "Reset local branch back to original branch..." +git checkout $current_branch + +describe "Successfully initialized $release_branch!" \ No newline at end of file diff --git a/scripts/release/print.sh b/scripts/release/print.sh new file mode 100644 index 00000000..14855e3d --- /dev/null +++ b/scripts/release/print.sh @@ -0,0 +1,9 @@ +# Echos the given string in green +function describe { + echo -e "\033[0;32m$1\033[0m" +} + +# Echos the given string in red +function error { + echo -e "\033[0;31m$1\033[0m" +} \ No newline at end of file diff --git a/scripts/release/publish.sh b/scripts/release/publish.sh new file mode 100644 index 00000000..589d43c2 --- /dev/null +++ b/scripts/release/publish.sh @@ -0,0 +1,175 @@ +#!/bin/bash + +# You'll need to update these to point to your actual print and helpers scripts +source ./print.sh +source ./helpers.sh + +describe "Generating and pushing release tags" +read -p "Release Version (X.Y.Z): " version + +if [[ ! $version =~ ^[0-9]+.[0-9]+.[0-9]+$ ]] +then + error "Release version must be in semantic versioning format (X.Y.Z)" + exit 1 +fi + +release_branch="release/v$version" +base_release_tag="v$version" + +# --- Prompt for Annotated Tag Message --- +read -p "Enter Tag Message (e.g., v${version} release): " TAG_MESSAGE +if [ -z "$TAG_MESSAGE" ]; then + error "Tag message cannot be empty. Aborting." + exit 1 +fi + +# Verify that the release branch exists +describe "Verifying existence of remote branch: $release_branch" +if ! git ls-remote --exit-code origin $release_branch > /dev/null 2>&1 +then + error "Remote branch '$release_branch' does not exist on origin." + exit 1 +fi + +# Ask for the stage of release +read -p "Version Stage [beta/stable]: " versionStage + +if [[ ! $versionStage =~ ^beta$|^stable$ ]] +then + echo "Type must be either beta or stable." + exit 1 +fi + +# Determine the final tag(s) and stage +final_tags=() +pre_release_suffix="" + +if [ $versionStage = "stable" ] +then + read -p "Deploy a stable production tag? ([N|n]o/[Y|y]es): " reply + if [[ $reply =~ ^[Yy]$|^[Yy]es$ ]] + then + echo "Preparing stable tag: $base_release_tag" + final_tags+=("$base_release_tag") + elif [[ $reply =~ ^[Nn]$|^[Nn]o$ ]] + then + echo "Aborting tag publication." + exit 0 + else + echo "Expected Yes/yes/Y/y or No/no/N/n" + exit 1 + fi +fi + + +if [ $versionStage = "beta" ] +then + # Fetch the list of remote tags matching the version to determine the next iteration counter + describe "Determining beta iteration using git ls-remote..." + + # Fetch all existing tags for the current version (vX.Y.Z*) + remote_tag_refs=$(git ls-remote --tags origin "$base_release_tag*" | awk '{print $2}') + + iter=-1 + # Read the tags line by line + while IFS= read -r ref; do + tag_name=${ref##refs/tags/} + + if [[ $tag_name == *"-beta."* ]] + then + number=${tag_name##*.} + if [[ $number =~ ^[0-9]+$ ]] + then + if [ $number -gt $iter ] + then + iter=$number + fi + fi + fi + done <<< "$remote_tag_refs" + + nextiter=$((iter+1)) + + # If no previous tags were found, start at beta.0 + if [ $iter -eq -1 ]; then + nextiter=0 + fi + + read -p "Use $nextiter as the next beta iteration value? ([N|n]o/[Y|y]es): " reply + if [[ $reply =~ ^[Yy]$|^[Yy]es$ ]] + then + echo "Using $nextiter as the next iteration value." + elif [[ $reply =~ ^[Nn]$|^[Nn]o$ ]] + then + read -p "Enter a custom iteration: " customiter + if [[ ! $customiter =~ ^[0-9]+$ ]]; then + error "Custom iteration must be a positive integer." + exit 1 + fi + nextiter=$customiter + else + echo "Expected Yes/yes/Y/y or No/no/N/n" + exit 1 + fi + + # Set the FINAL suffix + pre_release_suffix="-beta.$nextiter" + + # 1. Add the BASE tag (e.g., v1.0.0-beta.0) + final_tags+=("$base_release_tag$pre_release_suffix") + + # Component prefixes for additional tags + component_prefixes=( + "generator/protoc-gen-cre/" + "capabilities/scheduler/cron/" + "capabilities/networking/http/" + "capabilities/blockchain/evm/" + ) + + # 2. Add the COMPONENT tags (e.g., generator/protoc-gen-cre/v1.0.0-beta.0) + for prefix in "${component_prefixes[@]}"; do + final_tags+=("$prefix$base_release_tag$pre_release_suffix") + done +fi + +describe "Creating and pushing tags targeting $release_branch" + +# Ensure we are on the correct branch to create the tag +git fetch origin $release_branch > /dev/null 2>&1 +TARGET_COMMIT=$(git rev-parse "origin/$release_branch") +if [ -z "$TARGET_COMMIT" ]; then + error "Could not find commit for remote branch origin/$release_branch." + exit 1 +fi + +for tag in "${final_tags[@]}"; do + describe "Processing tag: $tag" + + # Check if tag already exists locally or remotely + if git rev-parse --quiet --verify "refs/tags/$tag" > /dev/null || git ls-remote --tags origin "$tag" | grep -q "$tag" + then + echo "Tag $tag already exists locally or remotely. Skipping." + continue + fi + + # Create the ANNOTATED tag on the target commit + echo "Creating annotated tag $tag on commit $TARGET_COMMIT with message: \"$TAG_MESSAGE\"" + if ! git tag -a "$tag" -m "$TAG_MESSAGE" "$TARGET_COMMIT" + then + error "Failed to create local tag $tag." + exit 1 + fi + + # Push the tag to the remote + echo "Pushing tag $tag to origin" + if ! git push origin "$tag" + then + error "Failed to push tag $tag to origin. Rolling back local tag..." + git tag -d "$tag" # Delete local tag on failure + exit 1 + fi + echo "Successfully pushed $tag." +done + +echo "Successfully generated and pushed all required tags!" +exit 0 \ No newline at end of file