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
|
PrettyTable<0.8,>=0.7.1 # BSD
|
||||||
docutils>=0.11 # OSI-Approved Open Source, Public Domain
|
docutils>=0.11 # OSI-Approved Open Source, Public Domain
|
||||||
Jinja2>=2.10 # BSD License (3 clause)
|
Jinja2>=2.10 # BSD License (3 clause)
|
||||||
|
ruamel.yaml>=0.15
|
||||||
|
@ -37,3 +37,4 @@ console_scripts =
|
|||||||
owners = openstack_election.cmds.change_owners:main
|
owners = openstack_election.cmds.change_owners:main
|
||||||
template-emails = openstack_election.cmds.template_emails:main
|
template-emails = openstack_election.cmds.template_emails:main
|
||||||
setup-election-config = openstack_election.cmds.setup_election_config: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