Skip to content

Commit

Permalink
tools: update our release scripts (#7493)
Browse files Browse the repository at this point in the history
Functionally this

removes the script which filed bugs to remove old config (per plan of record in #6271)
adds a script which files bugs to remove old code (cleanup of runtime guarded features)
updates our release process
In practice, this just hot swaps what our deprecation script does and updates the docs.

Risk Level: n/a (tooling + comment)
Testing: manual testing. Will do one more manual test with actual bug filing just before submitting.
Docs Changes: updated
Release Notes: n/a
Fixes #6472

Signed-off-by: Alyssa Wilk <alyssar@chromium.org>
  • Loading branch information
alyssawilk authored Jul 11, 2019
1 parent 2f8b6a2 commit e75ff19
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 45 deletions.
2 changes: 1 addition & 1 deletion GOVERNANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ or you can subscribe to the iCal feed [here](https://app.opsgenie.com/webcal/get
the same time, also add a new empty "pending" section to the [release
notes](docs/root/intro/version_history.rst) and to [deprecated log](docs/root/intro/deprecated.rst) for the
following version. E.g., "1.7.0 (pending)".
* Run the deprecate_versions.py script (e.g. `sh tools/deprecate_version/deprecate_version.sh 1.8.0 1.10.0`)
* Run the deprecate_versions.py script (e.g. `sh tools/deprecate_version/deprecate_version.sh`)
to file tracking issues for code which can be removed.
* Run the deprecate_features.py script (e.g. `sh tools/deprecate_version/deprecate_features.sh`)
to make the last release's deprecated features fatal-by-default. Submit the resultant PR and send
Expand Down
5 changes: 0 additions & 5 deletions source/common/runtime/runtime_features.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,6 @@ constexpr const char* runtime_features[] = {
//
// The release cycle after a feature has been marked disallowed, it is officially removable, and
// the maintainer team will run a script creating a tracking issue for proto and code clean up.
//
// TODO(alyssawilk) handle deprecation of reloadable_features and update the above comment. Ideally
// runtime override of a deprecated feature will log(warn) on runtime-load if not deprecated
// and hard-fail once it has been deprecated.

constexpr const char* disallowed_features[] = {
// Acts as both a test entry for deprecated.proto and a marker for the Envoy
// deprecation scripts.
Expand Down
114 changes: 75 additions & 39 deletions tools/deprecate_version/deprecate_version.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
# Script for automating cleanup PR creation for deprecated features at a given
# version. This script is generally run via
# Script for automating cleanup PR creation for deprecated runtime features
#
# sh tools/deprecate_version/deprecate_version.sh
#
# Direct usage (not recommended):
#
# python tools/deprecate_version/deprecate_version.py <2 releases ago> <current release>
# python tools/deprecate_version/deprecate_version.py
#
# e.g
#
# python tools/deprecate_version/deprecate_version.py 1.5.0 1.7.0
# python tools/deprecate_version/deprecate_version.py
#
# A GitHub access token must be set in GH_ACCESS_TOKEN. To create one, go to
# Settings -> Developer settings -> Personal access tokens in GitHub and create
Expand All @@ -20,14 +19,13 @@
# Known issues:
# - Minor fixup PRs (e.g. fixing a typo) will result in the creation of spurious
# issues.
# - Later PRs can clobber earlier changed to DEPRECATED.md, meaning we miss
# issues.

from __future__ import print_function

from collections import defaultdict
import os
import re
import subprocess
import sys

import github
Expand Down Expand Up @@ -72,77 +70,115 @@ def GetConfirmation():
return input('Creates issues? [yN] ').strip().lower() in ('y', 'yes')


def CreateIssues(deprecate_for_version, deprecate_by_version, access_token, commits):
"""Create issues in GitHub corresponding to a set of commits.
def CreateIssues(access_token, runtime_and_pr):
"""Create issues in GitHub for code to clean up old runtime guarded features.
Args:
deprecate_for_version: string providing version to deprecate for, e.g.
1.6.0.
deprecate_by_version: string providing version to deprecate by, e.g. 1.7.0.
access_token: GitHub access token (see comment at top of file).
commits: set of git commit objects.
runtime_and_pr: a list of runtime guards and the PRs they were added.
"""
repo = github.Github(access_token).get_repo('envoyproxy/envoy')
# Find GitHub milestone object for deprecation target.
milestone = None
for m in repo.get_milestones():
if m.title == deprecate_by_version:
milestone = m
break
if not milestone:
raise DeprecateVersionError('Unknown milestone %s' % deprecate_by_version)

# Find GitHub label objects for LABELS.
labels = []
for label in repo.get_labels():
if label.name in LABELS:
labels.append(label)
if len(labels) != len(LABELS):
raise DeprecateVersionError('Unknown labels (expected %s, got %s)' % (LABELS, labels))
# What are the PRs corresponding to the commits?
prs = (int(re.search('\(#(\d+)\)', c.message).group(1)) for c in commits)

issues = []
for pr in sorted(prs):
for runtime_guard, pr in runtime_and_pr:
# Who is the author?
pr_info = repo.get_pull(pr)
title = '[v%s deprecation] Remove features marked deprecated in #%d' % (deprecate_for_version,
pr)
body = ('#%d (%s) introduced a deprecation notice for v%s. This issue '
'tracks source code cleanup.') % (pr, pr_info.title, deprecate_for_version)

title = '%s deprecation' % (runtime_guard)
body = ('#%d (%s) introduced a runtime guarded feature. This issue '
'tracks source code cleanup.') % (pr, pr_info.title)
print(title)
print(body)
print(' >> Assigning to %s' % pr_info.user.login)

# TODO(htuch): Figure out how to do this without legacy and faster.
exists = repo.legacy_search_issues('open', '"%s"' % title) or repo.legacy_search_issues(
'closed', '"%s"' % title)
if exists:
print(' >> Issue already exists, not posting!')
else:
issues.append((title, body, pr_info.user))

if not issues:
print('No features to deprecate in this release')
return

if GetConfirmation():
print('Creating issues...')
for title, body, user in issues:
try:
repo.create_issue(
title, body=body, assignees=[user.login], milestone=milestone, labels=labels)
repo.create_issue(title, body=body, assignees=[user.login], labels=labels)
except github.GithubException as e:
print(('GithubException while creating issue. This is typically because'
' a user is not a member of envoyproxy org. Check that %s is in '
'the org.') % user.login)
raise


def GetRuntimeAlreadyTrue():
"""Returns a list of runtime flags already defaulted to true
"""
runtime_already_true = []
runtime_features = re.compile(r'.*"(envoy.reloadable_features..*)",.*')
with open('source/common/runtime/runtime_features.cc', 'r') as features:
for line in features.readlines():
match = runtime_features.match(line)
if match and 'test_feature_true' not in match.group(1):
print("Found existing flag " + match.group(1))
runtime_already_true.append(match.group(1))

return runtime_already_true


def GetRuntimeAndPr():
"""Returns a list of tuples of [runtime features to deprecate, PR the feature was added]
"""
repo = Repo(os.getcwd())

runtime_already_true = GetRuntimeAlreadyTrue()

# grep source code looking for reloadable features which are true to find the
# PR they were added.
grep_output = subprocess.check_output('grep -r "envoy.reloadable_features\." source/', shell=True)
features_to_flip = []
runtime_feature_regex = re.compile(r'.*(source.*cc).*"(envoy.reloadable_features\.[^"]+)".*')
for line in grep_output.splitlines():
match = runtime_feature_regex.match(str(line))
if match:
filename = (match.group(1))
runtime_guard = match.group(2)
# If this runtime guard isn't true, ignore it for this release.
if not runtime_guard in runtime_already_true:
continue
# For true runtime guards, walk the blame of the file they were added to,
# to find the pr the feature was added.
for commit, lines in repo.blame('HEAD', filename):
for line in lines:
if runtime_guard in line:
pr = (int(re.search('\(#(\d+)\)', commit.message).group(1)))
# Add the runtime guard and PR to the list to file issues about.
features_to_flip.append((runtime_guard, pr))

else:
print('no match in ' + str(line) + ' please address manually!')

return features_to_flip


if __name__ == '__main__':
if len(sys.argv) != 3:
print('Usage: %s <deprecate for version> <deprecate by version>' % sys.argv[0])
sys.exit(1)
runtime_and_pr = GetRuntimeAndPr()

access_token = os.getenv('GH_ACCESS_TOKEN')
if not access_token:
print('Missing GH_ACCESS_TOKEN')
sys.exit(1)
deprecate_for_version = sys.argv[1]
deprecate_by_version = sys.argv[2]
history = GetHistory()
if deprecate_for_version not in history:
print('Unknown version: %s (valid versions: %s)' % (deprecate_for_version, history.keys()))
CreateIssues(deprecate_for_version, deprecate_by_version, access_token,
history[deprecate_for_version])

CreateIssues(access_token, runtime_and_pr)

0 comments on commit e75ff19

Please sign in to comment.