From 77f6210dfdfc20c207a7089cec6c9319d3eb8950 Mon Sep 17 00:00:00 2001 From: Steve Starke Date: Mon, 11 Aug 2025 13:51:03 +0200 Subject: [PATCH 1/4] Automatically maintain list of supported releases We discussed that the list of supported releases should not be part of the Tarook project directly as its documentation is versioned. Instead, we decided the supported releases should be maintained in the meta repository. This commit introduces the necessary changes based on the existing approach. Based upon: https://gitlab.com/alasca.cloud/tarook/tarook/-/merge_requests/1875 and especially: https://gitlab.com/alasca.cloud/tarook/tarook/-/commit/5afcaad6b30f65f42925060fd4680212321e7cb9 --- .gitlab-ci.yml | 13 +++ ci/generate_supported_releases.py | 129 +++++++++++++++++++++++++++ ci/push_changes.py | 88 ++++++++++++++++++ ci/supported_releases_template.jinja | 32 +++++++ requirements.txt | 3 + src/SUMMARY.md | 2 + src/supported_releases.rst | 1 + 7 files changed, 268 insertions(+) create mode 100644 ci/generate_supported_releases.py create mode 100644 ci/push_changes.py create mode 100644 ci/supported_releases_template.jinja create mode 100644 src/supported_releases.rst diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3c13658..0b1ce46 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,6 @@ stages: - test + - housekeeping - deploy testBook: @@ -45,6 +46,18 @@ spellcheck: tags: - docker +generate_supported_releases: + stage: housekeeping + image: "${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/python" + script: + - pip install -r requirements.txt + - python3 ci/generate_supported_releases.py + - python3 ci/push_changes.py --commit-subtitle "Update supported releases" + tags: + - docker + only: + - main + pages: stage: deploy image: diff --git a/ci/generate_supported_releases.py b/ci/generate_supported_releases.py new file mode 100644 index 0000000..d5f486e --- /dev/null +++ b/ci/generate_supported_releases.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 + +import argparse +import os +from datetime import datetime, timedelta, timezone +from packaging.version import parse as parse_version +from packaging.version import Version +from jinja2 import Environment, FileSystemLoader +from itertools import groupby +import gitlab + + +def get_latest_patch_versions(versions): + """Group versions by major.minor and keep only the latest patch version.""" + + def get_major_minor(version): + return '.'.join(version['version'][1:].split('.')[:1]) + + # Sort versions by major.minor and then by patch level + versions.sort(key=lambda x: (get_major_minor(x), + parse_version(x['version'])), + reverse=True) + + # Group by major.minor and take only the first (latest) patch version + latest_versions = [] + for _, group in groupby(versions, key=get_major_minor): + latest_versions.append(next(group)) + + return latest_versions + + +def get_supported_releases(versions, current_date): + """ + Determine supported versions based on the rules: + - A release becomes EOL when there are three newer (major/minor) versions + - But earliest four weeks after it has been released + """ + supported = [] + major_minor_versions = [] + + # Get only latest patch versions + latest_versions = get_latest_patch_versions(versions) + + for ver in latest_versions: + major_minor = '.'.join(ver['version'].split('.')[:2]) + if major_minor not in major_minor_versions: + major_minor_versions.append(major_minor) + + for ver in latest_versions: + release_date = datetime.fromisoformat(ver['date']) + four_weeks_passed = (current_date - release_date) > timedelta(weeks=4) + major_minor = '.'.join(ver['version'].split('.')[:2]) + newer_major_minor_versions = len([ + v for v in major_minor_versions + if parse_version(v) > parse_version(major_minor) + ]) + + if newer_major_minor_versions < 3 or not four_weeks_passed: + supported.append({ + 'number': ver['version'], + 'date': datetime.fromisoformat(ver['date']) + .strftime('%Y-%m-%d'), + 'eol': (release_date + timedelta(weeks=4)).strftime('%Y-%m-%d') + if newer_major_minor_versions >= 3 else None + }) + + supported.sort(key=lambda x: Version(x['number']), + reverse=True) + return supported + + +def generate_doc_from_template(versions, template_path, output_path): + """Generate documentation using Jinja2 template.""" + env = Environment( + loader=FileSystemLoader(os.path.dirname(template_path)), + trim_blocks=True, + lstrip_blocks=True + ) + + template_name = os.path.basename(template_path) + template = env.get_template(template_name) + + content = template.render(versions=versions) + + os.makedirs(os.path.dirname(output_path), exist_ok=True) + + with open(output_path, 'w') as f: + f.write(content) + + +def main(): + parser = argparse.ArgumentParser( + description="Generate supported versions documentation" + ) + parser.add_argument( + "--template", default="./ci/supported_releases_template.jinja", + help="Path to Jinja template file" + ) + parser.add_argument( + "--output", default="./src/supported_releases.rst", + help="Output file path" + ) + args = parser.parse_args() + + # Authenticate to Gitlab + gl = gitlab.Gitlab( + "https://gitlab.com", + private_token=os.environ['META_TOKEN']) + gl.auth() + project = gl.projects.get(29738620, lazy=True) + + # Get versions and their dates + releases = project.releases.list(get_all=True) + versions = [] + for release in releases: + versions.append({'version': release.tag_name, + 'date': release.released_at}) + + current_date = datetime.now(timezone.utc) + + supported_releases = get_supported_releases(versions, current_date) + + generate_doc_from_template(supported_releases, args.template, args.output) + + print(f"Generated supported versions documentation at {args.output}") + + +if __name__ == "__main__": + main() diff --git a/ci/push_changes.py b/ci/push_changes.py new file mode 100644 index 0000000..9114824 --- /dev/null +++ b/ci/push_changes.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 + +""" +This helper script is used in the CI/CD + +It checks the repository for changed files and +commits and pushes them directly to Gitlab. +A commit message with references to the original author +and previous commit hash is created. +""" + +import gitlab +import os +import argparse +import sys +from pathlib import Path +from git import Repo +from datetime import datetime + +COMMIT_SUBTITLE = "Update repository" + + +def create_commit_data(changedFiles, commit_subtitle): + def _create_actions_array(changedFiles): + actions = [] + for changed_file in changedFiles: + actions.append({ + 'action': 'update', + 'file_path': changed_file, + 'content': open(changed_file).read() + }) + return actions + + commit_data = { + 'branch': os.environ.get("CI_COMMIT_BRANCH", + os.environ.get( + "CI_MERGE_REQUEST_SOURCE_BRANCH_NAME")), + 'commit_message': f'CI - {commit_subtitle} ({datetime.now().strftime("%Y-%m-%dT%H:%M:%S")})\n\n' + # noqa 501 + f'Job ID: {os.environ.get("CI_JOB_ID", "/")}\n' + + f'Job Name: {os.environ.get("CI_JOB_NAME", "/")}\n' + + f'Started by: {os.environ.get("GITLAB_USER_LOGIN", "/")}\n' + + '---\n' + + 'PREVIOUS COMMIT:\n' + + f'{os.environ.get("CI_COMMIT_SHA", "/")}', + 'actions': _create_actions_array(changedFiles) + } + return commit_data + + +def main(arguments): + # Setup connection to our Gitlab instance + gl = gitlab.Gitlab( + "https://gitlab.com", + private_token=os.environ["PUSH_TOKEN"] + ) + repo_project_id = gl.projects.get(os.environ.get("CI_PROJECT_ID")) + + meta_repo = Repo(Path.cwd()) + print("--- DIFF START ---") + print(meta_repo.git.diff(meta_repo.head.commit.tree)) + print("--- DIFF END ---") + changedFiles = [item.a_path for item in meta_repo.index.diff(None)] + print(f'The following files changed: {changedFiles}') + if changedFiles: + repo_project_id.commits.create( + create_commit_data(changedFiles, arguments.commit_subtitle) + ) + print("Created & Pushed commit.") + else: + print("Nothing to commit.") + + +if __name__ == '__main__': + argpar = argparse.ArgumentParser( + description=( + "Script which connects to Gitlab and commits and pushes \ + changes to the repository." + ) + ) + argpar.add_argument( + '--commit-subtitle', + type=str, + default=COMMIT_SUBTITLE, + help="String which will be used in the commit title", + ) + args = argpar.parse_args() + + sys.exit(main(args) or 0) diff --git a/ci/supported_releases_template.jinja b/ci/supported_releases_template.jinja new file mode 100644 index 0000000..7a27c5b --- /dev/null +++ b/ci/supported_releases_template.jinja @@ -0,0 +1,32 @@ +{# supported_releases.rst.template #} + +Supported TAROOK Releases +========================= + +This document lists all currently supported releases. + +Tarook versions follow the [Semantic Versioning format](https://semver.org/): x.y.z, where x denotes the major version, y the minor version, and z the patch version. + +Currently Supported Releases +---------------------------- + +{% for version in versions %} +* `{{ version.number }}` ({{ version.date }}) +{% if version.eol %} + * **EOL date:** {{ version.eol }} +{% endif %} +{% endfor %} + + +Upgrade Recommendations +----------------------- + +* We recommend staying on the latest supported version +* Plan upgrades before a version reaches its EOL date +* More information about Tarook release upgrades can be found in the [official documentation](https://docs.tarook.cloud/devel/user/tutorial/upgrade-release.html) + +Support Policy +-------------- + +A release becomes EOL when there are three newer (major/minor) versions but earliest four weeks after it has been released. +Until then a release will be supported with hotfixes. diff --git a/requirements.txt b/requirements.txt index 00c861b..2ec460e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,5 @@ python-gitlab yaml2ics +packaging +jinja2 +gitpython \ No newline at end of file diff --git a/src/SUMMARY.md b/src/SUMMARY.md index ef84005..d3cb0d9 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -1,5 +1,7 @@ # Summary +- [Supported TAROOK Releases](./supported_releases.rst) + - [Development Process](./01-development-process.md) - [Bug Reports](./01-bugs.md) diff --git a/src/supported_releases.rst b/src/supported_releases.rst new file mode 100644 index 0000000..e166442 --- /dev/null +++ b/src/supported_releases.rst @@ -0,0 +1 @@ +.gitkeep -- GitLab From 6fde583429e2968365b68daac24c4b7c89a5c3bf Mon Sep 17 00:00:00 2001 From: Steve Starke Date: Mon, 11 Aug 2025 15:18:04 +0200 Subject: [PATCH 2/4] Push updated ICAL output as otherwise subscribers don't get the updates. --- .gitlab-ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0b1ce46..7cd2db4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -86,9 +86,6 @@ calendar: - yaml2ics meetings/yaml_input/tarook.yaml > meetings/ical_output/tarook.ics # this is for compatibility during the transition period - cp meetings/ical_output/tarook.ics meetings/ical_output/shore_leave.ics - - mkdir -p public && cp -r meetings/ical_output public/meetings - artifacts: - paths: - - public + - python3 ci/push_changes.py --commit-subtitle "Update ICS file" tags: - docker -- GitLab From b3d7f9785ec048c4af2f65e81786a9c1f535f557 Mon Sep 17 00:00:00 2001 From: Steve Starke Date: Mon, 11 Aug 2025 15:35:19 +0200 Subject: [PATCH 3/4] Update book.toml --- book.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book.toml b/book.toml index c672a07..575da15 100644 --- a/book.toml +++ b/book.toml @@ -1,6 +1,6 @@ [book] -authors = ["Yaook Contributors"] +authors = ["Tarook Contributors"] language = "en" multilingual = false src = "src" -title = "Yaook Development Process Documentation" +title = "TAROOK Meta Information" -- GitLab From 64b4f2c832c532d0c17300dfe22fdbdf6d65e3d5 Mon Sep 17 00:00:00 2001 From: Steve Starke Date: Mon, 11 Aug 2025 16:03:24 +0200 Subject: [PATCH 4/4] CI: Introduce flake8 in new lint stage --- .flake8 | 2 ++ .gitlab-ci.yml | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 .flake8 diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..e14b761 --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +max-line-length=88 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7cd2db4..7a2a32f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,5 @@ stages: + - lint - test - housekeeping - deploy @@ -39,13 +40,22 @@ testCalendar: - docker spellcheck: - stage: test + stage: lint image: registry.gitlab.com/uhurutec/md-spellcheck script: - spellcheck.sh . tags: - docker +flake8: + image: "${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/python" + script: + - 'pip3 install flake8' + - 'python3 -m flake8 ./*/**.py' + stage: lint + tags: + - docker + generate_supported_releases: stage: housekeeping image: "${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/python" -- GitLab