#!/usr/bin/env python missing_modules = [] try: import requests except ImportError: missing_modules.append('requests') import json import os import sys try: import semantic_version except ImportError: 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 KeyError: 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() print("Loading credentials") CREDS_LOCATIONS = ('.github_creds', '~/.github_creds') for creds_loc in CREDS_LOCATIONS: if os.path.exists(os.path.expanduser(creds_loc)): with open(os.path.expanduser(creds_loc), "r") as f: password = f.read().strip() session.headers.update({'Authorization': 'bearer {}'.format(password), 'Accept': 'application/vnd.github.v3+json'}) break else: raise Exception(f'Could not location github token in locations {CREDS_LOCATIONS}') 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, ) )