Add a tool to update the releases repo
Change-Id: I216b77823281abe390e35395ac8d9ccc7f3064b9
This commit is contained in:
parent
cb661a42f8
commit
056184c363
176
openstack_election/cmds/update_releases_calendar.py
Normal file
176
openstack_election/cmds/update_releases_calendar.py
Normal file
@ -0,0 +1,176 @@
|
||||
# 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.
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
import os
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
from openstack_election import config
|
||||
from openstack_election import yamlutils
|
||||
|
||||
conf = config.load_conf()
|
||||
rst_template = textwrap.dedent("""
|
||||
{release} TC and PTL Elections
|
||||
---------------------------
|
||||
|
||||
.. _{nrl}-election-email-deadline:
|
||||
|
||||
{release} Election Email Deadline
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Contributors that will be in the electorate for the upcoming election
|
||||
should confirm their gerrit email addresses by this date ({deadline_date}
|
||||
at {deadline_time} UTC). Electorate rolls are generated after this date and ballots will
|
||||
be sent to the listed gerrit email address.
|
||||
|
||||
.. _{nrl}-election-nominations:
|
||||
|
||||
{release} Election Nomination Begins
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Candidates interested in serving for the next calendar year (TC), or
|
||||
development cycle (PTL) should announce their candidacies and platforms during
|
||||
this week. Please see the `Election site`_ for specific timing information.
|
||||
|
||||
.. _{nrl}-election-campaigning:
|
||||
|
||||
{release} Election Campaigning Begins
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
The electorate has time to ask candidates questions about their platforms
|
||||
and debate topics before polling begins. Please see the `Election site`_ for
|
||||
specific timing information.
|
||||
|
||||
.. _{nrl}-election-voting:
|
||||
|
||||
{release} Election Polling Begins
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Election polling for open seats on the TC and any required PTL elections.
|
||||
Please see the `Election site`_ for specific timing information.
|
||||
|
||||
.. _{nrl}-election-close:
|
||||
|
||||
{release} Election Polling Ends
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
All polls close in the {release} Election and results announced. Please see the
|
||||
`Election site`_ for specific timing information.
|
||||
|
||||
.. _Election site: https://governance.openstack.org/election/
|
||||
""") # noqa
|
||||
|
||||
|
||||
def next_release_letter(release_name):
|
||||
ord_a = ord('a')
|
||||
next_ord = (ord(release_name[0]) - ord_a + 1) % 26
|
||||
next_letter = chr(ord_a + next_ord)
|
||||
return next_letter
|
||||
|
||||
|
||||
def custom_strftime(format, t):
|
||||
def _suffix(d):
|
||||
return {1: 'st', 2: 'nd', 3: 'rd'}.get(d % 20, 'th')
|
||||
return t.strftime(format).replace('{S}', str(t.day) + _suffix(t.day))
|
||||
|
||||
|
||||
def add_election_event_to_schedule(nrl, election_events,
|
||||
schedule_events, schedule_data):
|
||||
election_event_idx = 0
|
||||
for cycle_event in schedule_data["cycle"]:
|
||||
(cycle_event_start, cycle_event_end) = (
|
||||
datetime.date.fromisoformat(cycle_event["start"]),
|
||||
datetime.date.fromisoformat(cycle_event["end"]))
|
||||
event_type = election_events[election_event_idx]
|
||||
event_label = f"{nrl}-election-{event_type}"
|
||||
(event_start, event_end) = schedule_events[event_label]
|
||||
|
||||
if cycle_event_start < event_start.date():
|
||||
continue
|
||||
|
||||
if "x-project" not in cycle_event:
|
||||
cycle_event["x-project"] = []
|
||||
if event_label not in cycle_event["x-project"]:
|
||||
cycle_event["x-project"].append(event_label)
|
||||
|
||||
if event_end.date() < cycle_event_end:
|
||||
election_event_idx += 1
|
||||
if election_event_idx >= len(election_events):
|
||||
break
|
||||
|
||||
|
||||
def main():
|
||||
if conf["election_type"] != "combined":
|
||||
print("This tool currently only supports 'combined' elections",
|
||||
file=sys.stderr)
|
||||
return 1
|
||||
|
||||
description = ('Update development cycle in openstack/releases with '
|
||||
' key election events')
|
||||
parser = argparse.ArgumentParser(description)
|
||||
parser.add_argument('--releases-repo', dest='releases_repo',
|
||||
required=True,
|
||||
help=('Path to a clone of the releases repo'))
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
series_path = os.path.join(args.releases_repo, "data",
|
||||
"series_status.yaml")
|
||||
with open(series_path) as f:
|
||||
series_data = yamlutils.loads(f)
|
||||
|
||||
release = conf.get("release")
|
||||
release_name = series_data[0]["name"]
|
||||
nrl = next_release_letter(release_name)
|
||||
|
||||
schedule_path = os.path.join(args.releases_repo, "doc", "source",
|
||||
release_name, "schedule.yaml")
|
||||
with open(schedule_path) as f:
|
||||
schedule_data = yamlutils.loads(f)
|
||||
|
||||
rst_path = os.path.join(args.releases_repo, "doc", "source", release_name,
|
||||
"schedule.rst")
|
||||
with open(rst_path) as f:
|
||||
rst = f.read()
|
||||
|
||||
email_deadline = conf["timeframe"]["email_deadline"]
|
||||
deadline_date = custom_strftime('%B {S}, %Y', email_deadline)
|
||||
deadline_time = email_deadline.strftime("%H:%M")
|
||||
rst_blob = rst_template.format(**dict(release=release,
|
||||
nrl=nrl,
|
||||
deadline_time=deadline_time,
|
||||
deadline_date=deadline_date,))
|
||||
election_canary = rst_blob.split("\n")[1]
|
||||
if election_canary not in rst:
|
||||
print(f"Updating {rst_path} to add {rst_blob}\n")
|
||||
with open(rst_path, "w") as f:
|
||||
f.write(rst)
|
||||
f.write(rst_blob)
|
||||
|
||||
schedule_events = {f"{nrl}-election-close":
|
||||
(conf["timeframe"]["end"], conf["timeframe"]["end"]),
|
||||
f"{nrl}-election-email-deadline":
|
||||
(email_deadline, email_deadline), }
|
||||
for event in conf["timeline"]:
|
||||
event_type = event["name"].split()[-1].lower()
|
||||
if event_type == "election":
|
||||
event_type = "voting"
|
||||
event_label = f"{nrl}-election-{event_type}"
|
||||
schedule_events[event_label] = (event["start"], event["end"])
|
||||
|
||||
election_events = ["email-deadline"]
|
||||
add_election_event_to_schedule(nrl, election_events, schedule_events,
|
||||
schedule_data)
|
||||
election_events = ["nominations", "campaigning", "voting", "close"]
|
||||
add_election_event_to_schedule(nrl, election_events, schedule_events,
|
||||
schedule_data)
|
||||
|
||||
with open(schedule_path, "w") as f:
|
||||
print(f"Updating {schedule_path}")
|
||||
f.write(yamlutils.dumps(schedule_data))
|
34
openstack_election/yamlutils.py
Normal file
34
openstack_election/yamlutils.py
Normal file
@ -0,0 +1,34 @@
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import ruamel.yaml
|
||||
import ruamel.yaml.compat
|
||||
|
||||
|
||||
def dumps(obj):
|
||||
"""Dumps yaml content into a string."""
|
||||
yaml = ruamel.yaml.YAML()
|
||||
yaml.width = 80
|
||||
|
||||
stream = ruamel.yaml.compat.StringIO()
|
||||
yaml.explicit_start = True
|
||||
yaml.indent(mapping=2, sequence=4, offset=2)
|
||||
yaml.dump(obj, stream)
|
||||
return stream.getvalue()
|
||||
|
||||
|
||||
def loads(blob):
|
||||
"""Load a yaml blob and retain key ordering."""
|
||||
yaml = ruamel.yaml.YAML()
|
||||
return yaml.load(blob)
|
@ -8,3 +8,4 @@ ndg-httpsclient>=0.4.2;python_version<'3.0' # BSD
|
||||
PrettyTable<0.8,>=0.7.1 # BSD
|
||||
docutils>=0.11 # OSI-Approved Open Source, Public Domain
|
||||
Jinja2>=2.10 # BSD License (3 clause)
|
||||
ruamel.yaml>=0.15
|
||||
|
@ -37,3 +37,4 @@ console_scripts =
|
||||
owners = openstack_election.cmds.change_owners:main
|
||||
template-emails = openstack_election.cmds.template_emails:main
|
||||
setup-election-config = openstack_election.cmds.setup_election_config:main
|
||||
update-releases-calendar = openstack_election.cmds.update_releases_calendar:main
|
||||
|
Loading…
Reference in New Issue
Block a user