|
| 1 | +""" |
| 2 | +Determines if NGO release automation job should run. |
| 3 | +
|
| 4 | +The script will check the following conditions: |
| 5 | +1. **Is today a release Saturday?** |
| 6 | + - The script checks if today is a Saturday that falls on the 4-week cycle for Netcode releases. |
| 7 | +2. **Is the [Unreleased] section of the CHANGELOG.md not empty?** |
| 8 | + - The script checks if the [Unreleased] section in the CHANGELOG.md contains meaningful entries. |
| 9 | +3. **Does the release branch already exist?** |
| 10 | + - If the release branch for the target release already exists, the script will not run. |
| 11 | + - For this you need to use separate function, see verifyNetcodeReleaseConditions definition |
| 12 | +""" |
| 13 | +#!/usr/bin/env python3 |
| 14 | +import datetime |
| 15 | +import re |
| 16 | +import sys |
| 17 | +import os |
| 18 | + |
| 19 | +UTILS_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '../Utils')) |
| 20 | +sys.path.insert(0, UTILS_DIR) |
| 21 | +from general_utils import get_package_version_from_manifest # nopep8 |
| 22 | +from git_utils import GithubUtils # nopep8 |
| 23 | +from config import getPackageManifestPath, getNetcodeGithubRepo, getPackageChangelogPath, getNetcodeReleaseBranchName # nopep8 |
| 24 | + |
| 25 | +def is_release_date(weekday, release_week_cycle, anchor_date): |
| 26 | + """ |
| 27 | + Checks if today is a weekday that falls on the release_week_cycle starting from anchor_date . |
| 28 | + Returns True if it is, False otherwise. |
| 29 | + """ |
| 30 | + today = datetime.date.today() |
| 31 | + # Condition 1: Must be a given weekday |
| 32 | + # Note as for example you could run a job that utilizes the fact that weekly trigger as per https://internaldocs.unity.com/yamato_continuous_integration/usage/jobs/recurring-jobs/#cron-syntax runs every Saturday, between 2 and 8 AM UTC depending on the load |
| 33 | + if today.weekday() != weekday: |
| 34 | + return False |
| 35 | + |
| 36 | + # Condition 2: Must be on a release_week_cycle interval from the anchor_date. |
| 37 | + days_since_anchor = (today - anchor_date).days |
| 38 | + weeks_since_anchor = days_since_anchor / 7 |
| 39 | + |
| 40 | + # We run on the first week of every release_week_cycle (e.g., week 0, 4, 8, ...) |
| 41 | + if weeks_since_anchor % release_week_cycle == 0: |
| 42 | + return True |
| 43 | + |
| 44 | + return False |
| 45 | + |
| 46 | +def is_changelog_empty(changelog_path): |
| 47 | + """ |
| 48 | + Checks if the [Unreleased] section in the CHANGELOG.md contains meaningful entries. |
| 49 | + It is considered "empty" if the section only contains headers (like ### Added) but no actual content. |
| 50 | + """ |
| 51 | + if not os.path.exists(changelog_path): |
| 52 | + print(f"Error: Changelog file not found at {changelog_path}") |
| 53 | + sys.exit(1) |
| 54 | + |
| 55 | + with open(changelog_path, 'r', encoding='UTF-8') as f: |
| 56 | + content = f.read() |
| 57 | + |
| 58 | + # This pattern starts where Unreleased section is placed |
| 59 | + # Then it matches in the first group all empty sections (only lines that are empty or start with ##) |
| 60 | + # The second group matches the start of the next Changelog entry (## [). |
| 61 | + # if both groups are matched it means that the Unreleased section is empty. |
| 62 | + pattern = re.compile(r"^## \[Unreleased\]\n((?:^###.*\n|^\s*\n)*)(^## \[)", re.MULTILINE) |
| 63 | + match = pattern.search(content) |
| 64 | + |
| 65 | + # If we find a match for the "empty unreleased changelog entry" pattern, it means the changelog IS empty. |
| 66 | + if match: |
| 67 | + print("Found an [Unreleased] section containing no release notes.") |
| 68 | + return True |
| 69 | + |
| 70 | + # If the pattern does not match, it means there must be meaningful content. |
| 71 | + return False |
| 72 | + |
| 73 | +def verifyNetcodeReleaseConditions(): |
| 74 | + """ |
| 75 | + Checks conditions and exits with appropriate status code. |
| 76 | + """ |
| 77 | + |
| 78 | + tools_manifest_path = getPackageManifestPath() |
| 79 | + tools_changelog_path = getPackageChangelogPath() |
| 80 | + tools_package_version = get_package_version_from_manifest(tools_manifest_path) |
| 81 | + tools_github_repo = getNetcodeGithubRepo() |
| 82 | + tools_release_branch_name = getNetcodeReleaseBranchName(tools_package_version) |
| 83 | + tools_github_token = os.environ.get("GITHUB_TOKEN") |
| 84 | + |
| 85 | + # An anchor date that was a Saturday. This is used to establish the 4-week cycle. |
| 86 | + # You can set this to any past Saturday that you want to mark as the start of a cycle (week 0). We use 2025-07-19 as starting point (previous release date). |
| 87 | + anchor_saturday = datetime.date(2025, 7, 19) |
| 88 | + |
| 89 | + print("--- Checking conditions for NGO release ---") |
| 90 | + |
| 91 | + if not os.path.exists(tools_manifest_path): |
| 92 | + print(f" Path does not exist: {tools_manifest_path}") |
| 93 | + sys.exit(1) |
| 94 | + |
| 95 | + if not os.path.exists(tools_changelog_path): |
| 96 | + print(f" Path does not exist: {tools_changelog_path}") |
| 97 | + sys.exit(1) |
| 98 | + |
| 99 | + if tools_package_version is None: |
| 100 | + print(f"Package version not found at {tools_manifest_path}") |
| 101 | + sys.exit(1) |
| 102 | + |
| 103 | + if not tools_github_token: |
| 104 | + print("Error: GITHUB_TOKEN environment variable not set.", file=sys.stderr) |
| 105 | + sys.exit(1) |
| 106 | + |
| 107 | + if not is_release_date(weekday=5, release_week_cycle=4, anchor_date=anchor_saturday): |
| 108 | + print("Condition not met: Today is not the scheduled release Saturday.") |
| 109 | + print("Job will not run. Exiting.") |
| 110 | + sys.exit(1) |
| 111 | + |
| 112 | + print("Condition met: Today is a scheduled release Saturday.") |
| 113 | + |
| 114 | + if is_changelog_empty(tools_changelog_path): |
| 115 | + print("Condition not met: The [Unreleased] section of the changelog is empty.") |
| 116 | + print("Job will not run. Exiting.") |
| 117 | + sys.exit(1) |
| 118 | + |
| 119 | + print("Condition met: The changelog contains entries to be released.") |
| 120 | + |
| 121 | + # Initialize PyGithub and get the repository object |
| 122 | + github_manager = GithubUtils(tools_github_token, tools_github_repo) |
| 123 | + |
| 124 | + if github_manager.is_branch_present(tools_release_branch_name): |
| 125 | + print("Condition not met: The release branch already exists.") |
| 126 | + print("Job will not run. Exiting.") |
| 127 | + sys.exit(1) |
| 128 | + |
| 129 | + print("Condition met: The release branch does not yet exist.") |
| 130 | + |
| 131 | + print("\nAll conditions met. The release preparation job can proceed.") |
| 132 | + sys.exit(0) |
| 133 | + |
| 134 | +if __name__ == "__main__": |
| 135 | + verifyNetcodeReleaseConditions() |
0 commit comments