From 14f5cf4a371ce092356dcaf93db7d1b1d7684d59 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Wed, 19 May 2021 11:51:25 -0700 Subject: [PATCH] Add release and mergeback workflows This commit ensures that the changelog is updated before a release with the correct date and version. Also, after a release, a mergeback PR is created to ensure that the changelog update and version bump is available in main. --- .../check-expected-release-files.yml | 0 .../{workflows => backupworkflows}/codeql.yml | 0 .../pr-checks.yml | 0 .../python-deps.yml | 0 .../release-runner.yml | 0 .../{workflows => backupworkflows}/split.yml | 0 .../update-release-branch.yml | 7 +- ...e-supported-enterprise-server-versions.yml | 0 .github/update-release-branch.py | 56 ++++++++- .github/workflows/post-release-mergeback.yml | 106 ++++++++++++++++++ 10 files changed, 164 insertions(+), 5 deletions(-) rename .github/{workflows => backupworkflows}/check-expected-release-files.yml (100%) rename .github/{workflows => backupworkflows}/codeql.yml (100%) rename .github/{workflows => backupworkflows}/pr-checks.yml (100%) rename .github/{workflows => backupworkflows}/python-deps.yml (100%) rename .github/{workflows => backupworkflows}/release-runner.yml (100%) rename .github/{workflows => backupworkflows}/split.yml (100%) rename .github/{workflows => backupworkflows}/update-release-branch.yml (84%) rename .github/{workflows => backupworkflows}/update-supported-enterprise-server-versions.yml (100%) create mode 100644 .github/workflows/post-release-mergeback.yml diff --git a/.github/workflows/check-expected-release-files.yml b/.github/backupworkflows/check-expected-release-files.yml similarity index 100% rename from .github/workflows/check-expected-release-files.yml rename to .github/backupworkflows/check-expected-release-files.yml diff --git a/.github/workflows/codeql.yml b/.github/backupworkflows/codeql.yml similarity index 100% rename from .github/workflows/codeql.yml rename to .github/backupworkflows/codeql.yml diff --git a/.github/workflows/pr-checks.yml b/.github/backupworkflows/pr-checks.yml similarity index 100% rename from .github/workflows/pr-checks.yml rename to .github/backupworkflows/pr-checks.yml diff --git a/.github/workflows/python-deps.yml b/.github/backupworkflows/python-deps.yml similarity index 100% rename from .github/workflows/python-deps.yml rename to .github/backupworkflows/python-deps.yml diff --git a/.github/workflows/release-runner.yml b/.github/backupworkflows/release-runner.yml similarity index 100% rename from .github/workflows/release-runner.yml rename to .github/backupworkflows/release-runner.yml diff --git a/.github/workflows/split.yml b/.github/backupworkflows/split.yml similarity index 100% rename from .github/workflows/split.yml rename to .github/backupworkflows/split.yml diff --git a/.github/workflows/update-release-branch.yml b/.github/backupworkflows/update-release-branch.yml similarity index 84% rename from .github/workflows/update-release-branch.yml rename to .github/backupworkflows/update-release-branch.yml index a4989fb892..ee66118097 100644 --- a/.github/workflows/update-release-branch.yml +++ b/.github/backupworkflows/update-release-branch.yml @@ -22,12 +22,17 @@ jobs: - name: Set up Python uses: actions/setup-python@v2 with: - python-version: 3.5 + python-version: 3.8 - name: Install dependencies run: | python -m pip install --upgrade pip pip install PyGithub==1.51 requests + - name: Update git config + run: | + git config --global user.email "codeql-core@github.com" + git config --global user.name "CodeQL Actions Bot" + - name: Update release branch run: python .github/update-release-branch.py ${{ secrets.GITHUB_TOKEN }} ${{ github.repository }} diff --git a/.github/workflows/update-supported-enterprise-server-versions.yml b/.github/backupworkflows/update-supported-enterprise-server-versions.yml similarity index 100% rename from .github/workflows/update-supported-enterprise-server-versions.yml rename to .github/backupworkflows/update-supported-enterprise-server-versions.yml diff --git a/.github/update-release-branch.py b/.github/update-release-branch.py index 119bd984a3..28e5858178 100644 --- a/.github/update-release-branch.py +++ b/.github/update-release-branch.py @@ -4,6 +4,16 @@ import requests import subprocess import sys +import json +import datetime +import os + +EMPTY_CHANGELOG = """ +# CodeQL Action: Changelog + +## [UNRELEASED] + +""" # The branch being merged from. # This is the one that contains day-to-day development work. @@ -71,6 +81,13 @@ def open_pr(repo, all_commits, short_main_sha, branch_name): body += ' - ' + get_truncated_commit_message(commit) body += ' (@' + commit.author.login + ')' + body += '\n\nPlease review the following:\n' + body += ' - [ ] The CHANGELOG displays the correct version and date.\n' + body += ' - [ ] The CHANGELOG includes all relevant, user-facing changes since the last release.\n' + body += ' - [ ] There are no unexpected commits being merged into the ' + LATEST_RELEASE_BRANCH + ' branch.\n' + body += ' - [ ] The docs team is aware of any documentation changes that need to be released.\n' + body += ' - [ ] The mergeback PR is merged back into ' + MAIN_BRANCH + ' after this PR is merged.\n' + title = 'Merge ' + MAIN_BRANCH + ' into ' + LATEST_RELEASE_BRANCH # Create the pull request @@ -95,7 +112,7 @@ def get_conductor(repo, pull_requests, other_commits): # This will not include any commits that exist on the release branch # that aren't on main. def get_commit_difference(repo): - commits = run_git('log', '--pretty=format:%H', ORIGIN + '/' + LATEST_RELEASE_BRANCH + '..' + MAIN_BRANCH).strip().split('\n') + commits = run_git('log', '--pretty=format:%H', ORIGIN + '/' + LATEST_RELEASE_BRANCH + '..' + ORIGIN + '/' + MAIN_BRANCH).strip().split('\n') # Convert to full-fledged commit objects commits = [repo.get_commit(c) for c in commits] @@ -135,6 +152,28 @@ def get_pr_for_commit(repo, commit): def get_merger_of_pr(repo, pr): return repo.get_commit(pr.merge_commit_sha).author.login +def get_current_version(): + with open('package.json', 'r') as f: + return json.load(f)['version'] + +def get_today_string(): + today = datetime.datetime.today() + return '{:%d %b %Y}'.format(today) + +def update_changelog(version): + if (os.path.exists('CHANGELOG.md')): + content = '' + with open('CHANGELOG.md', 'r') as f: + content = f.read() + else: + content = EMPTY_CHANGELOG + + newContent = content.replace('[UNRELEASED]', version + ' - ' + get_today_string(), 1) + + with open('CHANGELOG.md', 'w') as f: + f.write(newContent) + + def main(): if len(sys.argv) != 3: raise Exception('Usage: update-release.branch.py ') @@ -142,10 +181,11 @@ def main(): repository_nwo = sys.argv[2] repo = Github(github_token).get_repo(repository_nwo) + version = get_current_version() # Print what we intend to go print('Considering difference between ' + MAIN_BRANCH + ' and ' + LATEST_RELEASE_BRANCH) - short_main_sha = run_git('rev-parse', '--short', MAIN_BRANCH).strip() + short_main_sha = run_git('rev-parse', '--short', ORIGIN + '/' + MAIN_BRANCH).strip() print('Current head of ' + MAIN_BRANCH + ' is ' + short_main_sha) # See if there are any commits to merge in @@ -157,7 +197,7 @@ def main(): # The branch name is based off of the name of branch being merged into # and the SHA of the branch being merged from. Thus if the branch already # exists we can assume we don't need to recreate it. - new_branch_name = 'update-' + LATEST_RELEASE_BRANCH + '-' + short_main_sha + new_branch_name = 'update-v' + version + '-' + short_main_sha print('Branch name is ' + new_branch_name) # Check if the branch already exists. If so we can abort as this script @@ -168,7 +208,15 @@ def main(): # Create the new branch and push it to the remote print('Creating branch ' + new_branch_name) - run_git('checkout', '-b', new_branch_name, MAIN_BRANCH) + run_git('checkout', '-b', new_branch_name, ORIGIN + '/' + MAIN_BRANCH) + + print('Updating changelog') + update_changelog(version) + + # Create a commit that updates the CHANGELOG + run_git('add', 'CHANGELOG.md') + run_git('commit', '-m', version) + run_git('push', ORIGIN, new_branch_name) # Open a PR to update the branch diff --git a/.github/workflows/post-release-mergeback.yml b/.github/workflows/post-release-mergeback.yml new file mode 100644 index 0000000000..f95b1de2b3 --- /dev/null +++ b/.github/workflows/post-release-mergeback.yml @@ -0,0 +1,106 @@ +# This workflow runs after a release of the action. +# It merges any changes from the release back into the +# main branch. Typically, this is just a single commit +# that updates the changelog. +name: Mergeback after release + +on: + workflow_dispatch: + inputs: + baseBranch: + description: 'The base branch to merge into' + default: main + required: false + + # Commented for now to ensure we don't run this accidentally after a real merge + # push: + # branches: + # - v1 + + pull_request: + paths: + - .github/workflows/post-release-mergeback.yml + +jobs: + merge-back: + runs-on: ubuntu-latest + env: + BASE_BRANCH: "${{ github.event.inputs.baseBranch || 'main' }}" + # TODO head_ref won't exist in workflow dispatch + HEAD_BRANCH: "${{ github.head_ref }}" + NEW_BRANCH: "mergeback/${{ github.head_ref }}-to-${{ github.event.inputs.baseBranch || 'main' }}" + + steps: + - name: Dump GitHub context + env: + GITHUB_CONTEXT: '${{ toJson(github) }}' + run: echo "$GITHUB_CONTEXT" + + - name: Dump branches + run: | + echo "BASE_BRANCH $BASE_BRANCH" + echo "HEAD_BRANCH $HEAD_BRANCH" + echo "NEW_BRANCH $NEW_BRANCH" + + - name: validate + run: | + if [ "$BASE_BRANCH" -eq "$HEAD_BRANCH" ]; then + echo "error::Base branch and head branch are the same $BASE_BRANCH." + exit -1 + fi + + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + + - name: Update git config + run: | + git config --global user.email "codeql-core@github.com" + git config --global user.name "CodeQL Actions Bot" + + - name: Create mergeback branch + run: | + git checkout -b $NEW_BRANCH + + - name: Check for tag + id: check + run: | + set +e # don't fail on an errored command + VERSION="$(jq '.version' -r 'package.json')" + TAG="v$VERSION" + git ls-remote --tags origin | grep $TAG + EXISTS="$?" + if [ "$EXISTS" -ne 0 ]; then + echo "::set-output name=exists::true" + echo "Tag $TAG exists. Not going to re-release." + fi + + # we didn't tag the release during the update-release-branch workflow because the + # commit that actually makes it to the main branch may be different than the one + # created by the workflow. We tag now because we know the correct commit + - name: Tag release + if: steps.check.outputs.exists == 'true' + run: | + VERSION="$(jq '.version' -r 'package.json')" + git tag -a "v$VERSION" -m "v$VERSION" + git push origin --follow-tags "v$VERSION" + + - name: Create mergeback branch + if: steps.check.outputs.exists == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -exu + PR_TITLE="Mergeback $HEAD_BRANCH into $BASE_BRANCH" + PR_BODY="Updates version and changelog." + + # Update the changelog + perl -i -pe 's/^/## \[UNRELEASED\]\n\n/ if($.==3)' CHANGELOG.md + git add CHANGELOG.md + git commit -m "Update changelog for new version" + npm version + + # Create pull request TODO: choose a better reviewer + git push origin "$NEW_BRANCH" + + # TODO aeisenberg isn't the right reviewer, would like to specify the github/codeql-core team, but not working right now. + gh pr create --head "$NEW_BRANCH" --base "$BASE_BRANCH" --reviewer "aeisenberg" --title "$PR_TITLE" --body "$PR_BODY"