Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/bin/gen_calendar_entries.py
4545 views
1
#!/usr/bin/env python3
2
# SPDX-License-Identifier: MIT
3
4
# Copyright © 2021 Intel Corporation
5
6
# Permission is hereby granted, free of charge, to any person obtaining a copy
7
# of this software and associated documentation files (the "Software"), to deal
8
# in the Software without restriction, including without limitation the rights
9
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
# copies of the Software, and to permit persons to whom the Software is
11
# furnished to do so, subject to the following conditions:
12
13
# The above copyright notice and this permission notice shall be included in
14
# all copies or substantial portions of the Software.
15
16
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
# SOFTWARE.
23
24
"""Helper script for manipulating the release calendar."""
25
26
from __future__ import annotations
27
import argparse
28
import csv
29
import contextlib
30
import datetime
31
import pathlib
32
import subprocess
33
import typing
34
35
if typing.TYPE_CHECKING:
36
import _csv
37
from typing_extensions import Protocol
38
39
class RCArguments(Protocol):
40
"""Typing information for release-candidate command arguments."""
41
42
manager: str
43
44
class FinalArguments(Protocol):
45
"""Typing information for release command arguments."""
46
47
series: str
48
manager: str
49
zero_released: bool
50
51
class ExtendArguments(Protocol):
52
"""Typing information for extend command arguments."""
53
54
series: str
55
count: int
56
57
58
CalendarRowType = typing.Tuple[typing.Optional[str], str, str, str, typing.Optional[str]]
59
60
61
_ROOT = pathlib.Path(__file__).parent.parent
62
CALENDAR_CSV = _ROOT / 'docs' / 'release-calendar.csv'
63
VERSION = _ROOT / 'VERSION'
64
LAST_RELEASE = 'This is the last planned release of the {}.x series.'
65
OR_FINAL = 'Or {}.0 final.'
66
67
68
def read_calendar() -> typing.List[CalendarRowType]:
69
"""Read the calendar and return a list of it's rows."""
70
with CALENDAR_CSV.open('r') as f:
71
return [typing.cast('CalendarRowType', tuple(r)) for r in csv.reader(f)]
72
73
74
def commit(message: str) -> None:
75
"""Commit the changes the the release-calendar.csv file."""
76
subprocess.run(['git', 'commit', str(CALENDAR_CSV), '--message', message])
77
78
79
80
def _calculate_release_start(major: str, minor: str) -> datetime.date:
81
"""Calclulate the start of the release for release candidates.
82
83
This is quarterly, on the second wednesday, in Januray, April, July, and Octobor.
84
"""
85
quarter = datetime.date.fromisoformat(f'20{major}-0{[1, 4, 7, 10][int(minor)]}-01')
86
87
# Wednesday is 3
88
day = quarter.isoweekday()
89
if day > 3:
90
# this will walk back into the previous month, it's much simpler to
91
# duplicate the 14 than handle the calculations for the month and year
92
# changing.
93
return quarter.replace(day=quarter.day - day + 3 + 14)
94
elif day < 3:
95
quarter = quarter.replace(day=quarter.day + 3 - day)
96
return quarter.replace(day=quarter.day + 14)
97
98
99
def release_candidate(args: RCArguments) -> None:
100
"""Add release candidate entries."""
101
with VERSION.open('r') as f:
102
version = f.read().rstrip('-devel')
103
major, minor, _ = version.split('.')
104
date = _calculate_release_start(major, minor)
105
106
data = read_calendar()
107
108
with CALENDAR_CSV.open('w') as f:
109
writer = csv.writer(f)
110
writer.writerows(data)
111
112
writer.writerow([f'{major}.{minor}', date.isoformat(), f'{major}.{minor}.0-rc1', args.manager])
113
for row in range(2, 4):
114
date = date + datetime.timedelta(days=7)
115
writer.writerow([None, date.isoformat(), f'{major}.{minor}.0-rc{row}', args.manager])
116
date = date + datetime.timedelta(days=7)
117
writer.writerow([None, date.isoformat(), f'{major}.{minor}.0-rc4', args.manager, OR_FINAL.format(f'{major}.{minor}')])
118
119
commit(f'docs: Add calendar entries for {major}.{minor} release candidates.')
120
121
122
def _calculate_next_release_date(next_is_zero: bool) -> datetime.date:
123
"""Calculate the date of the next release.
124
125
If the next is .0, we have the release in seven days, if the next is .1,
126
then it's in 14
127
"""
128
date = datetime.date.today()
129
day = date.isoweekday()
130
if day < 3:
131
delta = 3 - day
132
elif day > 3:
133
# this will walk back into the previous month, it's much simpler to
134
# duplicate the 14 than handle the calculations for the month and year
135
# changing.
136
delta = (3 - day)
137
else:
138
delta = 0
139
delta += 7
140
if not next_is_zero:
141
delta += 7
142
return date + datetime.timedelta(days=delta)
143
144
145
def final_release(args: FinalArguments) -> None:
146
"""Add final release entries."""
147
data = read_calendar()
148
date = _calculate_next_release_date(not args.zero_released)
149
150
with CALENDAR_CSV.open('w') as f:
151
writer = csv.writer(f)
152
writer.writerows(data)
153
154
base = 1 if args.zero_released else 0
155
156
writer.writerow([args.series, date.isoformat(), f'{args.series}.{base}', args.manager])
157
for row in range(base + 1, 3):
158
date = date + datetime.timedelta(days=14)
159
writer.writerow([None, date.isoformat(), f'{args.series}.{row}', args.manager])
160
date = date + datetime.timedelta(days=14)
161
writer.writerow([None, date.isoformat(), f'{args.series}.3', args.manager, LAST_RELEASE.format(args.series)])
162
163
commit(f'docs: Add calendar entries for {args.series} release.')
164
165
166
def extend(args: ExtendArguments) -> None:
167
"""Extend a release."""
168
@contextlib.contextmanager
169
def write_existing(writer: _csv._writer, current: typing.List[CalendarRowType]) -> typing.Iterator[CalendarRowType]:
170
"""Write the orinal file, yield to insert new entries.
171
172
This is a bit clever, basically what happens it writes out the
173
original csv file until it reaches the start of the release after the
174
one we're appending, then it yields the last row. When control is
175
returned it writes out the rest of the original calendar data.
176
"""
177
last_row: typing.Optional[CalendarRowType] = None
178
in_wanted = False
179
for row in current:
180
if in_wanted and row[0]:
181
in_wanted = False
182
assert last_row is not None
183
yield last_row
184
if row[0] == args.series:
185
in_wanted = True
186
if in_wanted and len(row) >= 5 and row[4] in {LAST_RELEASE.format(args.series), OR_FINAL.format(args.series)}:
187
# If this was the last planned release and we're adding more,
188
# then we need to remove that message and add it elsewhere
189
r = list(row)
190
r[4] = None
191
# Mypy can't figure this out…
192
row = typing.cast('CalendarRowType', tuple(r))
193
last_row = row
194
writer.writerow(row)
195
# If this is the only entry we can hit a case where the contextmanager
196
# hasn't yielded
197
if in_wanted:
198
yield row
199
200
current = read_calendar()
201
202
with CALENDAR_CSV.open('w') as f:
203
writer = csv.writer(f)
204
with write_existing(writer, current) as row:
205
# Get rid of -rcX as well
206
if '-rc' in row[2]:
207
first_point = int(row[2].split('rc')[-1]) + 1
208
template = '{}.0-rc{}'
209
days = 7
210
else:
211
first_point = int(row[2].split('-')[0].split('.')[-1]) + 1
212
template = '{}.{}'
213
days = 14
214
215
date = datetime.date.fromisoformat(row[1])
216
for i in range(first_point, first_point + args.count):
217
date = date + datetime.timedelta(days=days)
218
r = [None, date.isoformat(), template.format(args.series, i), row[3], None]
219
if i == first_point + args.count - 1:
220
if days == 14:
221
r[4] = LAST_RELEASE.format(args.series)
222
else:
223
r[4] = OR_FINAL.format(args.series)
224
writer.writerow(r)
225
226
commit(f'docs: Extend calendar entries for {args.series} by {args.count} releases.')
227
228
229
def main() -> None:
230
parser = argparse.ArgumentParser()
231
sub = parser.add_subparsers()
232
233
rc = sub.add_parser('release-candidate', aliases=['rc'], help='Generate calendar entries for a release candidate.')
234
rc.add_argument('manager', help="the name of the person managing the release.")
235
rc.set_defaults(func=release_candidate)
236
237
fr = sub.add_parser('release', help='Generate calendar entries for a final release.')
238
fr.add_argument('manager', help="the name of the person managing the release.")
239
fr.add_argument('series', help='The series to extend, such as "29.3" or "30.0".')
240
fr.add_argument('--zero-released', action='store_true', help='The .0 release was today, the next release is .1')
241
fr.set_defaults(func=final_release)
242
243
ex = sub.add_parser('extend', help='Generate additional entries for a release.')
244
ex.add_argument('series', help='The series to extend, such as "29.3" or "30.0".')
245
ex.add_argument('count', type=int, help='The number of new entries to add.')
246
ex.set_defaults(func=extend)
247
248
args = parser.parse_args()
249
args.func(args)
250
251
252
if __name__ == "__main__":
253
main()
254
255