summaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorDylan Baker <dylan.c.baker@intel.com>2021-01-04 16:04:45 -0800
committerMarge Bot <eric+marge@anholt.net>2021-01-07 00:07:56 +0000
commitbc9e09360f824612468a8a8d34987bbf7f820b86 (patch)
tree050cc98e4dc1ea7eb602317928785d0e60b337a6 /bin
parent8587e57f1225e9171e6ef22a349c925ab9fa1da7 (diff)
bin: Add script for manipulating the release calendar
Currently it only handles creating entries for a new rc. Acked-by: Eric Engestrom <eric@engestrom.ch> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/8341>
Diffstat (limited to 'bin')
-rwxr-xr-xbin/gen_calendar_entries.py129
-rw-r--r--bin/gen_calendar_entries_test.py108
2 files changed, 237 insertions, 0 deletions
diff --git a/bin/gen_calendar_entries.py b/bin/gen_calendar_entries.py
new file mode 100755
index 00000000000..45562eefec8
--- /dev/null
+++ b/bin/gen_calendar_entries.py
@@ -0,0 +1,129 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+
+# Copyright © 2021 Intel Corporation
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+"""Helper script for manipulating the release calendar."""
+
+from __future__ import annotations
+import argparse
+import csv
+import contextlib
+import datetime
+import pathlib
+import subprocess
+import typing
+
+if typing.TYPE_CHECKING:
+ import _csv
+ from typing_extensions import Protocol
+
+ class RCArguments(Protocol):
+ """Typing information for release-candidate command arguments."""
+
+ manager: str
+
+ class ExtendArguments(Protocol):
+ """Typing information for extend command arguments."""
+
+ series: str
+ count: int
+
+
+ CalendarRowType = typing.Tuple[typing.Optional[str], str, str, str, typing.Optional[str]]
+
+
+_ROOT = pathlib.Path(__file__).parent.parent
+CALENDAR_CSV = _ROOT / 'docs' / 'release-calendar.csv'
+VERSION = _ROOT / 'VERSION'
+LAST_RELEASE = 'This is the last planned release of the {}.x series.'
+OR_FINAL = 'Or {}.0 final.'
+
+
+def read_calendar() -> typing.List[CalendarRowType]:
+ """Read the calendar and return a list of it's rows."""
+ with CALENDAR_CSV.open('r') as f:
+ return [typing.cast('CalendarRowType', tuple(r)) for r in csv.reader(f)]
+
+
+def commit(message: str) -> None:
+ """Commit the changes the the release-calendar.csv file."""
+ subprocess.run(['git', 'commit', str(CALENDAR_CSV), '--message', message])
+
+
+
+def _calculate_release_start(major: str, minor: str) -> datetime.date:
+ """Calclulate the start of the release for release candidates.
+
+ This is quarterly, on the second wednesday, in Januray, April, July, and Octobor.
+ """
+ quarter = datetime.date.fromisoformat(f'20{major}-0{[1, 4, 7, 10][int(minor)]}-01')
+
+ # Wednesday is 3
+ day = quarter.isoweekday()
+ if day > 3:
+ # this will walk back into the previous month, it's much simpler to
+ # duplicate the 14 than handle the calculations for the month and year
+ # changing.
+ return quarter.replace(day=quarter.day - day + 3 + 14)
+ elif day < 3:
+ quarter = quarter.replace(day=quarter.day + 3 - day)
+ return quarter.replace(day=quarter.day + 14)
+
+
+
+def release_candidate(args: RCArguments) -> None:
+ """Add release candidate entries."""
+ with VERSION.open('r') as f:
+ version = f.read().rstrip('-devel')
+ major, minor, _ = version.split('.')
+ date = _calculate_release_start(major, minor)
+
+ data = read_calendar()
+
+ with CALENDAR_CSV.open('w') as f:
+ writer = csv.writer(f)
+ writer.writerows(data)
+
+ writer.writerow([f'{major}.{minor}', date.isoformat(), f'{major}.{minor}.0-rc1', args.manager])
+ for row in range(2, 4):
+ date = date + datetime.timedelta(days=7)
+ writer.writerow([None, date.isoformat(), f'{major}.{minor}.0-rc{row}', args.manager])
+ date = date + datetime.timedelta(days=7)
+ writer.writerow([None, date.isoformat(), f'{major}.{minor}.0-rc4', args.manager, OR_FINAL.format(f'{major}.{minor}')])
+
+ commit(f'docs: Add calendar entries for {major}.{minor} release candidates.')
+
+
+def main() -> None:
+ parser = argparse.ArgumentParser()
+ sub = parser.add_subparsers()
+
+ rc = sub.add_parser('release-candidate', aliases=['rc'], help='Generate calendar entries for a release candidate.')
+ rc.add_argument('manager', help="the name of the person managing the release.")
+ rc.set_defaults(func=release_candidate)
+
+ args = parser.parse_args()
+ args.func(args)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/bin/gen_calendar_entries_test.py b/bin/gen_calendar_entries_test.py
new file mode 100644
index 00000000000..30df79d4008
--- /dev/null
+++ b/bin/gen_calendar_entries_test.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+
+# Copyright © 2021 Intel Corporation
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+from __future__ import annotations
+from unittest import mock
+import argparse
+import csv
+import tempfile
+import os
+import pathlib
+
+import pytest
+
+from . import gen_calendar_entries
+
+
+@pytest.fixture(autouse=True, scope='module')
+def disable_git_commits() -> None:
+ """Mock out the commit function so no git commits are made durring testing."""
+ with mock.patch('bin.gen_calendar_entries.commit', mock.Mock()):
+ yield
+
+
+class TestReleaseStart:
+
+ def test_first_is_wednesday(self) -> None:
+ d = gen_calendar_entries._calculate_release_start('20', '0')
+ assert d.day == 15
+ assert d.month == 1
+ assert d.year == 2020
+
+ def test_first_is_before_wednesday(self) -> None:
+ d = gen_calendar_entries._calculate_release_start('19', '0')
+ assert d.day == 16
+ assert d.month == 1
+ assert d.year == 2019
+
+ def test_first_is_after_wednesday(self) -> None:
+ d = gen_calendar_entries._calculate_release_start('21', '0')
+ assert d.day == 13
+ assert d.month == 1
+ assert d.year == 2021
+
+
+class TestRC:
+
+ ORIGINAL_DATA = [
+ ('20.3', '2021-01-13', '20.3.3', 'Dylan Baker', ''),
+ ('', '2021-01-27', '20.3.4', 'Dylan Baker', 'Last planned release of the 20.3.x series'),
+ ]
+
+ @pytest.fixture(autouse=True, scope='class')
+ def mock_version(self) -> None:
+ """Keep the version set at a specific value."""
+ with tempfile.TemporaryDirectory() as d:
+ v = os.path.join(d, 'version')
+ with open(v, 'w') as f:
+ f.write('21.0.0-devel\n')
+
+ with mock.patch('bin.gen_calendar_entries.VERSION', pathlib.Path(v)):
+ yield
+
+ @pytest.fixture(autouse=True)
+ def mock_data(self) -> None:
+ """inject our test data.."""
+ with tempfile.TemporaryDirectory() as d:
+ c = os.path.join(d, 'calendar.csv')
+ with open(c, 'w') as f:
+ writer = csv.writer(f)
+ writer.writerows(self.ORIGINAL_DATA)
+
+ with mock.patch('bin.gen_calendar_entries.CALENDAR_CSV', pathlib.Path(c)):
+ yield
+
+ def test_basic(self) -> None:
+ args = argparse.Namespace()
+ args.manager = "Dylan Baker"
+ gen_calendar_entries.release_candidate(args)
+
+ expected = self.ORIGINAL_DATA.copy()
+ expected.append(('21.0', '2021-01-13', f'21.0.0-rc1', 'Dylan Baker'))
+ expected.append(( '', '2021-01-20', f'21.0.0-rc2', 'Dylan Baker'))
+ expected.append(( '', '2021-01-27', f'21.0.0-rc3', 'Dylan Baker'))
+ expected.append(( '', '2021-02-03', f'21.0.0-rc4', 'Dylan Baker', 'Or 21.0.0 final.'))
+
+ actual = gen_calendar_entries.read_calendar()
+
+ assert actual == expected