Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge main into releases/v2 #2020

Merged
merged 25 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8a1e474
Update changelog and version after v2.22.8
github-actions[bot] Nov 23, 2023
3b5bd49
Update checked-in dependencies
github-actions[bot] Nov 23, 2023
2da9ad5
Merge pull request #2008 from github/mergeback/v2.22.8-to-main-407ffafa
aibaars Nov 23, 2023
4712487
Bump the npm group with 5 updates (#2010)
dependabot[bot] Nov 27, 2023
773bd9b
Exclude Dependabot updates workflow from required PR checks
henrymercer Nov 27, 2023
77605c7
Remove LGTM
henrymercer Nov 27, 2023
db40ac4
Merge pull request #2011 from github/henrymercer/exclude-dependabot-r…
henrymercer Nov 28, 2023
1d367b0
Improve error categorizations
henrymercer Dec 1, 2023
b929cca
Merge pull request #2012 from github/henrymercer/categorize-configura…
henrymercer Dec 1, 2023
cc940d7
Extract scanned languages using `--index-traceless-dbs`
henrymercer Dec 1, 2023
a16ac98
Merge pull request #2013 from github/henrymercer/index-traceless-dbs
henrymercer Dec 4, 2023
b7f289e
update release process to support multiple version
nickfyson Dec 4, 2023
bc50092
Bump the npm group with 4 updates (#2015)
dependabot[bot] Dec 4, 2023
2dbffae
add note in CONTRIBUTING.md on how to deprecate an action version
nickfyson Dec 4, 2023
65a2bb5
Merge branch 'main' into nickfyson/update-release-process
nickfyson Dec 4, 2023
784783d
Update supported GitHub Enterprise Server versions
cklin Dec 5, 2023
3675be0
Merge pull request #2017 from cklin/update-supported-enterprise-serve…
cklin Dec 5, 2023
3537bea
Apply suggestions from code review
nickfyson Dec 6, 2023
a6ea3c5
define backport commit message in constant
nickfyson Dec 6, 2023
57932be
remove unused function
nickfyson Dec 6, 2023
ee748cf
respond to more review comments
nickfyson Dec 6, 2023
47e90f2
Merge branch 'main' into nickfyson/update-release-process
nickfyson Dec 6, 2023
0e9a210
update workflows to run on all release branches
nickfyson Dec 6, 2023
e1d1fad
Merge pull request #2014 from github/nickfyson/update-release-process
nickfyson Dec 7, 2023
c6e24c9
Update changelog for v2.22.9
github-actions[bot] Dec 7, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/actions/release-branches/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: 'Release branches'
description: 'Determine branches for release & backport'
inputs:
major_version:
description: 'The version as extracted from the package.json file'
required: true
latest_tag:
description: 'The most recent tag published to the repository'
required: true
outputs:
backport_source_branch:
description: "The release branch for the given tag"
value: ${{ steps.branches.outputs.backport_source_branch }}
backport_target_branches:
description: "JSON encoded list of branches to target with backports"
value: ${{ steps.branches.outputs.backport_target_branches }}
runs:
using: "composite"
steps:
- id: branches
run: |
python ${{ github.action_path }}/release-branches.py \
--major-version ${{ inputs.major_version }} \
--latest-tag ${{ inputs.latest_tag }}
shell: bash
48 changes: 48 additions & 0 deletions .github/actions/release-branches/release-branches.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import argparse
import json
import os
import subprocess

# Name of the remote
ORIGIN = 'origin'

OLDEST_SUPPORTED_MAJOR_VERSION = 2

def main():

parser = argparse.ArgumentParser()
parser.add_argument("--major-version", required=True, type=str, help="The major version of the release")
parser.add_argument("--latest-tag", required=True, type=str, help="The most recent tag published to the repository")
args = parser.parse_args()

major_version = args.major_version
latest_tag = args.latest_tag

print("major_version: " + major_version)
print("latest_tag: " + latest_tag)

# If this is a primary release, we backport to all supported branches,
# so we check whether the major_version taken from the package.json
# is greater than or equal to the latest tag pulled from the repo.
# For example...
# 'v1' >= 'v2' is False # we're operating from an older release branch and should not backport
# 'v2' >= 'v2' is True # the normal case where we're updating the current version
# 'v3' >= 'v2' is True # in this case we are making the first release of a new major version
consider_backports = ( major_version >= latest_tag.split(".")[0] )

with open(os.environ["GITHUB_OUTPUT"], "a") as f:

f.write(f"backport_source_branch=releases/{major_version}\n")

backport_target_branches = []

if consider_backports:
for i in range(int(major_version.strip("v"))-1, 0, -1):
branch_name = f"releases/v{i}"
if i >= OLDEST_SUPPORTED_MAJOR_VERSION:
backport_target_branches.append(branch_name)

f.write("backport_target_branches="+json.dumps(backport_target_branches)+"\n")

if __name__ == "__main__":
main()
33 changes: 33 additions & 0 deletions .github/actions/release-initialise/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: 'Prepare release job'
description: 'Prepare for updating a release branch'

runs:
using: "composite"
steps:

- name: Dump environment
run: env
shell: bash

- name: Dump GitHub context
env:
GITHUB_CONTEXT: '${{ toJson(github) }}'
run: echo "$GITHUB_CONTEXT"
shell: bash

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.8

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install PyGithub==1.55 requests
shell: bash

- name: Update git config
run: |
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
shell: bash
169 changes: 143 additions & 26 deletions .github/update-release-branch.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@

"""

SOURCE_BRANCH = 'main'
TARGET_BRANCH = 'releases/v2'
# NB: This exact commit message is used to find commits for reverting during backports.
# Changing it requires a transition period where both old and new versions are supported.
BACKPORT_COMMIT_MESSAGE = 'Update version and changelog for v'

# Name of the remote
ORIGIN = 'origin'
Expand All @@ -34,7 +35,9 @@ def branch_exists_on_remote(branch_name):
return run_git('ls-remote', '--heads', ORIGIN, branch_name).strip() != ''

# Opens a PR from the given branch to the target branch
def open_pr(repo, all_commits, source_branch_short_sha, new_branch_name, conductor):
def open_pr(
repo, all_commits, source_branch_short_sha, new_branch_name, source_branch, target_branch,
conductor, is_primary_release, conflicted_files):
# Sort the commits into the pull requests that introduced them,
# and any commits that don't have a pull request
pull_requests = []
Expand All @@ -56,7 +59,7 @@ def open_pr(repo, all_commits, source_branch_short_sha, new_branch_name, conduct

# Start constructing the body text
body = []
body.append(f'Merging {source_branch_short_sha} into {TARGET_BRANCH}.')
body.append(f'Merging {source_branch_short_sha} into {target_branch}.')

body.append('')
body.append(f'Conductor for this PR is @{conductor}.')
Expand All @@ -79,20 +82,38 @@ def open_pr(repo, all_commits, source_branch_short_sha, new_branch_name, conduct

body.append('')
body.append('Please do the following:')
if len(conflicted_files) > 0:
body.append(' - [ ] Ensure `package.json` file contains the correct version.')
body.append(' - [ ] Add commits to this branch to resolve the merge conflicts ' +
'in the following files:')
body.extend([f' - [ ] `{file}`' for file in conflicted_files])
body.append(' - [ ] Ensure another maintainer has reviewed the additional commits you added to this ' +
'branch to resolve the merge conflicts.')
body.append(' - [ ] Ensure the CHANGELOG displays the correct version and date.')
body.append(' - [ ] Ensure the CHANGELOG includes all relevant, user-facing changes since the last release.')
body.append(f' - [ ] Check that there are not any unexpected commits being merged into the {TARGET_BRANCH} branch.')
body.append(f' - [ ] Check that there are not any unexpected commits being merged into the {target_branch} branch.')
body.append(' - [ ] Ensure the docs team is aware of any documentation changes that need to be released.')

if not is_primary_release:
body.append(' - [ ] Remove and re-add the "Update dependencies" label to the PR to trigger just this workflow.')
body.append(' - [ ] Wait for the "Update dependencies" workflow to push a commit updating the dependencies.')

body.append(' - [ ] Mark the PR as ready for review to trigger the full set of PR checks.')
body.append(' - [ ] Approve and merge this PR. Make sure `Create a merge commit` is selected rather than `Squash and merge` or `Rebase and merge`.')
body.append(' - [ ] Merge the mergeback PR that will automatically be created once this PR is merged.')

title = f'Merge {SOURCE_BRANCH} into {TARGET_BRANCH}'
if is_primary_release:
body.append(' - [ ] Merge the mergeback PR that will automatically be created once this PR is merged.')
body.append(' - [ ] Merge all backport PRs to older release branches, that will automatically be created once this PR is merged.')

title = f'Merge {source_branch} into {target_branch}'
labels = ['Update dependencies'] if not is_primary_release else []

# Create the pull request
# PR checks won't be triggered on PRs created by Actions. Therefore mark the PR as draft so that
# a maintainer can take the PR out of draft, thereby triggering the PR checks.
pr = repo.create_pull(title=title, body='\n'.join(body), head=new_branch_name, base=TARGET_BRANCH, draft=True)
print(f'Created PR #{pr.number}')
pr = repo.create_pull(title=title, body='\n'.join(body), head=new_branch_name, base=target_branch, draft=True)
pr.add_to_labels(*labels)
print(f'Created PR #{str(pr.number)}')

# Assign the conductor
pr.add_to_assignees(conductor)
Expand All @@ -102,10 +123,10 @@ def open_pr(repo, all_commits, source_branch_short_sha, new_branch_name, conduct
# since the last release to the target branch.
# This will not include any commits that exist on the target branch
# that aren't on the source branch.
def get_commit_difference(repo):
def get_commit_difference(repo, source_branch, target_branch):
# Passing split nothing means that the empty string splits to nothing: compare `''.split() == []`
# to `''.split('\n') == ['']`.
commits = run_git('log', '--pretty=format:%H', f'{ORIGIN}/{TARGET_BRANCH}..{ORIGIN}/{SOURCE_BRANCH}').strip().split()
commits = run_git('log', '--pretty=format:%H', f'{ORIGIN}/{target_branch}..{ORIGIN}/{source_branch}').strip().split()

# Convert to full-fledged commit objects
commits = [repo.get_commit(c) for c in commits]
Expand Down Expand Up @@ -182,6 +203,24 @@ def main():
required=True,
help='The nwo of the repository, for example github/codeql-action.'
)
parser.add_argument(
'--source-branch',
type=str,
required=True,
help='Source branch for release branch update.'
)
parser.add_argument(
'--target-branch',
type=str,
required=True,
help='Target branch for release branch update.'
)
parser.add_argument(
'--is-primary-release',
action='store_true',
default=False,
help='Whether this update is the primary release for the current major version.'
)
parser.add_argument(
'--conductor',
type=str,
Expand All @@ -191,18 +230,29 @@ def main():

args = parser.parse_args()

source_branch = args.source_branch
target_branch = args.target_branch
is_primary_release = args.is_primary_release

repo = Github(args.github_token).get_repo(args.repository_nwo)
version = get_current_version()

# the target branch will be of the form releases/vN, where N is the major version number
target_branch_major_version = target_branch.strip('releases/v')

# split version into major, minor, patch
_, v_minor, v_patch = get_current_version().split('.')

version = f"{target_branch_major_version}.{v_minor}.{v_patch}"

# Print what we intend to go
print(f'Considering difference between {SOURCE_BRANCH} and {TARGET_BRANCH}...')
source_branch_short_sha = run_git('rev-parse', '--short', f'{ORIGIN}/{SOURCE_BRANCH}').strip()
print(f'Current head of {SOURCE_BRANCH} is {source_branch_short_sha}.')
print(f'Considering difference between {source_branch} and {target_branch}...')
source_branch_short_sha = run_git('rev-parse', '--short', f'{ORIGIN}/{source_branch}').strip()
print(f'Current head of {source_branch} is {source_branch_short_sha}.')

# See if there are any commits to merge in
commits = get_commit_difference(repo=repo)
commits = get_commit_difference(repo=repo, source_branch=source_branch, target_branch=target_branch)
if len(commits) == 0:
print(f'No commits to merge from {SOURCE_BRANCH} to {TARGET_BRANCH}.')
print(f'No commits to merge from {source_branch} to {target_branch}.')
return

# The branch name is based off of the name of branch being merged into
Expand All @@ -220,17 +270,80 @@ def main():
# Create the new branch and push it to the remote
print(f'Creating branch {new_branch_name}.')

# If we're performing a standard release, there won't be any new commits on the target branch,
# as these will have already been merged back into the source branch. Therefore we can just
# start from the source branch.
run_git('checkout', '-b', new_branch_name, f'{ORIGIN}/{SOURCE_BRANCH}')
# The process of creating the v{Older} release can run into merge conflicts. We commit the unresolved
# conflicts so a maintainer can easily resolve them (vs erroring and requiring maintainers to
# reconstruct the release manually)
conflicted_files = []

if not is_primary_release:

# the source branch will be of the form releases/vN, where N is the major version number
source_branch_major_version = source_branch.strip('releases/v')

# If we're performing a backport, start from the target branch
print(f'Creating {new_branch_name} from the {ORIGIN}/{target_branch} branch')
run_git('checkout', '-b', new_branch_name, f'{ORIGIN}/{target_branch}')

# Revert the commit that we made as part of the last release that updated the version number and
# changelog to refer to {older}.x.x variants. This avoids merge conflicts in the changelog and
# package.json files when we merge in the v{latest} branch.
# This commit will not exist the first time we release the v{N-1} branch from the v{N} branch, so we
# use `git log --grep` to conditionally revert the commit.
print('Reverting the version number and changelog updates from the last release to avoid conflicts')
vOlder_update_commits = run_git('log', '--grep', f'^{BACKPORT_COMMIT_MESSAGE}', '--format=%H').split()

if len(vOlder_update_commits) > 0:
print(f' Reverting {vOlder_update_commits[0]}')
# Only revert the newest commit as older ones will already have been reverted in previous
# releases.
run_git('revert', vOlder_update_commits[0], '--no-edit')

# Also revert the "Update checked-in dependencies" commit created by Actions.
update_dependencies_commit = run_git('log', '--grep', '^Update checked-in dependencies', '--format=%H').split()[0]
print(f' Reverting {update_dependencies_commit}')
run_git('revert', update_dependencies_commit, '--no-edit')

else:
print(' Nothing to revert.')

print(f'Merging {ORIGIN}/{source_branch} into the release prep branch')
# Commit any conflicts (see the comment for `conflicted_files`)
run_git('merge', f'{ORIGIN}/{source_branch}', allow_non_zero_exit_code=True)
conflicted_files = run_git('diff', '--name-only', '--diff-filter', 'U').splitlines()
if len(conflicted_files) > 0:
run_git('add', '.')
run_git('commit', '--no-edit')

# Migrate the package version number from a vLatest version number to a vOlder version number
print(f'Setting version number to {version}')
subprocess.check_output(['npm', 'version', version, '--no-git-tag-version'])
run_git('add', 'package.json', 'package-lock.json')

# Migrate the changelog notes from vLatest version numbers to vOlder version numbers
print(f'Migrating changelog notes from v{source_branch_major_version} to v{target_branch_major_version}')
subprocess.check_output(['sed', '-i', f's/^## {source_branch_major_version}\./## {target_branch_major_version}./g', 'CHANGELOG.md'])

# Remove changelog notes from all versions that do not apply to the vOlder branch
print(f'Removing changelog notes that do not apply to v{target_branch_major_version}')
for v in range(int(source_branch_major_version), int(target_branch_major_version), -1):
print(f'Removing changelog notes that are tagged [v{v}+ only\]')
subprocess.check_output(['sed', '-i', f'/^- \[v{v}+ only\]/d', 'CHANGELOG.md'])

# Amend the commit generated by `npm version` to update the CHANGELOG
run_git('add', 'CHANGELOG.md')
run_git('commit', '-m', f'{BACKPORT_COMMIT_MESSAGE}{version}')
else:
# If we're performing a standard release, there won't be any new commits on the target branch,
# as these will have already been merged back into the source branch. Therefore we can just
# start from the source branch.
run_git('checkout', '-b', new_branch_name, f'{ORIGIN}/{source_branch}')

print('Updating changelog')
update_changelog(version)
print('Updating changelog')
update_changelog(version)

# Create a commit that updates the CHANGELOG
run_git('add', 'CHANGELOG.md')
run_git('commit', '-m', f'Update changelog for v{version}')
# Create a commit that updates the CHANGELOG
run_git('add', 'CHANGELOG.md')
run_git('commit', '-m', f'Update changelog for v{version}')

run_git('push', ORIGIN, new_branch_name)

Expand All @@ -240,7 +353,11 @@ def main():
commits,
source_branch_short_sha,
new_branch_name,
source_branch=source_branch,
target_branch=target_branch,
conductor=args.conductor,
is_primary_release=is_primary_release,
conflicted_files=conflicted_files
)

if __name__ == '__main__':
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/__all-platform-bundle.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .github/workflows/__analyze-ref-input.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .github/workflows/__autobuild-action.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .github/workflows/__config-export.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.