Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ But you might still need to adapt your code:
- Added a migration step for api repositories to fix `mkdocs.yml` when the previous `mkdocstrings-python` v2 migration moved only `paths: ["src"]` under `handlers.python.options` but not `paths: ["py"]`.
- Fixed runners for jobs that require Docker and where wrongly converted to `ubuntu-slim` in v0.15.0, changing them back to `ubuntu-24.04` to avoid Docker-related failures. The template and the migration script were both updated to reflect this change.
- Updated the repo-config migration workflow template and migration script so existing repositories also add the `merge_group` trigger and skip the job unless the event is `pull_request_target`, allowing the workflow to be used as a required merge-queue check.
- Added a migration step to remove the copilot review request from the Protect version branch protection rules. This was also done by v0.15.0 in theory, but the migration step was wrong and didn't update it properly.
65 changes: 65 additions & 0 deletions cookiecutter/migrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ def main() -> None:
print("Migrating protolint and publish-to-pypi runners to ubuntu-24.04...")
migrate_docker_based_runners()
print("=" * 72)
print("Updating 'Protect version branches' GitHub ruleset...")
migrate_protect_version_branches_ruleset()
print("=" * 72)
print()

if _manual_steps:
Expand Down Expand Up @@ -303,6 +306,68 @@ def migrate_repo_config_migration_merge_group_trigger() -> None:
)


def migrate_protect_version_branches_ruleset() -> None:
"""Update the 'Protect version branches' GitHub ruleset.

Uses the GitHub API (via ``gh`` CLI) to check whether the
'Protect version branches' ruleset on the current repository is aligned
with the current template. Recent template changes include:

* Removing the ``copilot_code_review`` rule.

If the ruleset is already aligned, prints an informational message.
If it needs updating, applies the changes via the API without removing
any existing required status checks.
If the ruleset is not found at all, issues a manual-step message that
points the user to the docs.
"""
rule_name = "Protect version branches"
docs_url = (
"https://frequenz-floss.github.io/frequenz-repo-config-python/"
"user-guide/start-a-new-project/configure-github/#rulesets"
)

# Build a link to the repo's ruleset settings for manual-step messages.
ruleset_url = get_ruleset_settings_url() or docs_url

# ── Fetch ruleset details ────────────────────────────────────────
ruleset = get_ruleset(rule_name)
if ruleset is None:
manual_step(
f"The '{rule_name}' GitHub ruleset was not found (or the gh CLI "
"is not available / the API call failed). "
"Please check whether it should exist for this repository. "
f"If it should, import it following the instructions at: {docs_url}"
)
return

# ── Detect and apply changes in-memory ───────────────────────────────
changes: list[str] = []
updated_rules = []

for rule in ruleset.get("rules", []):
if rule.get("type") == "copilot_code_review":
changes.append("remove copilot_code_review")
continue
updated_rules.append(rule)

if not changes:
print(f" Ruleset '{rule_name}' is already up to date")
return

# ── Push the update ───────────────────────────────────────────────────
ruleset["rules"] = updated_rules
if not update_ruleset(ruleset["id"], ruleset):
manual_step(
f"Failed to update the '{rule_name}' ruleset via the GitHub API. "
f"Please apply the following changes manually at {ruleset_url}: "
+ "; ".join(changes)
)
return

print(f" Updated ruleset '{rule_name}': " + ", ".join(changes))


def apply_patch(patch_content: str) -> None:
"""Apply a patch using the patch utility."""
subprocess.run(["patch", "-p1"], input=patch_content.encode(), check=True)
Expand Down
Loading