Update tooling to delete unmaintained branches
The tools/list_eol_stale_branches.sh script needs to handle unmaintained/* branches in addition to stable/* branches, the former also need to be deleted once the matching *-eol tag has been added to a repository. In addition it needs to handle the delta between series and branch names starting with 2023.1/antelope. Also add a script to abandon open reviews on unmaintained branches, which is a requirement in order to be able to delete them. Change-Id: Iba0a50ad0d92013362e2a5eca5a4b1968e6acaf9
This commit is contained in:
parent
933e99c395
commit
54c6dd50da
63
tools/abandon_patches_on_unmaintained_branch.sh
Executable file
63
tools/abandon_patches_on_unmaintained_branch.sh
Executable file
@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
# This script helps to abandon open patches when a given branch transitions
|
||||
# to Unmaintained or End of Life and all patches on stable/<series> branch
|
||||
# needs to be abandoned in order to be able to delete that branch.
|
||||
|
||||
set -e
|
||||
|
||||
if [[ $# -lt 2 ]]; then
|
||||
echo "Usage: $(basename $0) <branch> <repo> [<repo>...]"
|
||||
echo "repo should be e.g. glance"
|
||||
echo
|
||||
echo "Example: $(basename $0) yoga \$(list-deliverables --series yoga --is-eol)"
|
||||
echo
|
||||
echo " !!! WARNING: please do not run this script without discussing it"
|
||||
echo " first with release managers!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
series="$1"
|
||||
shift
|
||||
repos="$@"
|
||||
|
||||
function abandon_change {
|
||||
gitid=$1
|
||||
msg=$2
|
||||
commit_message=$(ssh review.opendev.org "gerrit query $gitid --current-patch-set --format json" | head -n1 | jq .subject)
|
||||
echo "Abandoning: $change -- $commit_message"
|
||||
ssh review.opendev.org gerrit review $gitid --abandon --message \"$msg\"
|
||||
}
|
||||
|
||||
|
||||
|
||||
for repo in $repos; do
|
||||
echo "Processing repository: $repo..."
|
||||
open_changes=$(
|
||||
ssh review.opendev.org "gerrit query --current-patch-set --format json \
|
||||
status:open branch:unmaintained/${series} project:openstack/${repo}" | \
|
||||
jq .currentPatchSet.revision | grep -v null | sed 's/"//g'
|
||||
)
|
||||
|
||||
abandon_message="
|
||||
unmaintained/$series branch of openstack/$repo transitioned to End of Life
|
||||
and is about to be deleted.
|
||||
To be able to do that, all open patches need to be abandoned."
|
||||
|
||||
for change in $open_changes; do
|
||||
abandon_change $change "$abandon_message"
|
||||
done
|
||||
done
|
||||
|
114
tools/delete_unmaintained_branch.py
Executable file
114
tools/delete_unmaintained_branch.py
Executable file
@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2021 Ericsson Software Technology
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
# This script helps deleting unmaintained branches. Requires 'Delete reference'
|
||||
# access category for 'refs/for/unmaintained/*' for the user who executes it.
|
||||
|
||||
import argparse
|
||||
import getpass
|
||||
import json
|
||||
import sys
|
||||
|
||||
from oslo_utils import timeutils
|
||||
import requests
|
||||
|
||||
|
||||
GERRIT_URL = 'https://review.opendev.org/'
|
||||
GITEA_URL = 'https://opendev.org/api/v1'
|
||||
|
||||
|
||||
def delete_branch(username, project_name, branch_id):
|
||||
print(f'!!! WARNING: You are about to delete unmaintained/{branch_id} '
|
||||
f'from {project_name} !!!')
|
||||
gerrit_auth = requests.auth.HTTPBasicAuth(
|
||||
username,
|
||||
getpass.getpass('Gerrit password: '))
|
||||
url = (f'{GERRIT_URL}a/projects/{project_name.replace("/", "%2F")}/'
|
||||
f'branches/unmaintained%2F{branch_id}')
|
||||
response = requests.delete(url, auth=gerrit_auth)
|
||||
if response.status_code == 204:
|
||||
print(f'{timeutils.utcnow()} | Branch unmaintained/{branch_id} successfully deleted '
|
||||
f'from {project_name}!')
|
||||
return 0
|
||||
elif response.status_code == 401:
|
||||
print(f'{timeutils.utcnow()} | 401 Unauthorized.')
|
||||
return 1
|
||||
else:
|
||||
# NOTE(elod.illes): other possible errors from gerrit:
|
||||
# 404: In case of project or branch is not found
|
||||
# 409: Branch has open changes
|
||||
print(f'{timeutils.utcnow()} | Delete failed ({response.status_code}): {response.text}')
|
||||
return 2
|
||||
|
||||
|
||||
def is_branch_open(project_name, branch_id, quiet):
|
||||
url = (f'{GITEA_URL}/repos/{project_name.replace("/", "%2F")}/'
|
||||
f'branches/unmaintained%2F{branch_id}')
|
||||
response = requests.get(url)
|
||||
try:
|
||||
response_details = response.json()
|
||||
except json.decoder.JSONDecodeError as exc:
|
||||
print(f'ERROR: JSON decode failed ({exc})')
|
||||
print(f'ERROR: ({response.status_code}): {response.text}')
|
||||
print('Is the project name correct, like "openstack/nova"?')
|
||||
return 4
|
||||
if response.status_code == 200:
|
||||
if not quiet:
|
||||
print(f'unmaintained/{branch_id} exists in {project_name}.')
|
||||
return 0
|
||||
elif ((response.status_code == 404) and
|
||||
(response_details['errors'] == [f'branch does not exist [name: unmaintained/{branch_id}]']) and
|
||||
(response_details['message'] == "The target couldn't be found.")):
|
||||
if not quiet:
|
||||
print(f'unmaintained/{branch_id} does not exist in {project_name}.')
|
||||
return 1
|
||||
else:
|
||||
print(f'ERROR: ({response.status_code}): {response.text}')
|
||||
return 2
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Deletes unmaintained/<branch> from <project>.')
|
||||
subparsers = parser.add_subparsers(required=True, dest='command',
|
||||
metavar='{delete,check}')
|
||||
|
||||
delete_parser = subparsers.add_parser(
|
||||
'delete', help='Delete unmaintained/<branch> from <project>')
|
||||
delete_parser.add_argument('username', help='Gerrit Username')
|
||||
delete_parser.add_argument('project', help='Project to delete from')
|
||||
delete_parser.add_argument('branch', help='Branch to delete')
|
||||
|
||||
check_parser = subparsers.add_parser(
|
||||
'check', help='Check if unmaintained/<branch> exists for <project>')
|
||||
check_parser.add_argument('project', help='Project to check')
|
||||
check_parser.add_argument('branch', help='Branch to check if exists')
|
||||
check_parser.add_argument(
|
||||
'-q', '--quiet', action='store_true',
|
||||
help='Return code only (0 means branch exists)')
|
||||
|
||||
args = parser.parse_args()
|
||||
if args.command == 'delete':
|
||||
return delete_branch(args.username,
|
||||
args.project,
|
||||
args.branch.replace('unmaintained/', ''))
|
||||
elif args.command == 'check':
|
||||
return is_branch_open(args.project,
|
||||
args.branch.replace('unmaintained/', ''),
|
||||
args.quiet)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
@ -61,9 +61,7 @@ export PAGER=
|
||||
|
||||
setup_temp_space 'list-eol-stale-branches'
|
||||
|
||||
branch=$(series_to_branch "$series")
|
||||
|
||||
function no_open_patches {
|
||||
function no_stable_open_patches {
|
||||
req="${GERRIT_URL}/changes/?q=status:open+project:${repo}+branch:stable/${eom_branch}"
|
||||
patches=$(curl -s ${req} | sed 1d | jq --raw-output '.[] | .change_id')
|
||||
[ -z "${patches}" ]
|
||||
@ -75,7 +73,19 @@ function no_open_patches {
|
||||
return $no_opens
|
||||
}
|
||||
|
||||
function eol_tag_matches_head {
|
||||
function no_unmaintained_open_patches {
|
||||
req="${GERRIT_URL}/changes/?q=status:open+project:${repo}+branch:unmaintained/${eom_branch}"
|
||||
patches=$(curl -s ${req} | sed 1d | jq --raw-output '.[] | .change_id')
|
||||
[ -z "${patches}" ]
|
||||
no_opens=$?
|
||||
if [[ "$no_opens" -eq 1 ]]; then
|
||||
echo "Patches remained open on stale branch (make sure to abandon them):"
|
||||
echo "https://review.opendev.org/q/status:open+project:${repo}+branch:unmaintained/${eom_branch}"
|
||||
fi
|
||||
return $no_opens
|
||||
}
|
||||
|
||||
function eol_tag_matches_stable_head {
|
||||
head=$(git log --oneline --decorate -1)
|
||||
[[ "$head" =~ "${eom_branch}-eol" ]] && [[ "$head" =~ "origin/stable/${eom_branch}" ]]
|
||||
matches=$?
|
||||
@ -100,14 +110,39 @@ function eol_tag_matches_head {
|
||||
return $matches
|
||||
}
|
||||
|
||||
function is_eol {
|
||||
function eol_tag_matches_unmaintained_head {
|
||||
head=$(git log --oneline --decorate -1)
|
||||
[[ "$head" =~ "${eom_branch}-eol" ]] && [[ "$head" =~ "origin/unmaintained/${eom_branch}" ]]
|
||||
matches=$?
|
||||
if [[ "$matches" -eq 1 ]] ; then
|
||||
tags=$(git tag)
|
||||
[[ "$tags" =~ "${eom_branch}-eol" ]]
|
||||
eol_tag_exists=$?
|
||||
if [[ "$eol_tag_exists" -eq 0 ]]; then
|
||||
echo "WARNING !!! unmaintained/${eom_branch} has patches on top of the ${eom_branch}-eol tag."
|
||||
echo "Please check the branch and ${eom_branch}-eol tag manually."
|
||||
echo "Do not delete the branch if you are not sure!"
|
||||
read -p "> If you are sure the branch can be deleted, then press D + Enter: " DELETE
|
||||
if [ "${DELETE,,}" == "d" ]; then
|
||||
matches=0
|
||||
else
|
||||
echo "Skipping."
|
||||
fi
|
||||
else
|
||||
echo "No ${eom_branch}-eol tag found! Branch cannot be deleted. Skipping."
|
||||
fi
|
||||
fi
|
||||
return $matches
|
||||
}
|
||||
|
||||
function is_stable_eol {
|
||||
${TOOLSDIR}/delete_stable_branch.py check --quiet ${repo} ${eom_branch}
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo
|
||||
echo "${repo} contains eol stale branch (${eom_branch})"
|
||||
clone_repo ${repo} stable/${eom_branch}
|
||||
cd ${repo}
|
||||
if no_open_patches && eol_tag_matches_head; then
|
||||
if no_stable_open_patches && eol_tag_matches_stable_head; then
|
||||
read -p "> Do you want to delete the branch stable/${eom_branch} from ${repo} repository? [y/N]: " YN
|
||||
if [ "${YN,,}" == "y" ]; then
|
||||
if [ -z "$gerrit_username" ]; then
|
||||
@ -120,14 +155,36 @@ function is_eol {
|
||||
fi
|
||||
}
|
||||
|
||||
for eom_branch in "${eom_series[@]}"; do
|
||||
repos=$(list-deliverables -r --series "${eom_branch}" --is-eol)
|
||||
function is_unmaintained_eol {
|
||||
${TOOLSDIR}/delete_unmaintained_branch.py check --quiet ${repo} ${eom_branch}
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo
|
||||
echo "${repo} contains eol stale branch (${eom_branch})"
|
||||
clone_repo ${repo} unmaintained/${eom_branch}
|
||||
cd ${repo}
|
||||
if no_unmaintained_open_patches && eol_tag_matches_unmaintained_head; then
|
||||
read -p "> Do you want to delete the branch unmaintained/${eom_branch} from ${repo} repository? [y/N]: " YN
|
||||
if [ "${YN,,}" == "y" ]; then
|
||||
if [ -z "$gerrit_username" ]; then
|
||||
read -p "Gerrit username: " gerrit_username
|
||||
fi
|
||||
${TOOLSDIR}/delete_unmaintained_branch.py delete ${gerrit_username} ${repo} ${eom_branch}
|
||||
fi
|
||||
fi
|
||||
cd ..
|
||||
fi
|
||||
}
|
||||
|
||||
for eom_series in "${eom_series[@]}"; do
|
||||
eom_branch=$(python -c "from openstack_releases import gitutils; print(gitutils.get_stable_branch_id(\"$eom_series\"))")
|
||||
repos=$(list-deliverables -r --series "$eom_series" --is-eol)
|
||||
|
||||
# Show the eol stale branches for each repository.
|
||||
for repo in ${repos}; do
|
||||
cd ${MYTMPDIR}
|
||||
echo
|
||||
echo " --- $repo ($eom_branch) --- "
|
||||
is_eol "${repo}" "${eom_branch}"
|
||||
is_stable_eol "${repo}" "${eom_branch}"
|
||||
is_unmaintained_eol "${repo}" "${eom_branch}"
|
||||
done
|
||||
done
|
||||
|
Loading…
x
Reference in New Issue
Block a user