diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000000..44e48e4eec --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,193 @@ +name: "Release" +run-name: Release new ${{ inputs.version-type }} from ${{ github.ref_name }} + +on: + workflow_dispatch: + inputs: + version-type: + description: "Version type" + required: true + type: choice + default: "alpha" + options: + - "alpha" + - "beta" + - "GA" + what-is-new: + description: "What's new: it should be a JSON-formatted list such as [\"feature 1\", \"feature 2\"]" + required: true + default: "[]" + type: string + +jobs: + prepare-version: + runs-on: ubuntu-24.04 + steps: + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.ACTIONS_APP_ID }} + private-key: ${{ secrets.ACTIONS_APP_PRIVATE_KEY }} + - name: Validate what's new + run: | + # We just check if handle this input using jq will not fail. + # If it does, the workflow will fail in a inconsistent way. + echo '${{ inputs.what-is-new }}' | jq -r '.[]' + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ steps.app-token.outputs.token }} + - name: Install semver tool + run: | + curl --fail -LO https://raw.githubusercontent.com/fsaintjacques/semver-tool/3.4.0/src/semver + chmod +x ./semver + - name: Compose release tag + run: | + source VERSION + + SHORT_VERSION="$VERSION_MAJOR.$VERSION_MINOR" + BASE_VERSION="$SHORT_VERSION.$VERSION_PATCH" + TAG="$BASE_VERSION" + + if [ "${{ inputs.version-type }}" == "alpha" ] || [ "${{ inputs.version-type }}" == "beta" ] ; then + TAG="$TAG-${{ inputs.version-type }}" + TAG_QUERY="$TAG*" + elif [ "${{ inputs.version-type }}" == "GA" ]; then + TAG_QUERY="$TAG" + else + echo "Invalid release type: ${{ inputs.version-type }}" + exit 1 + fi + + LAST_MATCH_TAG=$(git tag -l --sort=-v:refname "$TAG_QUERY" | head -n 1) + + if [ "${{ inputs.version-type }}" == "GA" ] && [ "$LAST_MATCH_TAG" != "" ]; then + echo "Release $LAST_MATCH_TAG already exists" + exit 1 + fi + + if [ "${{ inputs.version-type }}" == "alpha" ] || [ "${{ inputs.version-type }}" == "beta" ] ; then + SUFFIX_NUMBER="1" + + if [ "$LAST_MATCH_TAG" != "" ]; then + SUFFIX_NUMBER=$(./semver get prerel $LAST_MATCH_TAG | cut -d'.' -f2) + SUFFIX_NUMBER=$((SUFFIX_NUMBER + 1)) + fi + + TAG="$TAG.$SUFFIX_NUMBER" + fi + + echo "SHORT_VERSION=$SHORT_VERSION" >> $GITHUB_ENV + echo "PATCH_VERSION=$VERSION_PATCH" >> $GITHUB_ENV + echo "BASE_VERSION=$BASE_VERSION" >> $GITHUB_ENV + echo "RELEASE_TAG=$TAG" >> $GITHUB_ENV + echo "RELEASE_BRANCH=release/$TAG" >> $GITHUB_ENV + - name: Validate ${{ env.RELEASE_TAG }} tag + run: ./semver validate ${{ env.RELEASE_TAG }} + - name: Check if version ${{ env.BASE_VERSION }} is on the right branch + run: | + # Need to fetch dev branches first + git fetch --no-tags origin HEAD '+refs/heads/development/*:refs/remotes/origin/development/*' + + GIT_REMOTE=$(git remote) + BASE_BRANCH=$(git branch -a --list ${GIT_REMOTE}/development/${{ env.SHORT_VERSION }}) + if ! git merge-base --is-ancestor HEAD ${BASE_BRANCH}; then + echo "Version commit is not included in base branch ${BASE_BRANCH}" + exit 1 + fi + - name: Prepare branch + run: git checkout -b ${{ env.RELEASE_BRANCH }} + - name: Set the new version + run: | + if [ -z "${NEW_VERSION_SUFFIX}" ]; then + NEW_VERSION_SUFFIX=$(./semver get prerel ${{ env.RELEASE_TAG }}) + [ -n "$NEW_VERSION_SUFFIX" ] && NEW_VERSION_SUFFIX="-$NEW_VERSION_SUFFIX" + fi + sed -i "s/VERSION_SUFFIX=.*/VERSION_SUFFIX=$NEW_VERSION_SUFFIX/" VERSION + - name: Commit with requested version and push + run: | + git fsck + git gc + git add VERSION + git config --global user.email ${{ github.actor }}@scality.com + git config --global user.name ${{ github.actor }} + git commit -m "Release ${{ env.RELEASE_TAG }}" + git push --set-upstream origin ${{ env.RELEASE_BRANCH }} + - name: Create and push `${{ env.RELEASE_TAG }}` tag + run: | + BODY=$(mktemp) + + PATCH_MSG="" + if [ "${{ env.PATCH_VERSION }}" != "0" ]; then + PATCH_MSG="MetalK8s ${{ env.RELEASE_TAG }} is a patch release (see [the main release ${{ env.BASE_VERSION }}](https://github.com/scality/metalk8s/releases/${{ env.BASE_VERSION }}))." + fi + + cat > $BODY <<- EOM + MetalK8s ${{ env.RELEASE_TAG }} + === + + $PATCH_MSG + + Useful links + --- + + - [Documentation](https://metal-k8s.readthedocs.io/en/${{ env.RELEASE_TAG }}) + - [Upgrade notes](https://metal-k8s.readthedocs.io/en/${{ env.RELEASE_TAG }}/operation/upgrade.html) + - [Changelog](https://github.com/scality/metalk8s/blob/${{ env.RELEASE_TAG }}/CHANGELOG.md) + + EOM + + if [ '${{ inputs.what-is-new }}' != '' ]; then + echo "What's new" >> $BODY + echo "===" >> $BODY + echo "" >> $BODY + while IFS= read -r line; do + echo "- $line" >> $BODY + done <<< $(echo '${{ inputs.what-is-new }}' | jq -r '.[]') + echo "" >> $BODY + fi + + git tag -a "${{ env.RELEASE_TAG }}" -F $BODY + git push origin ${{ env.RELEASE_TAG }} + - name: Prepare branch to merge ${{ env.RELEASE_TAG }} tag + run: | + git checkout development/${{ env.SHORT_VERSION }} + git pull + git checkout -b release/merge/${{ env.RELEASE_TAG }} + git merge ${{ env.RELEASE_TAG }} --no-ff + + source VERSION + + if [ "${{ inputs.version-type }}" == "GA" ]; then + VERSION_PATCH=$((VERSION_PATCH + 1)) + sed -i "s/## Release ${{ env.BASE_VERSION }} (in development)/## Release $VERSION_MAJOR.$VERSION_MINOR.$VERSION_PATCH (in development)\n\n## Release ${{ env.BASE_VERSION }}/" CHANGELOG.md + git add CHANGELOG.md + + sed -i "s/VERSION_PATCH=.*/VERSION_PATCH=$VERSION_PATCH/" VERSION + fi + + VERSION_SUFFIX="-dev" + sed -i "s/VERSION_SUFFIX=.*/VERSION_SUFFIX=$VERSION_SUFFIX/" VERSION + + git add VERSION + git commit -m "Bump version to $VERSION_MAJOR.$VERSION_MINOR.$VERSION_PATCH$VERSION_SUFFIX" + git push --set-upstream origin release/merge/${{ env.RELEASE_TAG }} + - name: Create pull request + uses: actions/github-script@v7 + with: + script: | + const pr = await github.rest.pulls.create({ + owner: context.repo.owner, + repo: context.repo.repo, + head: "release/merge/${{ env.RELEASE_TAG }}", + base: "development/${{ env.SHORT_VERSION }}", + title: "Release version ${{ env.RELEASE_TAG }}" + }); + + await github.rest.issues.createComment({ + issue_number: pr.data.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: '/approve' + })