diff --git a/.github/workflows/create-release-pr.yml b/.github/workflows/create-release-pr.yml new file mode 100644 index 000000000..8b71fb66d --- /dev/null +++ b/.github/workflows/create-release-pr.yml @@ -0,0 +1,210 @@ +name: Create Release PR + +on: + workflow_dispatch: + inputs: + release_version: + description: 'The release_version used for the release branch name, e.g. release/vx.x.x' + default: 'vx.x.x' + required: true + type: string + pre_release_version: + description: "Pre-Release version, e.g. 'beta.1'" + required: false + type: string + +env: + RELEASE_VERSION: ${{ inputs.release_version }} + PRE_RELEASE_VERSION: ${{ inputs.pre_release_version }} + RELEASE_BRANCH: release/${{ inputs.release_version }} + +jobs: + create-release-pr: + runs-on: ubuntu-latest + + steps: + - name: Set Release Version and Branch to Check Out + id: set-release + run: | + if [[ $RELEASE_VERSION =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + if [[ $PRE_RELEASE_VERSION =~ ^[a-z.0-9]+$ ]]; then + echo "release-tag: $RELEASE_VERSION-$PRE_RELEASE_VERSION" + echo "release-tag=$RELEASE_VERSION-$PRE_RELEASE_VERSION" >> $GITHUB_OUTPUT + elif [[ -n $PRE_RELEASE_VERSION ]]; then + echo "Input pre_release_version is not empty, but does not match the regex pattern ^[-a-z0-9]+$" + exit 1 + else + echo "release-tag: $RELEASE_VERSION" + echo "release-tag=$RELEASE_VERSION" >> $GITHUB_OUTPUT + fi + else + echo "Version input doesn't match the regex pattern ^[0-9]+\.[0-9]+\.[0-9]+$" + exit 1 + fi + + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Create Release Branch if it does not exist + run: | + if ! git show-ref --verify --quiet "refs/remotes/origin/$RELEASE_BRANCH"; then + git checkout -b $RELEASE_BRANCH + git push --set-upstream origin $RELEASE_BRANCH + elif [[ $(git rev-parse --abbrev-ref HEAD) != "$RELEASE_BRANCH" ]]; then + echo "Current Branch: $(git rev-parse --abbrev-ref HEAD)" + echo "Release branch exists, make sure you're using the workflow from the release branch or delete the existing release branch." + exit 1 + else + echo "Release branch exists and used as workflow ref." + fi + + - name: Get Latest Release + id: get-release + run: | + if [[ -n $PRE_RELEASE_VERSION ]]; then + echo "Get the latest release" + tag=$(curl -L \ + --header "Accept: application/vnd.github.v3+json" \ + "https://api.github.com/repos/${{ github.repository }}/releases" | jq -r '.[0].tag_name') + echo "latest-tag=$tag" >> $GITHUB_OUTPUT + else + echo "Get the latest stable release" + tag=$(curl -L \ + --header "Accept: application/vnd.github.v3+json" \ + "https://api.github.com/repos/${{ github.repository }}/releases/latest" | jq -r '.tag_name') + echo "latest-tag=$tag" >> $GITHUB_OUTPUT + fi + + - name: Build Changelog + id: build-changelog + env: + PREVIOUS_VERSION: ${{ steps.get-release.outputs.latest-tag }} + run: | + CHANGELOG=$(curl -L \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ github.token }}"\ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/${{ github.repository }}/releases/generate-notes \ + -d '{"tag_name":"${{ env.RELEASE_VERSION }}","target_commitish":"${{ env.RELEASE_BRANCH }}","previous_tag_name":"${{ env.PREVIOUS_VERSION }}","configuration_file_path":".github/release.yml"}' \ + | jq -r '.body') + + # The EOF steps are used to save multiline string in github: + # https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#example-of-a-multiline-string + EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) + echo "changelog<<$EOF" >> $GITHUB_OUTPUT + echo -e "${CHANGELOG}" >> $GITHUB_OUTPUT + echo "$EOF" >> $GITHUB_OUTPUT + + - name: Update Changelog + if: ${{ env.PRE_RELEASE_VERSION == '' }} + env: + CHANGELOG_CONTENT: ${{ steps.build-changelog.outputs.changelog }} + PREVIOUS_VERSION: ${{ steps.get-release.outputs.latest-tag }} + run: | + echo -e "# ${RELEASE_VERSION}\n\n${CHANGELOG_CONTENT}\n" | cat - CHANGELOG.md > temp && mv temp CHANGELOG.md + + - name: Use Node.js + uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Update Version References in Source + env: + RELEASE_TAG: ${{ steps.set-release.outputs.release-tag }} + run: | + rm -rf dist node_modules + npm ci + npm run build + NEW_HASH=$(cat dist/browser/algosdk.min.js | openssl dgst -sha384 -binary | openssl base64 -A) + python3 scripts/bump_version.py ${RELEASE_TAG:1} --new_hash "${NEW_HASH}" + npx prettier --write . + + - name: Commit Changes + uses: EndBug/add-and-commit@v9.1.3 + env: + RELEASE_TAG: ${{ steps.set-release.outputs.release-tag }} + with: + message: "bump up version to ${{ env.RELEASE_TAG }}" + + - name: Create Pull Request to Master + env: + CHANGELOG_CONTENT: ${{ steps.build-changelog.outputs.changelog }} + PREVIOUS_VERSION: ${{ steps.get-release.outputs.latest-tag }} + GH_TOKEN: ${{ github.token }} + RELEASE_TAG: ${{ steps.set-release.outputs.release-tag }} + run: | + # Note: There's an issue adding teams as reviewers, see https://github.com/cli/cli/issues/6395 + PULL_REQUEST_URL=$(gh pr create --base "master" \ + --title "FOR REVIEW ONLY: ${{ github.event.repository.name }} $RELEASE_TAG" \ + --label "Skip-Release-Notes" \ + --label "Team Hyper Flow" \ + --body "${CHANGELOG_CONTENT}" | tail -n 1) + if [[ $PULL_REQUEST_URL =~ ^https://github.com/${{ github.repository }}/pull/[0-9]+$ ]]; then + PULL_REQUEST_NUM=$(echo $PULL_REQUEST_URL | sed 's:.*/::') + echo "pull-request-master=$PULL_REQUEST_URL" >> $GITHUB_ENV + echo "pull-request-master-num=$PULL_REQUEST_NUM" >> $GITHUB_ENV + echo "Pull request to Master created: $PULL_REQUEST_URL" + else + echo "There was an issue creating the pull request to master branch." + exit 1 + fi + + - name: Create Pull Request to Develop + if: ${{ env.PRE_RELEASE_VERSION == '' }} + env: + GH_TOKEN: ${{ github.token }} + RELEASE_TAG: ${{ steps.set-release.outputs.release-tag }} + run: | + # Note: There's an issue adding teams as reviewers, see https://github.com/cli/cli/issues/6395 + PULL_REQUEST_URL=$(gh pr create --base "develop" \ + --title "FOR REVIEW ONLY: Merge back ${{ github.event.repository.name }} $RELEASE_TAG to develop" \ + --label "Skip-Release-Notes" \ + --label "Team Hyper Flow" \ + --body "Merge back version changes to develop." | tail -n 1) + if [[ $PULL_REQUEST_URL =~ ^https://github.com/${{ github.repository }}/pull/[0-9]+$ ]]; then + echo "Pull request to Develop created: $PULL_REQUEST_URL" + DEVELOP_PR_MESSAGE="\nPull Request to develop: $PULL_REQUEST_URL" + echo "pull-request-develop-message=$DEVELOP_PR_MESSAGE" >> $GITHUB_ENV + else + echo "There was an issue creating the pull request to develop branch." + exit 1 + fi + + - name: Send Slack Message + id: slack + uses: slackapi/slack-github-action@v1.24.0 + env: + RELEASE_TAG: ${{ steps.set-release.outputs.release-tag }} + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK + SDK_DEPLOYMENT_URL: ${{ secrets.SDK_DEPLOYMENT_URL }} + with: + payload: | + { + "blocks": [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "${{ github.event.repository.name }} Release PR for ${{ env.RELEASE_TAG }}" + } + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*Approvals needed for*:\nPull Request to master: ${{ env.pull-request-master}}${{ env.pull-request-develop-message }}" + } + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*After approvals*\nDeploy SDK using the <${{ env.SDK_DEPLOYMENT_URL }}|Deployment Pipeline> with the following parameters:\n*SDK*: ${{ github.event.repository.name }}\n*RELEASE_PR_NUM*: ${{ env.pull-request-master-num }}\n*RELEASE_VERSION*: ${{ env.RELEASE_VERSION }}\n*PRE_RELEASE_VERSION*: ${{ env.PRE_RELEASE_VERSION }}" + } + } + ] + } diff --git a/scripts/bump_version.py b/scripts/bump_version.py new file mode 100755 index 000000000..b201b8cce --- /dev/null +++ b/scripts/bump_version.py @@ -0,0 +1,82 @@ +# This script bumps up the version in `pom.xml` and `README.md` for new releases. +# Usage: python bump_version.py {new_version} (--new_hash +# --read_me --package_json +# --package-lock_json ) + +import argparse +import re +import json +import sys + + +def check_version(new_version): + if not re.fullmatch(r"[0-9]+\.[0-9]+\.[-a-z.0-9]+", new_version): + sys.exit( + "The version does not match the regex(major.minor.patch): [0-9]+\.[0-9]+\.[-a-z.0-9]+" + ) + + +def bump_package_json(new_version, file_path): + with open(file_path, "r") as file: + content = json.load(file) + + content["version"] = new_version + + with open(file_path, "w") as file: + json.dump(content, file, indent=2) + + +def bump_package_lock_json(new_version, file_path): + with open(file_path, "r") as file: + content = json.load(file) + + content["version"] = new_version + content["packages"][""]["version"] = new_version + + with open(file_path, "w") as file: + json.dump(content, file, indent=2) + + +def update_read_me(new_version, new_hash, file_path): + with open(file_path, "r") as file: + content = file.read() + + # Replace version + new_content = re.sub( + "algosdk@v[0-9]+\.[0-9]+\.[-a-z.0-9]+", + f"algosdk@v{new_version}", + content, + ) + # Replace hash + new_content = re.sub( + 'integrity="sha384-.*?"', f'integrity="sha384-{new_hash}"', new_content + ) + + with open(file_path, "w") as file: + file.write(new_content) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="updates the version for a release and the integrity hash in the README.md, package.json, and package-lock.json", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument("new_version", help="new version as major.minor.patch") + parser.add_argument( + "--new_hash", required=True, help="new integrity hash for the build" + ) + parser.add_argument( + "--package_json", default="package.json", help="path to package.json" + ) + parser.add_argument( + "--package_lock_json", + default="package-lock.json", + help="path to package-lock.json", + ) + parser.add_argument("--read_me", default="README.md", help="path to readme") + + args = parser.parse_args() + check_version(args.new_version) + bump_package_json(args.new_version, args.package_json) + bump_package_lock_json(args.new_version, args.package_lock_json) + update_read_me(args.new_version, args.new_hash, args.read_me)