Release #14
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Release | |
on: | |
workflow_dispatch: | |
inputs: | |
branch: | |
type: string | |
description: Branch name to release | |
required: true | |
default: main | |
section: | |
type: choice | |
description: Version section to increment | |
options: | |
- PATCH | |
- MINOR | |
- MAJOR | |
required: true | |
default: PATCH | |
version: | |
type: string | |
description: | | |
...or manually define version like 1.2.3, 1.2.3-suffix | |
required: false | |
jobs: | |
release: | |
runs-on: ubuntu-latest | |
steps: | |
- name: Setup bot token | |
uses: actions/create-github-app-token@v1 | |
id: bot | |
with: | |
app-id: ${{ secrets.BOT_APP_ID }} | |
private-key: ${{ secrets.BOT_PRIVATE_KEY }} | |
- name: Configure bot git account | |
id: get-user-id | |
env: | |
GH_TOKEN: ${{ steps.bot.outputs.token }} | |
run: | | |
USER_ID="$(gh api "/users/${{ steps.bot.outputs.app-slug }}[bot]" --jq .id)" | |
USER_EMAIL="$USER_ID+${{ steps.bot.outputs.app-slug }}[bot]@users.noreply.github.com" | |
USER_NAME="${{ steps.bot.outputs.app-slug }}[bot]" | |
git config --global user.name "$USER_NAME" | |
git config --global user.email "$USER_EMAIL" | |
- name: Checkout | |
uses: actions/checkout@v4 | |
with: | |
ref: "refs/heads/${{ inputs.branch }}" | |
token: ${{ steps.bot.outputs.token }} | |
# Required to deduce version from git tags | |
fetch-depth: 0 | |
- name: Validate build succeeded | |
env: | |
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
run: | | |
if git tag --points-at HEAD | grep -Eq "^v[0-9]+.[0-9]+.[0-9]+$"; then | |
echo "Last commit is a release commit." | |
exit 0 | |
fi | |
declare -r SHA="$(git rev-parse HEAD)" | |
declare -r RUNS="$(gh api \ | |
-H "Accept: application/vnd.github+json" \ | |
/repos/${{ github.repository }}/actions/runs?head_sha=$SHA)" | |
declare -r BUILD_SUCCESS="$(echo "$RUNS" | jq -r 'limit(1; .workflow_runs[] | select(.name == "Build" and .conclusion == "success")) | .conclusion')" | |
if [ -z "$BUILD_SUCCESS" ]; then | |
echo "Commit did not pass Build. Stopping release!" | |
exit 1 | |
fi | |
echo "Last commit passed build." | |
exit 0 | |
- name: Get versions | |
id: versions | |
env: | |
BRANCH: ${{ inputs.branch }} | |
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} | |
MANUAL_VERSION: ${{ inputs.version }} | |
INCREMENT_SECTION: ${{ inputs.section }} | |
run: | | |
declare -r GIT_VERSION="$(git tag -l 'v[0-9]*' --merged HEAD --sort=-v:refname | grep -E "^v[0-9]+.[0-9]+.[0-9]+$" | head -n 1 | cut -c2-)" | |
declare -r VERSION=${GIT_VERSION:-0.0.0} | |
declare -r MAJOR="$(echo "$VERSION" | cut -d. -f1)" | |
declare -r MINOR="$(echo "$VERSION" | cut -d. -f2)" | |
declare -r PATCH="$(echo "$VERSION" | cut -d. -f3)" | |
declare NEXT_VERSION="" | |
if [ -n "$MANUAL_VERSION" ]; then | |
if [[ "$MANUAL_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
NEXT_VERSION="$MANUAL_VERSION" | |
else | |
echo "Invalid manual version: $MANUAL_VERSION" >&2 | |
echo "Expected versions format: 1.2.3" >&2 | |
exit 1 | |
fi | |
elif [ "$INCREMENT_SECTION" == "PATCH" ]; then | |
NEXT_VERSION="$MAJOR.$MINOR.$(( PATCH + 1 ))" | |
elif [ "$INCREMENT_SECTION" == "MINOR" ]; then | |
NEXT_VERSION="$MAJOR.$(( MINOR + 1 )).0" | |
elif [ "$INCREMENT_SECTION" == "MAJOR" ]; then | |
NEXT_VERSION="$(( MAJOR + 1 )).0.0" | |
else | |
echo "Unrecognized option: $INCREMENT_SECTION" >&2 | |
exit 1 | |
fi | |
if [ "$BRANCH" != "$DEFAULT_BRANCH" ] && [ "$(echo "$NEXT_VERSION" | cut -d. -f1)" != "$MAJOR" ]; then | |
echo "New major version can be created only from the $DEFAULT_BRANCH branch" >&2 | |
exit 1 | |
fi | |
echo -e "VERSION: $VERSION\nNEXT_VERSION: $NEXT_VERSION" | |
echo "next_version=$NEXT_VERSION" >> $GITHUB_OUTPUT | |
echo "version=$VERSION" >> $GITHUB_OUTPUT | |
- name: Update version in files | |
id: commit | |
env: | |
PREV_VERSION: ${{ steps.versions.outputs.version }} | |
NEXT_VERSION: ${{ steps.versions.outputs.next_version }} | |
run: | | |
declare -r ESC_PREV_VERSION="${PREV_VERSION//./\\.}" | |
echo "Changing: $PREV_VERSION -> $NEXT_VERSION" | |
sed -i "s|${ESC_PREV_VERSION}|${NEXT_VERSION}|" README.md | |
sed -i "s|^version=${ESC_PREV_VERSION}|version=${NEXT_VERSION}|" gradle.properties | |
if [ "$NEXT_VERSION" == "${NEXT_VERSION%%-SNAPSHOT}" ]; then | |
if [ -n "$(git status --porcelain)" ]; then | |
git add -A | |
git commit -a -m "Update version $PREV_VERSION -> $NEXT_VERSION" -m "[ci skip]" | |
git push | |
echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT | |
else | |
echo "Nothing changed. Skipping commit." | |
fi | |
else | |
echo "Skipping committing updated version in config files for SNAPSHOT version." | |
fi | |
- name: Publish Release to Gradle Plugin Portal | |
id: publish | |
env: | |
NEXT_VERSION: ${{ steps.versions.outputs.next_version }} | |
GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }} | |
GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }} | |
run: | | |
./gradlew publishPlugins \ | |
-Pgradle.publish.key=$GRADLE_PUBLISH_KEY \ | |
-Pgradle.publish.secret=$GRADLE_PUBLISH_SECRET \ | |
-Pversion=$NEXT_VERSION | |
echo "### Published release version $NEXT_VERSION 🚀" | tee -a $GITHUB_STEP_SUMMARY | |
- name: Revert version commit on failure | |
if: failure() && steps.publish.conclusion == 'failure' | |
env: | |
COMMIT_SHA: ${{ steps.commit.outputs.sha }} | |
run: | | |
if [ -n "$COMMIT_SHA" ]; then | |
git revert "$COMMIT_SHA" | |
git push | |
echo "Reverted version commit: $COMMIT_SHA" | |
fi | |
- name: Create release tag | |
env: | |
NEXT_VERSION: ${{ steps.versions.outputs.next_version }} | |
run: | | |
declare -r TAG_NAME="v$NEXT_VERSION" | |
git tag "$TAG_NAME" | |
git push origin "$TAG_NAME" | |
echo "name=$TAG_NAME" >> $GITHUB_OUTPUT | |
- name: Create major branch | |
id: tag | |
env: | |
BRANCH: ${{ inputs.branch }} | |
NEXT_VERSION: ${{ steps.versions.outputs.next_version }} | |
PREV_VERSION: ${{ steps.versions.outputs.version }} | |
run: | | |
declare -r NEXT_MAJOR="$(echo "$NEXT_VERSION" | cut -d. -f1)" | |
declare -r PREV_MAJOR="$(echo "$PREV_VERSION" | cut -d. -f1)" | |
if [ "$NEXT_MAJOR" != "$PREV_MAJOR" ]; then | |
NEW_BRANCH="v${PREV_MAJOR}.x.x" | |
if git rev-parse --verify "$NEW_BRANCH" &>/dev/null; then | |
echo "Repository already contains branch for previous major version: $NEW_BRANCH" | |
else | |
git checkout -b "$NEW_BRANCH" | |
git push origin "$NEW_BRANCH" | |
git checkout "$BRANCH" | |
echo "Created branch for new major version: $NEW_BRANCH" | |
echo "### Created branch for previous major version $NEW_BRANCH 🚀" | tee -a $GITHUB_STEP_SUMMARY | |
fi | |
else | |
echo "Next and prev major versions are the same: $NEXT_MAJOR" | |
fi | |
- name: Generate release notes | |
id: notes | |
env: | |
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
PREV_VERSION: ${{ steps.versions.outputs.version }} | |
NEXT_VERSION: ${{ steps.versions.outputs.next_version }} | |
run: | | |
declare -r PREV_TAG_NAME="$([ "$PREV_VERSION" == "0.0.0" ] && echo "" || echo "v$PREV_VERSION")" | |
declare -r NOTES="$(gh api \ | |
--method POST \ | |
-H "Accept: application/vnd.github+json" \ | |
/repos/${{ github.repository }}/releases/generate-notes \ | |
-f tag_name="v$NEXT_VERSION" \ | |
-f previous_tag_name="$PREV_TAG_NAME" \ | |
| jq -r '.body')" | |
echo 'notes<<EOF' >> $GITHUB_OUTPUT | |
echo "$NOTES" >> $GITHUB_OUTPUT | |
echo 'EOF' >> $GITHUB_OUTPUT | |
- name: Create github release | |
if: steps.notes.conclusion == 'success' | |
uses: ncipollo/release-action@v1 | |
with: | |
allowUpdates: true | |
body: ${{ steps.notes.outputs.notes }} | |
draft: ${{ inputs.publish == 'SKIP' }} | |
tag: v${{ steps.versions.outputs.next_version }} | |
token: ${{ secrets.GITHUB_TOKEN }} |