Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automate release process #2734

Open
wants to merge 33 commits into
base: v5.1.0-rc
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
4d9ddd5
Add script to bump version numbers. (Initial revision).
tomchapman Jul 11, 2023
988753d
Add script to check for new authors. (Initial revision).
tomchapman Jul 11, 2023
88f709e
Move scripts to bin directory
tomchapman Jul 12, 2023
80d5a4f
Remove hard-coded paths.
tomchapman Jul 12, 2023
7df3104
Make KNOWN_AUTHORS a dictionary, holding more information to help ide…
tomchapman Jul 12, 2023
80129ae
Handle version numbers with multiple digits.
tomchapman Jul 12, 2023
0111768
Add classes VersionNumber and ShortVersionNumber.
tomchapman Jul 12, 2023
f329b47
Run git log command to get the list of authors.
tomchapman Jul 13, 2023
7ec28f4
Extracted function get_main_directory().
tomchapman Jul 13, 2023
b4db274
Also list emails for unrecognised authors.
tomchapman Jul 13, 2023
048207d
Copy functions from other scripts:
tomchapman Aug 10, 2023
db5d413
Apply copied functions to this script.
tomchapman Aug 10, 2023
696da06
Black formatting
tomchapman Aug 21, 2023
9b8bc24
No need for ShortVersionNumber to inherit from VersionNumber
tomchapman Aug 21, 2023
2d514f0
Use cwd argument to subprocess.run() to change directory (subprocess.…
tomchapman Aug 21, 2023
5329cf4
Use subprocess check=True argument to check for error.
tomchapman Aug 21, 2023
c7051f2
Use subprocess text=True argument (then no need to decode() stdout).
tomchapman Aug 21, 2023
8a27bfa
No need to catch real errors.
tomchapman Aug 21, 2023
f85ad2a
When catching KeyError, raise a new exception instead. Use an f-string.
tomchapman Aug 21, 2023
19357aa
Use get_main_directory() function.
tomchapman Aug 21, 2023
80b914c
Make VersionNumber a dataclass.
tomchapman Aug 21, 2023
cbb9823
Use str dunder method.
tomchapman Aug 21, 2023
80eced1
Remove commented-out copied code.
tomchapman Aug 21, 2023
e9686f2
Add command line argument for the new version number.
tomchapman Aug 21, 2023
6de3826
Corrected regex.
tomchapman Aug 21, 2023
a932ba0
Add command line description to update_version_number_in_file script.
tomchapman Aug 22, 2023
d52d622
Make ShortVersionNumber a dataclass.
tomchapman Aug 22, 2023
19fe7ed
Maint: Make update version/citation scripts executable
ZedThree Sep 20, 2023
5a8375c
Maint: Fix typo in next version regex
ZedThree Sep 20, 2023
a7f057e
Maint: Update list of known authors in citation script
ZedThree Sep 20, 2023
bc1b436
Maint: Don't print anything if no unrecognised authors
ZedThree Sep 20, 2023
a3fa61c
Add Tom Chapman to authors list
ZedThree Sep 20, 2023
33632ad
Fix typo in author list
ZedThree Sep 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 166 additions & 0 deletions tools/pylib/_boutpp_build/update_citations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
from pathlib import Path
from unidecode import unidecode
import yaml

authors_from_git = ["A Allen", "Aaron Fisher", "Adam Dempsey", "Andrew Allen", "arkabokshi", "Ben Dudson", "bendudson",
"Benjamin Dudson", "Brendan", "Brendan Shanahan", "Brett Friedman", "brey", "BS", "bshanahan",
"Chenhao Ma", "Chris MacMackin", "D Dickinson", "David", "David Bold", "David Dickinson",
"David Schwörer", "dependabot[bot]", "Dmitry Feliksovich Meyerson", "Dmitry Meyerson", "dschwoerer",
"Eric Medwedeff", "Erik Grinaker", "Fabio Riva", "George Breyiannis", "GitHub Merge Button",
"github-actions[bot]", "hahahasan", "Haruki Seto", "Haruki SETO", "holger", "Holger Jones",
"Hong Zhang", "Ilon Joseph", "Ilon Joseph - x31405", "Jarrod Leddy", "JB Leddy", "j-b-o",
"Jed Brown", "Jens Madsen", "John Omotani", "johnomotani", "jonesholger", "Joseph Parker",
"Joshua Sauppe", "Kab Seok Kang", "kangkabseok", "Kevin Savage", "Licheng Wang", "loeiten",
"Luke Easy", "M Leconte", "M V Umansky", "Marta Estarellas", "Matt Thomas", "Maxim Umansky",
"Maxim Umansky - x26041", "Michael Løiten", "Michael Loiten Magnussen", "Minwoo Kim",
"Nicholas Walkden", "Nick Walkden", "nick-walkden", "Olivier Izacard", "Pengwei Xi", "Peter Hill",
"Peter Naylor", "Qin, Yining", "Sajidah Ahmed", "Sanat Tiwari", "Sean Farley", "seanfarley",
"Seto Haruki", "Simon Myers", "Tianyang Xia", "Toby James", "tomc271", "Tongnyeol Rhee",
"Xiang Liu", "Xinliang Xu", "Xueqiao Xu", "Yining Qin", "ZedThree", "Zhanhui Wang"]


def parse_cff_file(filename):
with open(filename, "r", encoding='UTF-8') as stream:
try:
return yaml.safe_load(stream)
except yaml.YAMLError as exc:
print(exc)


def get_authors_from_cff_file():
filename = Path(r"C:\git\BOUT-dev") / "CITATION.cff"
file_contents = parse_cff_file(filename)
try:
return file_contents["authors"]
except KeyError as key_error:
print("Failed to find section:", key_error, "in", filename)


class ExistingAuthorNames:

def __init__(self, existing_authors):
self._existing_author_names = \
[(unidecode(a.get("given-names")), unidecode(a.get("family-names"))) for a in existing_authors]

def last_name_matches_surname_and_first_name_or_first_letter_matches_given_name(self, last_name, first_name):
matches = [n for n in self._existing_author_names if
n[1].casefold() == last_name.casefold()] # Last name matches surname

for match in matches:
if match[0].casefold() == first_name.casefold(): # The given name also matches author first name
return True
if match[0][0].casefold() == first_name[0].casefold(): # The first initial matches author first name
return True

def first_name_matches_surname_and_last_name_matches_given_name(self, first_name, last_name):
matches = [n for n in self._existing_author_names if
n[1].casefold() == first_name.casefold()] # First name matches surname

for match in matches:
if match[0].casefold() == last_name.casefold(): # The given name also matches author last name
return True

def surname_matches_whole_author_name(self, author):

surname_matches = [n for n in self._existing_author_names if n[1].casefold() == author.casefold()]
if len(surname_matches) > 0:
return True

def given_name_matches_matches_whole_author_name(self, author):
given_name_matches = [n for n in self._existing_author_names if n[0].casefold() == author.casefold()]
if len(given_name_matches) > 0:
return True

def combined_name_matches_whole_author_name(self, author):

combined_name_matches = [n for n in self._existing_author_names if
(n[0] + n[1]).casefold() == author.casefold()]
if len(combined_name_matches) > 0:
return True

def combined_name_reversed_matches(self, author):

combined_name_reversed_matches = [n for n in self._existing_author_names if
(n[1] + n[0]).casefold() == author.casefold()]
if len(combined_name_reversed_matches) > 0:
return True

def author_name_is_first_initial_and_surname_concatenated(self, author):
first_character = author[0]
remaining_characters = author[1:]
matches = [n for n in self._existing_author_names if
n[1].casefold() == remaining_characters.casefold()] # Second part of name matches surname
for match in matches:
if match[0][0].casefold() == first_character.casefold(): # The first initial matches author first name
return True


def author_found_in_existing_authors(author, existing_authors):
existing_author_names = ExistingAuthorNames(existing_authors)

names = author.split()
first_name = unidecode(names[0].replace(",", ""))
last_name = unidecode(names[-1])

if existing_author_names.last_name_matches_surname_and_first_name_or_first_letter_matches_given_name(
last_name, first_name):
return True

if existing_author_names.first_name_matches_surname_and_last_name_matches_given_name(first_name, last_name):
return True

if existing_author_names.surname_matches_whole_author_name(author):
return True

if existing_author_names.given_name_matches_matches_whole_author_name(author):
return True

if existing_author_names.combined_name_matches_whole_author_name(author):
return True

if existing_author_names.combined_name_reversed_matches(author):
return True

if existing_author_names.author_name_is_first_initial_and_surname_concatenated(author):
return True

return False


def update_citations():

existing_authors = get_authors_from_cff_file()

nonhuman_authors = [a for a in authors_from_git if "github" in a.casefold() or "dependabot" in a.casefold()]

known_authors = [a for a in authors_from_git if a in KNOWN_AUTHORS]

human_authors = [a for a in authors_from_git if a not in nonhuman_authors]

authors_to_search_for = [a for a in human_authors if a not in known_authors]

unrecognised_authors = [a for a in authors_to_search_for if
not author_found_in_existing_authors(a, existing_authors)]

print("The following authors were not recognised. Add to citations?")
for author in unrecognised_authors:
print(author)


KNOWN_AUTHORS = {"bendudson",
"brey",
"David Schwörer",
"dschwoerer",
"hahahasan",
"Ilon Joseph - x31405",
"kangkabseok",
"loeiten",
"Michael Loiten Magnussen",
"Maxim Umansky - x26041",
"nick-walkden",
"ZedThree",
# "tomc271"
}

if __name__ == '__main__':
update_citations()
53 changes: 53 additions & 0 deletions tools/pylib/_boutpp_build/update_version_number_in_files.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from pathlib import Path
import re


def get_full_filepath(filepath):

return Path(r"C:\git\BOUT-dev") / filepath
tomc271 marked this conversation as resolved.
Show resolved Hide resolved


def update_version_number_in_file(relative_filepath, pattern, new_version_number):

def get_replacement(match):
return match[0].replace(match[1], new_version_number)

full_filepath = get_full_filepath(relative_filepath)
with open(full_filepath, "r", encoding='UTF-8') as file:
file_contents = file.read()
updated_text = re.sub(pattern, get_replacement, file_contents, flags=re.MULTILINE)
with open(full_filepath, "w", encoding='UTF-8') as file:
file.write(updated_text)


def bump_version_numbers(new_version_number):

short_version_number = new_version_number[0:3]
new_minor_version_number = str(int(new_version_number[2]) + 1)
bout_next_version_number = re.sub(r"(?<=\d\.)\d(?=\.\d)", new_minor_version_number, new_version_number)
tomc271 marked this conversation as resolved.
Show resolved Hide resolved

update_version_number_in_file("configure.ac",
r"^AC_INIT\(\[BOUT\+\+\],\[(\d\.\d\.\d)\]", new_version_number)
update_version_number_in_file("CITATION.cff",
r"^version: (\d\.\d\.\d)", new_version_number)
update_version_number_in_file("manual/sphinx/conf.py",
r"^version = \"(\d\.\d)\"", short_version_number)
update_version_number_in_file("manual/sphinx/conf.py",
r"^release = \"(\d\.\d\.\d)\"", new_version_number)
update_version_number_in_file("manual/doxygen/Doxyfile_readthedocs",
r"^PROJECT_NUMBER = (\d\.\d\.\d)", new_version_number)
update_version_number_in_file("manual/doxygen/Doxyfile",
r"^PROJECT_NUMBER = (\d\.\d\.\d)", new_version_number)
update_version_number_in_file("CMakeLists.txt",
r"^set\(_bout_previous_version \"v(\d\.\d\.\d)\"\)", new_version_number)
update_version_number_in_file("CMakeLists.txt",
r"^set\(_bout_next_version \"(\d\.\d\.\d)\"\)", bout_next_version_number)
update_version_number_in_file("tools/pylib/_boutpp_build/backend.py",
r"_bout_previous_version = \"v(\d\.\d\.\d)\"", new_version_number)
update_version_number_in_file("tools/pylib/_boutpp_build/backend.py",
r"_bout_next_version = \"v(\d\.\d\.\d)\"", bout_next_version_number)


if __name__ == '__main__':

bump_version_numbers(new_version_number="6.1.0")
tomc271 marked this conversation as resolved.
Show resolved Hide resolved