From ece7910a54ba256f97ed2653a48cc2355129799c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Chrobot?= <124174716+michalChrobot@users.noreply.github.com> Date: Mon, 27 Oct 2025 12:44:35 +0100 Subject: [PATCH 1/8] ci: Corrections to release automation (#3737) * Ser release frequency to 10 AM on Sunday's * Make the release fail silently when release conditions are not met * Added possibility of a manual trigger that is disregarding the release date * Removed standards failure about "Using directive is unnecessary" * Revert "Removed standards failure about "Using directive is unnecessary"" This reverts commit 722b5b9277085b8f21cde3d0b52f0353ae003f13. * Updated wrench * temp update of BossRoom branch + TargetFramework update * Updated typo in cmb tests runAll definition * Added wrench regeneration step to the release automation * removed apparently unnecessary directive * surpressed standards check complaint --- .yamato/ngo-publish.yml | 2 +- .../ReleaseAutomation/release_config.py | 4 +-- .../run_release_preparation.py | 9 ++++- Tools/scripts/Utils/general_utils.py | 36 +++++++++++++++++++ .../scripts/Utils/verifyReleaseConditions.py | 12 ++++++- Tools/scripts/release.py | 4 ++- .../NetworkVariable/ResizableBitVector.cs | 6 ++++ .../netcode.standards.csproj | 2 +- 8 files changed, 68 insertions(+), 7 deletions(-) diff --git a/.yamato/ngo-publish.yml b/.yamato/ngo-publish.yml index e43eeb3ea3..05671ecf1d 100644 --- a/.yamato/ngo-publish.yml +++ b/.yamato/ngo-publish.yml @@ -4,7 +4,7 @@ ngo_release_preparation: triggers: recurring: - branch: develop # We make new releases from this branch - frequency: weekly # Run at some point every Saturday. Note that it's restricted to every 4th Saturday inside the script + frequency: "10 * * 1" # Runs every Sunday at 10:00 AM rerun: always commands: - pip install PyGithub diff --git a/Tools/scripts/ReleaseAutomation/release_config.py b/Tools/scripts/ReleaseAutomation/release_config.py index de2085d016..2b014aea45 100644 --- a/Tools/scripts/ReleaseAutomation/release_config.py +++ b/Tools/scripts/ReleaseAutomation/release_config.py @@ -39,9 +39,9 @@ def __init__(self): self.yamato_project_id = '1201' self.command_to_run_on_release_branch = make_package_release_ready - self.release_weekday = 5 # Saturday + self.release_weekday = 6 # Sunday self.release_week_cycle = 4 # Release every 4 weeks - self.anchor_date = datetime.date(2025, 7, 19) # Anchor date for the release cycle (previous release Saturday) + self.anchor_date = datetime.date(2025, 7, 20) # Anchor date for the release cycle (previous release Sunday) self.package_version = get_package_version_from_manifest(self.manifest_path) self.release_branch_name = f"release/{self.package_version}" # Branch from which we want to release diff --git a/Tools/scripts/ReleaseAutomation/run_release_preparation.py b/Tools/scripts/ReleaseAutomation/run_release_preparation.py index 2c57472023..562680f8d3 100644 --- a/Tools/scripts/ReleaseAutomation/run_release_preparation.py +++ b/Tools/scripts/ReleaseAutomation/run_release_preparation.py @@ -18,7 +18,14 @@ def PrepareNetcodePackageForRelease(): print("\nStep 1: Verifying release conditions...") verifyReleaseConditions(config) - + except Exception as e: + print("\n--- Release conditions were not met ---", file=sys.stderr) + print(f"Reason: {e}", file=sys.stderr) + sys.exit(0) # In this case we want the job not to fail because this will be an intended blocking behavior + + + + try: print("\nStep 2: Creating release branch...") create_branch_execute_commands_and_push(config) diff --git a/Tools/scripts/Utils/general_utils.py b/Tools/scripts/Utils/general_utils.py index 4f1a025357..09f99cd954 100644 --- a/Tools/scripts/Utils/general_utils.py +++ b/Tools/scripts/Utils/general_utils.py @@ -75,6 +75,42 @@ def update_package_version_by_patch(package_manifest_path): return new_package_version +def regenerate_wrench(): + """ + It runs Tools/regenerate-ci.cmd OR Tools/regenerate-ci.sh script + to regenerate the CI files. (depending on the OS) + + This is needed because wrench scripts content is created dynamically depending on the available editors + """ + + # --- Regenerate the CI files --- + print("\nRegenerating CI files...") + script_path = "" + if platform.system() == "Windows": + script_path = os.path.join('Tools', 'CI', 'regenerate-ci.bat') + else: # macOS and Linux + script_path = os.path.join('Tools' 'CI', 'regenerate-ci.sh') + + if not os.path.exists(script_path): + print(f"Error: Regeneration script not found at '{script_path}'.") + return + + try: + # Execute the regeneration script + # On non-Windows systems, the script might need execute permissions. + if platform.system() != "Windows": + os.chmod(script_path, 0o755) + + print(f"Running '{script_path}'...") + subprocess.run([script_path], check=True, shell=True) + print("CI regeneration completed successfully.") + + except subprocess.CalledProcessError as e: + print(f"Error: The CI regeneration script failed with exit code {e.returncode}.") + except Exception as e: + print(f"An unexpected error occurred while running the regeneration script: {e}") + + def update_validation_exceptions(validation_file, package_version): """ Updates the ValidationExceptions.json file with the new package version. diff --git a/Tools/scripts/Utils/verifyReleaseConditions.py b/Tools/scripts/Utils/verifyReleaseConditions.py index e1f3d0a8c6..0cce113cd3 100644 --- a/Tools/scripts/Utils/verifyReleaseConditions.py +++ b/Tools/scripts/Utils/verifyReleaseConditions.py @@ -4,6 +4,7 @@ The script will check the following conditions: 1. **Is today a release day?** - The script checks if today is a specified in ReleaseConfig weekday that falls on the release cycle of the team. + - Note that if the job is triggered manually, this condition will be bypassed. 2. **Is the [Unreleased] section of the CHANGELOG.md not empty?** - The script checks if the [Unreleased] section in the CHANGELOG.md contains meaningful entries. 3. **Does the release branch already exist?** @@ -21,6 +22,15 @@ import re from release_config import ReleaseConfig +def get_yamato_trigger_type(): + """ + Retrieves the trigger type for the current Yamato job from environment variables. + In other words, we can check if the job was triggered manually, by a schedule, or by a PR, etc. + """ + trigger_type = os.environ.get('YAMATO_TRIGGER_TYPE', 'unknown') + return trigger_type + + def is_release_date(weekday, release_week_cycle, anchor_date): """ Checks if today is a weekday that falls on the release_week_cycle starting from anchor_date . @@ -100,7 +110,7 @@ def verifyReleaseConditions(config: ReleaseConfig): ] try: - if not is_release_date(config.release_weekday, config.release_week_cycle, config.anchor_date): + if get_yamato_trigger_type() != "Manual" and not is_release_date(config.release_weekday, config.release_week_cycle, config.anchor_date): error_messages.append(f"Condition not met: Today is not the scheduled release day. It should be weekday: {config.release_weekday}, every {config.release_week_cycle} weeks starting from {config.anchor_date}.") if is_changelog_empty(config.changelog_path, exceptions): diff --git a/Tools/scripts/release.py b/Tools/scripts/release.py index 8533f4bff7..e89c79aab0 100644 --- a/Tools/scripts/release.py +++ b/Tools/scripts/release.py @@ -13,7 +13,7 @@ import subprocess import platform -from Utils.general_utils import get_package_version_from_manifest, update_changelog, update_validation_exceptions # nopep8 +from Utils.general_utils import get_package_version_from_manifest, update_changelog, update_validation_exceptions, regenerate_wrench # nopep8 def make_package_release_ready(manifest_path, changelog_path, validation_exceptions_path, package_version): @@ -36,6 +36,8 @@ def make_package_release_ready(manifest_path, changelog_path, validation_excepti # package version is already know as explained in # https://github.cds.internal.unity3d.com/unity/dots/pull/14318 update_changelog(changelog_path, package_version) + # Make sure that the wrench scripts are up to date + regenerate_wrench() if __name__ == '__main__': diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/ResizableBitVector.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/ResizableBitVector.cs index b4b4b76e83..e7376c2b2f 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/ResizableBitVector.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/ResizableBitVector.cs @@ -1,6 +1,12 @@ using System; using Unity.Collections; + +// The following gets complained on by standards check that is +// likely not analyzing extension method usage deeply enough and thinks the using directive is unused. +// The directive is required for GetUnsafePtr() to be available. +#pragma warning disable IDE0005 using Unity.Collections.LowLevel.Unsafe; +#pragma warning restore IDE0005 namespace Unity.Netcode { diff --git a/dotnet-tools/netcode.standards/netcode.standards.csproj b/dotnet-tools/netcode.standards/netcode.standards.csproj index 74e37a2170..ece14dcc49 100644 --- a/dotnet-tools/netcode.standards/netcode.standards.csproj +++ b/dotnet-tools/netcode.standards/netcode.standards.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 true netcode.standards ./nupkg From db2c9a2f48522f4e417f52062b6f402009321eb5 Mon Sep 17 00:00:00 2001 From: michal-chrobot Date: Mon, 27 Oct 2025 13:04:09 +0100 Subject: [PATCH 2/8] validation correction --- Tools/scripts/Utils/verifyReleaseConditions.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Tools/scripts/Utils/verifyReleaseConditions.py b/Tools/scripts/Utils/verifyReleaseConditions.py index 0cce113cd3..a29cc5ebf1 100644 --- a/Tools/scripts/Utils/verifyReleaseConditions.py +++ b/Tools/scripts/Utils/verifyReleaseConditions.py @@ -28,6 +28,7 @@ def get_yamato_trigger_type(): In other words, we can check if the job was triggered manually, by a schedule, or by a PR, etc. """ trigger_type = os.environ.get('YAMATO_TRIGGER_TYPE', 'unknown') + return trigger_type @@ -110,7 +111,11 @@ def verifyReleaseConditions(config: ReleaseConfig): ] try: - if get_yamato_trigger_type() != "Manual" and not is_release_date(config.release_weekday, config.release_week_cycle, config.anchor_date): + trigger_type = get_yamato_trigger_type() + print(f"Yamato Trigger Type: {trigger_type}") + is_manual = trigger_type in {"Manual", "AdHoc"} + + if if not is_manual and not is_release_date(config.release_weekday, config.release_week_cycle, config.anchor_date): error_messages.append(f"Condition not met: Today is not the scheduled release day. It should be weekday: {config.release_weekday}, every {config.release_week_cycle} weeks starting from {config.anchor_date}.") if is_changelog_empty(config.changelog_path, exceptions): @@ -124,7 +129,7 @@ def verifyReleaseConditions(config: ReleaseConfig): for i, msg in enumerate(error_messages, 1): print(f"{i}. {msg}") print("\nJob will not run. Exiting.") - sys.exit(1) + sys.exit(0) except Exception as e: print("\n--- ERROR: Release Verification failed ---", file=sys.stderr) From e94157fe6d746acae31564a0affedc71143cf1e2 Mon Sep 17 00:00:00 2001 From: michal-chrobot Date: Mon, 27 Oct 2025 13:07:14 +0100 Subject: [PATCH 3/8] typo --- Tools/scripts/Utils/verifyReleaseConditions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/scripts/Utils/verifyReleaseConditions.py b/Tools/scripts/Utils/verifyReleaseConditions.py index a29cc5ebf1..d811a94f55 100644 --- a/Tools/scripts/Utils/verifyReleaseConditions.py +++ b/Tools/scripts/Utils/verifyReleaseConditions.py @@ -115,7 +115,7 @@ def verifyReleaseConditions(config: ReleaseConfig): print(f"Yamato Trigger Type: {trigger_type}") is_manual = trigger_type in {"Manual", "AdHoc"} - if if not is_manual and not is_release_date(config.release_weekday, config.release_week_cycle, config.anchor_date): + if not is_manual and not is_release_date(config.release_weekday, config.release_week_cycle, config.anchor_date): error_messages.append(f"Condition not met: Today is not the scheduled release day. It should be weekday: {config.release_weekday}, every {config.release_week_cycle} weeks starting from {config.anchor_date}.") if is_changelog_empty(config.changelog_path, exceptions): From 8527c27e5368f7a2b4c3c468fe2cec45ec6b6728 Mon Sep 17 00:00:00 2001 From: michal-chrobot Date: Mon, 27 Oct 2025 13:18:11 +0100 Subject: [PATCH 4/8] release trigger name correction --- Tools/scripts/Utils/verifyReleaseConditions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tools/scripts/Utils/verifyReleaseConditions.py b/Tools/scripts/Utils/verifyReleaseConditions.py index d811a94f55..4a993928a3 100644 --- a/Tools/scripts/Utils/verifyReleaseConditions.py +++ b/Tools/scripts/Utils/verifyReleaseConditions.py @@ -26,8 +26,9 @@ def get_yamato_trigger_type(): """ Retrieves the trigger type for the current Yamato job from environment variables. In other words, we can check if the job was triggered manually, by a schedule, or by a PR, etc. + This value is set to Recurring when triggered by automation """ - trigger_type = os.environ.get('YAMATO_TRIGGER_TYPE', 'unknown') + trigger_type = os.environ.get('YAMATO_TRIGGER_TYPE', 'Manual') return trigger_type @@ -112,7 +113,6 @@ def verifyReleaseConditions(config: ReleaseConfig): try: trigger_type = get_yamato_trigger_type() - print(f"Yamato Trigger Type: {trigger_type}") is_manual = trigger_type in {"Manual", "AdHoc"} if not is_manual and not is_release_date(config.release_weekday, config.release_week_cycle, config.anchor_date): From 60ad490cfeca903f90d70317e1dc5393dcf51ef0 Mon Sep 17 00:00:00 2001 From: michal-chrobot Date: Mon, 27 Oct 2025 13:23:33 +0100 Subject: [PATCH 5/8] added missed import --- Tools/scripts/Utils/general_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Tools/scripts/Utils/general_utils.py b/Tools/scripts/Utils/general_utils.py index 09f99cd954..0e4e676940 100644 --- a/Tools/scripts/Utils/general_utils.py +++ b/Tools/scripts/Utils/general_utils.py @@ -4,6 +4,7 @@ import os import re import datetime +import platform UNRELEASED_CHANGELOG_SECTION_TEMPLATE = r""" ## [Unreleased] From 46ddee034a4b9deae1de0ccc72cef55677696ba6 Mon Sep 17 00:00:00 2001 From: michal-chrobot Date: Mon, 27 Oct 2025 13:37:32 +0100 Subject: [PATCH 6/8] typo --- Tools/scripts/Utils/general_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/scripts/Utils/general_utils.py b/Tools/scripts/Utils/general_utils.py index 0e4e676940..27c4a83ff0 100644 --- a/Tools/scripts/Utils/general_utils.py +++ b/Tools/scripts/Utils/general_utils.py @@ -90,7 +90,7 @@ def regenerate_wrench(): if platform.system() == "Windows": script_path = os.path.join('Tools', 'CI', 'regenerate-ci.bat') else: # macOS and Linux - script_path = os.path.join('Tools' 'CI', 'regenerate-ci.sh') + script_path = os.path.join('Tools', 'CI', 'regenerate-ci.sh') if not os.path.exists(script_path): print(f"Error: Regeneration script not found at '{script_path}'.") From d03d39cbf3514749d9ca57387a7d71ba11ebb1ef Mon Sep 17 00:00:00 2001 From: michal-chrobot Date: Mon, 27 Oct 2025 13:51:02 +0100 Subject: [PATCH 7/8] corrected script name --- Tools/scripts/Utils/general_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tools/scripts/Utils/general_utils.py b/Tools/scripts/Utils/general_utils.py index 27c4a83ff0..6bd5ddf85e 100644 --- a/Tools/scripts/Utils/general_utils.py +++ b/Tools/scripts/Utils/general_utils.py @@ -88,9 +88,9 @@ def regenerate_wrench(): print("\nRegenerating CI files...") script_path = "" if platform.system() == "Windows": - script_path = os.path.join('Tools', 'CI', 'regenerate-ci.bat') + script_path = os.path.join('Tools', 'CI', 'regenerate.bat') else: # macOS and Linux - script_path = os.path.join('Tools', 'CI', 'regenerate-ci.sh') + script_path = os.path.join('Tools', 'CI', 'regenerate.sh') if not os.path.exists(script_path): print(f"Error: Regeneration script not found at '{script_path}'.") From f32e85f975d2e710af78ed520e4152f443310014 Mon Sep 17 00:00:00 2001 From: michal-chrobot Date: Mon, 27 Oct 2025 14:04:09 +0100 Subject: [PATCH 8/8] missing import --- Tools/scripts/Utils/general_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Tools/scripts/Utils/general_utils.py b/Tools/scripts/Utils/general_utils.py index 6bd5ddf85e..121272dfee 100644 --- a/Tools/scripts/Utils/general_utils.py +++ b/Tools/scripts/Utils/general_utils.py @@ -5,6 +5,7 @@ import re import datetime import platform +import subprocess UNRELEASED_CHANGELOG_SECTION_TEMPLATE = r""" ## [Unreleased]