Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 85 additions & 14 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ on:
required: true
type: string
release_branch:
description: 'Release branch (e.g., release/2.3)'
description: 'Release branch (e.g., release/2.3._)'
required: true
type: string
next_snapshot:
Expand All @@ -59,6 +59,7 @@ defaults:
env:
RELEASE_VERSION: ${{ inputs.release_version }}
RELEASE_TAG: v${{ inputs.release_version }}
RC_TAG: v${{ inputs.release_version }}_RC

jobs:
# ============================================================
Expand Down Expand Up @@ -117,6 +118,15 @@ jobs:
echo "::error::Tag v${{ inputs.release_version }} already exists on remote"
exit 1
fi
# Check for leftover RC tag
if git rev-parse "${RC_TAG}" >/dev/null 2>&1; then
echo "::error::RC tag ${RC_TAG} already exists locally. Clean up with: git tag -d ${RC_TAG}"
exit 1
fi
if git ls-remote --tags origin "refs/tags/${RC_TAG}" | grep -q .; then
echo "::error::RC tag ${RC_TAG} already exists on remote. Clean up with: git push origin :refs/tags/${RC_TAG}"
exit 1
fi
echo "Tag does not exist - OK"

- name: Check if release branch exists
Expand Down Expand Up @@ -338,26 +348,26 @@ jobs:
echo "sha=${SHA}" >> $GITHUB_OUTPUT
echo "Release commit: ${SHA}"

- name: Create tag
- name: Create RC tag
if: ${{ inputs.dry_run != true }}
run: |
git tag -a "${RELEASE_TAG}" -m "Release ${RELEASE_TAG}"
echo "Created tag ${RELEASE_TAG}"
git tag -a "${RC_TAG}" -m "Release candidate ${RELEASE_TAG}"
echo "Created RC tag ${RC_TAG}"

- name: Push branch and tag
- name: Push branch and RC tag
if: ${{ inputs.dry_run != true }}
run: |
git push origin "${{ inputs.release_branch }}"
git push origin "${RELEASE_TAG}"
echo "Pushed branch and tag"
git push origin "${RC_TAG}"
echo "Pushed branch and RC tag"

- name: Dry run summary
if: ${{ inputs.dry_run == true }}
run: |
echo "::warning::DRY RUN - Branch and tag NOT pushed"
echo "Would have pushed:"
echo " - Branch: ${{ inputs.release_branch }}"
echo " - Tag: ${RELEASE_TAG}"
echo " - RC Tag: ${RC_TAG}"

# ============================================================
# Job 5: Stage to Maven Central (does NOT release)
Expand All @@ -368,10 +378,10 @@ jobs:
runs-on: ubuntu-latest
if: ${{ inputs.dry_run != true }}
steps:
- name: Checkout release tag
- name: Checkout RC tag
uses: actions/checkout@v6
with:
ref: ${{ env.RELEASE_TAG }}
ref: ${{ env.RC_TAG }}

- name: Cache Java binaries
id: cache-java
Expand Down Expand Up @@ -547,6 +557,46 @@ jobs:
git push || echo "⚠️ Push failed - update catalog manually at https://github.com/btraceio/jbang-catalog"
fi

# ============================================================
# Job 5d: Finalize release tag (RC -> final)
# ============================================================
finalize-tag:
name: Finalize Release Tag
needs: [prepare-release, wait-for-maven]
runs-on: ubuntu-latest
if: ${{ inputs.dry_run != true }}
steps:
- name: Checkout at release SHA
uses: actions/checkout@v6
with:
ref: ${{ needs.prepare-release.outputs.release_sha }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Fetch tags
run: git fetch origin --tags

- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

- name: Create final tag
run: |
git tag -a "${RELEASE_TAG}" -m "Release ${RELEASE_TAG}"
echo "Created final tag ${RELEASE_TAG}"

- name: Push final tag
run: |
git push origin "${RELEASE_TAG}"
echo "Pushed final tag ${RELEASE_TAG}"

- name: Delete RC tag
run: |
git tag -d "${RC_TAG}" || true
git push origin ":refs/tags/${RC_TAG}" || true
echo "Deleted RC tag ${RC_TAG}"

# ============================================================
# Job 6: Build distribution packages
# ============================================================
Expand All @@ -555,10 +605,10 @@ jobs:
needs: prepare-release
runs-on: ubuntu-latest
steps:
- name: Checkout release tag
- name: Checkout release commit
uses: actions/checkout@v6
with:
ref: ${{ inputs.dry_run == true && inputs.commit_sha || env.RELEASE_TAG }}
ref: ${{ inputs.dry_run == true && inputs.commit_sha || env.RC_TAG }}

- name: Cache Java binaries
id: cache-java
Expand Down Expand Up @@ -612,7 +662,7 @@ jobs:
# ============================================================
create-github-release:
name: Create GitHub Release
needs: [build-distributions, wait-for-maven]
needs: [build-distributions, finalize-tag]
runs-on: ubuntu-latest
if: ${{ inputs.dry_run != true }}
steps:
Expand All @@ -628,6 +678,25 @@ jobs:
name: release-distributions
path: distributions

- name: Download Maven artifact
continue-on-error: true
run: |
VERSION="${{ inputs.release_version }}"
BASE_URL="https://repo1.maven.org/maven2/io/btrace/btrace/${VERSION}"
mkdir -p maven-artifacts
MAX_ATTEMPTS=40
for i in $(seq 1 $MAX_ATTEMPTS); do
if curl -fSL "${BASE_URL}/btrace-${VERSION}.jar" \
-o maven-artifacts/btrace-${VERSION}.jar; then
echo "Downloaded btrace-${VERSION}.jar"
ls -la maven-artifacts/
exit 0
fi
echo "Attempt ${i}/${MAX_ATTEMPTS} failed, retrying in 30s..."
sleep 30
done
echo "::warning::Failed to download Maven JAR after ${MAX_ATTEMPTS} attempts (~20 minutes). GitHub release will proceed without it."

- name: Check for no-release-notes label
id: check-label
run: |
Expand All @@ -653,6 +722,7 @@ jobs:
distributions/btrace-v${{ inputs.release_version }}-sdkman-bin.zip
distributions/*.deb
distributions/*.rpm
maven-artifacts/*

# ============================================================
# Job 8: Update SDKMan
Expand Down Expand Up @@ -861,7 +931,7 @@ jobs:
# ============================================================
summary:
name: Release Summary
needs: [create-github-release, update-sdkman, update-milestones, update-jbang-catalog]
needs: [create-github-release, update-sdkman, update-milestones, update-jbang-catalog, finalize-tag]
runs-on: ubuntu-latest
if: always()
steps:
Expand Down Expand Up @@ -915,6 +985,7 @@ jobs:
echo "| Prepare Release | ${{ needs.prepare-release.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
echo "| Stage Maven | ${{ needs.stage-maven.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
echo "| Wait for Maven | ${{ needs.wait-for-maven.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
echo "| Finalize Tag | ${{ needs.finalize-tag.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
echo "| GitHub Release | ${{ needs.create-github-release.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
echo "| SDKMan | ${{ needs.update-sdkman.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
echo "| JBang Catalog | ${{ needs.update-jbang-catalog.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
Expand Down
47 changes: 30 additions & 17 deletions scripts/release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# Examples:
# ./scripts/release.sh minor # Minor release from develop
# ./scripts/release.sh major # Major release from develop
# ./scripts/release.sh patch release/2.3 # Patch release from release/2.3
# ./scripts/release.sh patch release/2.3._ # Patch release from release/2.3._
#
# Environment variables:
# DRY_RUN=true Show what would be done without triggering workflow
Expand Down Expand Up @@ -73,20 +73,20 @@ Arguments:
Release Types:
major Bump major version (e.g., 2.3.0-SNAPSHOT -> 3.0.0)
Next develop: 3.1.0-SNAPSHOT
Creates: release/3.0 branch
Creates: release/3.0._ branch

minor Release current version (e.g., 2.3.0-SNAPSHOT -> 2.3.0)
Next develop: 2.4.0-SNAPSHOT
Creates: release/2.3 branch
Creates: release/2.3._ branch

patch Bump patch version on release branch (e.g., 2.3.1-SNAPSHOT -> 2.3.1)
Next release branch: 2.3.2-SNAPSHOT
Requires: release/X.Y branch as source
Requires: release/X.Y._ branch as source

Examples:
$(basename "$0") minor # Release 2.3.0 from develop
$(basename "$0") major # Release 3.0.0 from develop
$(basename "$0") patch release/2.3 # Release 2.3.1 from release/2.3
$(basename "$0") patch release/2.3._ # Release 2.3.1 from release/2.3._

Environment:
DRY_RUN=true Show what would happen without triggering the workflow
Expand Down Expand Up @@ -224,10 +224,10 @@ pick_commit() {
pick_release_branch() {
# Get release branches sorted by version (newest first)
local branches
branches=$(git branch -a --list '*release/*' | sed 's/.*\(release\/[0-9]*\.[0-9]*\).*/\1/' | sort -t. -k1,1nr -k2,2nr | uniq)
branches=$(git branch -a --list '*release/*' | sed 's/.*\(release\/[0-9]*\.[0-9]*\._\).*/\1/' | sort -t. -k1,1nr -k2,2nr | uniq)

if [[ -z "${branches}" ]]; then
error "No release branches found (expected pattern: release/X.Y)"
error "No release branches found (expected pattern: release/X.Y._)"
exit 1
fi

Expand Down Expand Up @@ -410,21 +410,21 @@ calculate_versions() {
release_version="$((major + 1)).0.0"
next_develop_snapshot="$((major + 1)).1.0-SNAPSHOT"
next_release_snapshot="$((major + 1)).0.1-SNAPSHOT"
release_branch="release/$((major + 1)).0"
release_branch="release/$((major + 1)).0._"
;;
minor)
# Minor: X.Y.Z-SNAPSHOT -> X.Y.Z (just drop SNAPSHOT)
release_version="${major}.${minor}.${patch}"
# Minor: X.Y.Z-SNAPSHOT -> X.Y.0 (always .0 for minor releases)
release_version="${major}.${minor}.0"
next_develop_snapshot="${major}.$((minor + 1)).0-SNAPSHOT"
next_release_snapshot="${major}.${minor}.$((patch + 1))-SNAPSHOT"
release_branch="release/${major}.${minor}"
next_release_snapshot="${major}.${minor}.1-SNAPSHOT"
release_branch="release/${major}.${minor}._"
;;
patch)
# Patch: X.Y.Z-SNAPSHOT -> X.Y.Z
release_version="${major}.${minor}.${patch}"
next_develop_snapshot="" # Not updated for patch releases
next_release_snapshot="${major}.${minor}.$((patch + 1))-SNAPSHOT"
release_branch="release/${major}.${minor}"
release_branch="release/${major}.${minor}._"
;;
*)
error "Invalid release type: ${release_type}"
Expand Down Expand Up @@ -463,8 +463,8 @@ validate_source_ref() {
fi
;;
patch)
# Must be from a release/X.Y branch or a commit from one
if [[ "${source_ref}" =~ ^release/[0-9]+\.[0-9]+$ ]]; then
# Must be from a release/X.Y._ branch or a commit from one
if [[ "${source_ref}" =~ ^release/[0-9]+\.[0-9]+\._$ ]]; then
# It's a branch name, check if it exists
if ! git show-ref --verify --quiet "refs/heads/${source_ref}" && \
! git show-ref --verify --quiet "refs/remotes/origin/${source_ref}"; then
Expand All @@ -480,7 +480,7 @@ validate_source_ref() {
fi
fi
else
error "Patch releases must be from a release/X.Y branch."
error "Patch releases must be from a release/X.Y._ branch."
error "Got: ${source_ref}"
exit 1
fi
Expand All @@ -506,6 +506,19 @@ check_tag_exists() {
error "Tag v${tag} already exists on remote."
exit 1
fi

# Check for leftover RC tag (locally and on remote)
if git rev-parse "v${tag}_RC" &> /dev/null; then
error "RC tag v${tag}_RC already exists locally."
error "Clean up with: git tag -d v${tag}_RC"
exit 1
fi

if git ls-remote --tags origin "refs/tags/v${tag}_RC" | grep -q .; then
error "RC tag v${tag}_RC already exists on remote."
error "Clean up with: git push origin :refs/tags/v${tag}_RC && git tag -d v${tag}_RC"
exit 1
fi
}

#######################################
Expand Down Expand Up @@ -642,7 +655,7 @@ main() {
;;
patch)
error "Patch releases require a release branch."
echo "Usage: $(basename "$0") patch release/X.Y"
echo "Usage: $(basename "$0") patch release/X.Y._"
exit 1
;;
esac
Expand Down
Loading