-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refined release documentation (#14221)
- Loading branch information
1 parent
cb5a8aa
commit 07e30a3
Showing
2 changed files
with
290 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,27 @@ | ||
# Releasing AWX (and awx-operator) | ||
|
||
The release process for AWX is completely automated as of version 19.5.0. | ||
The release process for AWX is mostly automated as of version 19.5.0. | ||
|
||
If you need to revert a release, please refer to the [Revert a Release](#revert-a-release) section. | ||
## Get latest release version and list of new work | ||
|
||
## Select the next release version | ||
|
||
There are two methods you can use to get the next release version. The manual way and the automated way. | ||
|
||
### Automated Way | ||
|
||
#### Get a github token | ||
|
||
Log into your github account, under your user icon go to Settings => Developer Settings => Personal Access Tokens => Tokens (classic). | ||
Select the Generate new token => Generate new token (classic) | ||
Fill in the note, select no scopes select "Generate token". | ||
Copy the token and create a file in your awx repo called `.github_creds`. Enter the token in this file. | ||
Run `./tools/scripts/get_next_release.py` | ||
This will use your token to go query for the PRs in the release and scan their bodies to select X/Y/Z and suggest new versions and spit out notifications. | ||
|
||
### Manual Way | ||
|
||
#### Get latest release version and list of new work | ||
|
||
1. Open the main project page for [AWX](https://github.com/ansible/awx/releases) and [AWX Operator](https://github.com/ansible/awx-operator/releases). | ||
|
||
|
@@ -20,10 +38,10 @@ The page will now automatically update with a list of PRs that are in `AWX/devel | |
|
||
![PR Compare List](img/pr_compare_list.png) | ||
|
||
## Select the next release version | ||
|
||
Use this list of PRs to decide if this is a X-stream (major) release, Y-stream (minor) release, or a Z-stream (patch) release. Use [semver](https://semver.org/#summary) to help determine what kind of release is needed. | ||
|
||
#### Select the next release version | ||
|
||
Indicators of a Z-stream release: | ||
|
||
- No significant new features have been merged into devel since the last release. | ||
|
@@ -126,16 +144,19 @@ This workflow will take the generated images and promote them to quay.io. | |
|
||
![Verify released awx-operator image](img/verify-released-awx-operator-image.png) | ||
|
||
## Notify the AWX mailing list | ||
Send an email to the [AWX Mailing List](mailto:[email protected]) with a message format of type "AWX Release" from the [mailing list triage standard replies](../.github/triage_replies.md#awx-release) | ||
## Send notifications | ||
Send notifications to the following groups: | ||
* AWX Mailing List | ||
* #social:ansible.com IRC (@newsbot for inclusion in bullhorn) | ||
* #awx:ansible.com (no @newsbot in this room) | ||
* #ansible-controller slack channel | ||
|
||
## Send an IRC message over matrix to #social:ansible.com for bullhorn: | ||
These messages are templated out for you in the output of `get_next_release.yml`. | ||
|
||
@newsbot | ||
We're happy to announce that [AWX version 21.1.0](https://github.com/ansible/awx/releases/tag/21.1.0) is now available! | ||
We're happy to announce that [AWX Operator version 0.22.0](https://github.com/ansible/awx-operator/releases/tag/0.22.0) is now available! | ||
Note: the slack message is the same as the IRC message. | ||
|
||
## Send the same IRC message (less the @newsbot) to #awx:ansible.com | ||
## Create operator hub PRs. | ||
Operator hub PRs are generated via an Ansible Playbook. See someone on the AWX team for the location of the playbooks and instructions on how to run them. | ||
|
||
## Revert a Release | ||
|
||
|
@@ -165,4 +186,4 @@ Here are the steps needed to revert an AWX and an AWX-Operator release. Dependin | |
|
||
7. Navigate to the [PyPi](https://pypi.org/project/awxkit/#history) and delete the bad AWX tag and release that got published. | ||
|
||
8. [Restart the Release Process](#releasing-awx-and-awx-operator) | ||
8. [Restart the Release Process](#releasing-awx-and-awx-operator) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,257 @@ | ||
#!/usr/bin/env python | ||
|
||
missing_modules = [] | ||
try: | ||
import requests | ||
except: | ||
missing_modules.append('requests') | ||
import json | ||
import os | ||
import re | ||
import sys | ||
import time | ||
|
||
try: | ||
import semantic_version | ||
except: | ||
missing_modules.append('semantic_version') | ||
|
||
if len(missing_modules) > 0: | ||
print("This requires python libraries to work; try:") | ||
for a_module in missing_modules: | ||
print(" pip install {}".format(a_module)) | ||
sys.exit(1) | ||
|
||
|
||
def getCurrentVersions(): | ||
print("Getting current versions") | ||
for repo in product_repos: | ||
response = session.get('https://api.github.com/repos/ansible/{}/releases'.format(repo)) | ||
if 'X-RateLimit-Limit' in response.headers and int(response.headers['X-RateLimit-Limit']) <= 60: | ||
print("Your key in .github_creds did not work right and you are using unauthenticated requests") | ||
print("This script would likely overrun your available requests, exiting") | ||
sys.exit(3) | ||
versions['current'][repo] = response.json()[0]['tag_name'] | ||
print(" {}: {}".format(repo, versions['current'][repo])) | ||
|
||
|
||
def getNextReleases(): | ||
# | ||
# This loads the commits since the last release and gets their associated PRs and scans those for release_type: [xyz] | ||
# Any X or Y changes also get captured for bullhorn release notes | ||
# | ||
for repo in product_repos: | ||
response = session.get('https://api.github.com/repos/ansible/{}/compare/{}...devel'.format(repo, versions['current'][repo])) | ||
commit_data = response.json() | ||
pr_votes = {} | ||
suggested_release_type = None | ||
prs_missing_relese_type = 0 | ||
versions['release_notes'][repo] = [] | ||
for commit in commit_data['commits']: | ||
response = session.get('https://api.github.com/repos/ansible/{}/commits/{}/pulls'.format(repo, commit['sha'])) | ||
prs = response.json() | ||
for a_pr in prs: | ||
# If we've already seen this PR we don't need to check again | ||
try: | ||
if a_pr['html_url'] in pr_votes: | ||
continue | ||
except: | ||
print("Unable to check on PR") | ||
print(json.dumps(a_pr, indent=4)) | ||
sys.exit(255) | ||
append_title = False | ||
pr_release = 'is non voting' | ||
if a_pr and a_pr.get('body', None): | ||
if 'Breaking Change' in a_pr['body']: | ||
suggested_release_type = 'x' | ||
pr_release = 'votes x' | ||
append_title = True | ||
elif 'New or Enhanced Feature' in a_pr['body']: | ||
if suggested_release_type != 'x': | ||
suggested_release_type = 'y' | ||
pr_release = 'votes y' | ||
append_title = True | ||
elif 'Bug, Docs Fix or other nominal change' in a_pr['body']: | ||
if suggested_release_type == None: | ||
suggested_release_type = 'z' | ||
pr_release = 'votes z' | ||
# This was a format along the way | ||
elif 'Bug or Docs Fix' in a_pr['body']: | ||
if suggested_release_type == None: | ||
suggested_release_type = 'z' | ||
pr_release = 'votes z' | ||
# Old PR format | ||
elif ( | ||
'- Bug Report' in a_pr['body'] | ||
or '- Bug Fix' in a_pr['body'] | ||
or '- Bugfix Pull Request' in a_pr['body'] | ||
or '- Documentation' in a_pr['body'] | ||
or '- Docs Pull Request' in a_pr['body'] | ||
): | ||
if suggested_release_type == None: | ||
suggested_release_type = 'z' | ||
pr_release = 'votes z (from old PR body)' | ||
elif '- Feature Idea' in a_pr['body'] or '- Feature Pull Request' in a_pr['body']: | ||
if suggested_release_type != 'x': | ||
suggested_release_type = 'y' | ||
pr_release = 'votes y (from old PR body)' | ||
append_title = True | ||
else: | ||
prs_missing_relese_type += 1 | ||
else: | ||
prs_missing_relese_type += 1 | ||
|
||
if append_title: | ||
versions['release_notes'][repo].append("* {}".format(a_pr['title'])) | ||
print("PR {} {}".format(a_pr['html_url'], pr_release)) | ||
pr_votes[a_pr['html_url']] = pr_release | ||
|
||
print("https://github.com/ansible/{}/compare/{}...devel".format(repo, versions['current'][repo])) | ||
print("{} devel is {} commit(s) ahead of release {}".format(repo, commit_data['total_commits'], versions['current'][repo])) | ||
if prs_missing_relese_type == 0: | ||
print("\nAll commits voted, the release type suggestion is {}".format(suggested_release_type)) | ||
else: | ||
total_prs = len(pr_votes) | ||
voted_prs = total_prs - prs_missing_relese_type | ||
print("From {} commits, {} of {} PRs voted".format(commit_data['total_commits'], voted_prs, total_prs)) | ||
if suggested_release_type: | ||
print("\nOf commits with release type, the suggestion is {}".format(suggested_release_type)) | ||
else: | ||
print("\nNone of the commits had the release type indicated") | ||
print() | ||
|
||
current_version = semantic_version.Version(versions['current'][repo]) | ||
if suggested_release_type.lower() == 'x': | ||
versions['next'][repo] = current_version.next_major() | ||
elif suggested_release_type.lower() == 'y': | ||
versions['next'][repo] = current_version.next_minor() | ||
else: | ||
versions['next'][repo] = current_version.next_patch() | ||
|
||
|
||
# | ||
# Load the users session information | ||
# | ||
session = requests.Session() | ||
try: | ||
print("Loading credentials") | ||
with open(".github_creds", "r") as f: | ||
password = f.read().strip() | ||
session.headers.update({'Authorization': 'bearer {}'.format(password), 'Accept': 'application/vnd.github.v3+json'}) | ||
except Exception: | ||
print("Failed to load credentials from ./.github_creds") | ||
sys.exit(255) | ||
|
||
versions = { | ||
'current': {}, | ||
'next': {}, | ||
'release_notes': {}, | ||
} | ||
|
||
product_repos = ['awx', 'awx-operator'] | ||
|
||
# | ||
# Get latest release version from releases page | ||
# | ||
getCurrentVersions() | ||
|
||
# | ||
# Scan PRs for release types | ||
# | ||
getNextReleases() | ||
|
||
# | ||
# Confirm the release number with the human | ||
# | ||
print( | ||
''' | ||
Next recommended releases: | ||
AWX: {0} | ||
Operator: {1} | ||
'''.format( | ||
versions['next']['awx'], | ||
versions['next']['awx-operator'], | ||
) | ||
) | ||
|
||
for product in product_repos: | ||
version_override = input("Enter the next {} release number ({}): ".format(product, versions['next'][product])) | ||
if version_override != '': | ||
versions['next'][product] = version_override | ||
|
||
# | ||
# Generate IRC and Mailing list messages | ||
# | ||
print("Enter any known issues (one per line, empty line to end)") | ||
known_issues = [] | ||
keep_getting_issues = True | ||
while keep_getting_issues: | ||
issue = input() | ||
if issue == '': | ||
keep_getting_issues = False | ||
else: | ||
known_issues.append(issue) | ||
|
||
display_known_issues = '' | ||
if len(known_issues) > 0: | ||
display_known_issues = "\n".join(['Known Issues:'] + ['* {}'.format(item) for item in known_issues]) | ||
|
||
print( | ||
''' | ||
Bullhorn/irc list message: | ||
@newsbot We're happy to announce that the next release of AWX, version {0} is now available! | ||
Some notable features include: | ||
{2} | ||
In addition AWX Operator version {1} has also been released! | ||
Some notable features include: | ||
{3} | ||
Please see the releases pages for more details: | ||
AWX: [https://github.com/ansible/awx/releases/tag/{0}](https://github.com/ansible/awx/releases/tag/{0}) | ||
Operator: [https://github.com/ansible/awx-operator/releases/tag/{1}](https://github.com/ansible/awx-operator/releases/tag/{1}) | ||
{4} | ||
'''.format( | ||
versions['next']['awx'], | ||
versions['next']['awx-operator'], | ||
'\n'.join(versions['release_notes']['awx']), | ||
'\n'.join(versions['release_notes']['awx-operator']), | ||
display_known_issues, | ||
) | ||
) | ||
|
||
print( | ||
''' | ||
Mailing list message: | ||
Subject: Announcing AWX {0} and AWX-Operator {1} | ||
Body: | ||
Hi all, | ||
We're happy to announce that the next release of AWX, version {0} is now available! | ||
Some notable features include: | ||
{2} | ||
In addition AWX Operator version {1} has also been released! | ||
Some notable features include: | ||
{3} | ||
Please see the releases pages for more details: | ||
AWX: https://github.com/ansible/awx/releases/tag/{0} | ||
Operator: https://github.com/ansible/awx-operator/releases/tag/{1} | ||
{4} | ||
-The AWX team. | ||
'''.format( | ||
versions['next']['awx'], | ||
versions['next']['awx-operator'], | ||
'\n'.join(versions['release_notes']['awx']), | ||
'\n'.join(versions['release_notes']['awx-operator']), | ||
display_known_issues, | ||
) | ||
) |