diff --git a/requirements/base.in b/requirements/base.in index 0cbd878d..43f8409a 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -8,7 +8,6 @@ cloudflare edx-opaque-keys==0.4.0 edx-rest-api-client==5.5.0 freezegun==0.3.8 -future==0.16.0 GitPython==3.1.18 google-api-python-client==1.7.3 jenkinsapi==0.3.3 diff --git a/requirements/base.txt b/requirements/base.txt index 1514c6db..414565a4 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -42,9 +42,9 @@ click==8.1.7 # edx-django-utils click-log==0.4.0 # via -r requirements/base.in -cloudflare==2.11.7 +cloudflare==2.12.4 # via -r requirements/base.in -cryptography==41.0.3 +cryptography==41.0.4 # via # pyjwt # simple-salesforce @@ -64,14 +64,12 @@ easydict==1.10 # via yagocd edx-django-utils==5.7.0 # via edx-rest-api-client -edx-opaque-keys==0.4.0 +edx-opaque-keys==0.4 # via -r requirements/base.in edx-rest-api-client==5.5.0 # via -r requirements/base.in freezegun==0.3.8 # via -r requirements/base.in -future==0.16.0 - # via -r requirements/base.in gitdb==4.0.10 # via gitpython gitpython==3.1.18 @@ -219,7 +217,7 @@ stevedore==1.32.0 # via # edx-django-utils # edx-opaque-keys -typing-extensions==4.7.1 +typing-extensions==4.8.0 # via asgiref unicodecsv==0.14.1 # via -r requirements/base.in diff --git a/requirements/pip-tools.txt b/requirements/pip-tools.txt index d2e8e4e5..894fa179 100644 --- a/requirements/pip-tools.txt +++ b/requirements/pip-tools.txt @@ -23,7 +23,7 @@ tomli==2.0.1 # pyproject-hooks wheel==0.41.2 # via pip-tools -zipp==3.16.2 +zipp==3.17.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: diff --git a/requirements/testing.txt b/requirements/testing.txt index 21fa0200..a6df3613 100644 --- a/requirements/testing.txt +++ b/requirements/testing.txt @@ -36,7 +36,7 @@ code-annotations==1.5.0 # via edx-lint coverage[toml]==7.3.1 # via pytest-cov -cryptography==41.0.3 +cryptography==41.0.4 # via moto ddt==1.6.0 # via -r requirements/testing.in @@ -163,7 +163,7 @@ tomli==2.0.1 # pytest tomlkit==0.12.1 # via pylint -typing-extensions==4.7.1 +typing-extensions==4.8.0 # via # astroid # pylint diff --git a/tubular/scripts/update_release_page.py b/tubular/scripts/update_release_page.py new file mode 100755 index 00000000..2175632b --- /dev/null +++ b/tubular/scripts/update_release_page.py @@ -0,0 +1,195 @@ +#! /usr/bin/env python3 + +""" +Command-line script to create or update the release page for a specific release. +""" +from os import path +from datetime import datetime +import sys +import logging + +import click +import click_log +import yaml + + +# Add top-level module path to sys.path before importing tubular code. +sys.path.append(path.dirname(path.dirname(path.abspath(__file__)))) + +from tubular.confluence_api import ReleasePage, publish_page, AMI, ReleaseStatus # pylint: disable=wrong-import-position +from tubular.github_api import ( # pylint: disable=wrong-import-position + default_expected_release_date, +) + +logging.basicConfig(stream=sys.stdout, level=logging.INFO) +LOG = logging.getLogger(__name__) + +EXPECTED_RELEASE_DATE = default_expected_release_date() + + +@click.command("create_release_page") +@click.option( + '-c', '--compare', 'ami_pairs', + help=u"A pair of paths to AMI description yaml files.", + type=(click.File(), click.File()), + multiple=True +) +@click.option( + '--confluence-url', + help=u"The base url of the confluence instance to publish the release page to.", + default='https://openedx.atlassian.net/wiki', +) +@click.option( + '--user', + help=u"The username of the confluence user to post as.", + required=True, +) +@click.option( + '--password', + help=u"The password for the confluence user.", + required=True, +) +@click.option( + '--parent-title', + help=u"The title of the page to publish this page as a child of.", +) +@click.option( + '--space', + help=u"The space to publish this page in.", +) +@click.option( + '--title', + help=( + u"The title of this page. May contain the formatting value {timestamp} " + u"to insert the time of publishing into the wiki title. May also include `/` " + u"characters, which will create hierarchy pages that just display children." + ), +) +@click.option( + '--github-token', + help=u"The token to use when accessing the github api.", + required=True, +) +@click.option( + '--jira-url', + help=u"The base url for the JIRA instance to link JIRA tickets to.", + default='https://openedx.atlassian.net' +) +@click.option( + '--gocd-url', + help=u"The url of the GoCD pipeline that build this release.", +) +@click.option( + '--status', + help=u"The current status of this deployment", + required=True, + type=click.Choice(ReleaseStatus.__members__.keys()), # pylint: disable=no-member +) +@click.option( + '--out-file', + help=u"File location to export metadata that can be used to update this page.", + type=click.File(mode='w'), + default=sys.stdout, +) +@click.option( + '--in-file', + help=u"File location to import metadata from to specify a page to update.", + type=click.File(), +) +@click_log.simple_verbosity_option(default=u'INFO') +def create_release_page( + ami_pairs, confluence_url, user, password, parent_title, + space, title, github_token, jira_url, gocd_url, status, + out_file, in_file +): + """ + Create a new release page for edxapp. + """ + if in_file and any([parent_title, space, title]): + raise click.BadOptionUsage( + "in_file", + "Either --in-file or --parent-title/--space/--title must be specified, but not both." + ) + + if in_file: + in_data = yaml.safe_load(in_file) + parent_title = in_data['parent_title'] + space = in_data['space'] + title = in_data['title'] + + if title is None: + title = "{:%Y-%m-%d} Release".format(EXPECTED_RELEASE_DATE) + + # This allows titles of the form "{timestamp:%Y-%m-%d} Release" and the like + # to format in the current timestamp + title = title.format(timestamp=datetime.now()) + + folders = title.split('/')[:-1] + title = title.split('/')[-1] + + if space is None: + space = 'RELEASES' + + if parent_title is None: + parent_title = 'LMS/Studio Release Pages' + + ami_pairs = [ + ( + AMI(**yaml.safe_load(old)), + AMI(**yaml.safe_load(new)) + ) + for old, new in ami_pairs + ] + + page = ReleasePage( + github_token, + jira_url, + getattr(ReleaseStatus, status), + ami_pairs, + gocd_url=gocd_url, + ) + + parent_id = None + parent_title_root = parent_title + for folder in folders: + result = publish_page( + confluence_url, + user, + password, + space, + folder, + """ + + """, + parent_title=parent_title, + parent_id=parent_id + ) + parent_title = None + parent_id = result[u'id'] + + publish_page( + confluence_url, + user, + password, + space, + title, + page.format(), + parent_title=parent_title, + parent_id=parent_id + ) + + yaml.safe_dump({ + 'parent_title': parent_title_root, + 'space': space, + 'title': title + }, stream=out_file) + + +if __name__ == "__main__": + # pylint: disable=no-value-for-parameter + create_release_page()