From 9c89101b553000702cb634ac6f4a05f5a9f4992a Mon Sep 17 00:00:00 2001 From: Steve Boyd Date: Wed, 22 May 2024 13:04:33 +1200 Subject: [PATCH] NEW Update rulesets --- funcs_utils.php | 41 ++++++++++++- rulesets/branch-ruleset.json | 44 ++++++++++++++ rulesets/tag-ruleset.json | 34 +++++++++++ rulesets_command.php | 111 +++++++++++++++++++++++++++++++++++ run.php | 12 ++++ 5 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 rulesets/branch-ruleset.json create mode 100644 rulesets/tag-ruleset.json create mode 100644 rulesets_command.php diff --git a/funcs_utils.php b/funcs_utils.php index 1208e25..edccfa6 100644 --- a/funcs_utils.php +++ b/funcs_utils.php @@ -152,7 +152,8 @@ function github_api($url, $data = [], $httpMethod = '') { // silverstripe-themes has a kind of weird redirect only for api requests $url = str_replace('/silverstripe-themes/silverstripe-simple', '/silverstripe/silverstripe-simple', $url); - info("Making curl request to $url"); + $method = $httpMethod ? strtoupper($httpMethod) : 'GET'; + info("Making $method curl request to $url"); $token = github_token(); $jsonStr = empty($data) ? '' : json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); $ch = curl_init($url); @@ -244,6 +245,44 @@ function output_repos_with_labels_updated() $io->writeln(''); } +/** + * Outputs a list of repos that that had rulesets updated + * If there was an error with a run (probably a secondary rate limit), this can be + * copy pasted into the --exclude option for the next run to continue from where you left off + */ +function output_repos_with_rulesets_created_or_updated() +{ + if (running_unit_tests()) { + return; + } + global $REPOS_WITH_RULESETS_UPDATED; + $io = io(); + $io->writeln(''); + $io->writeln('Repos with rulesets created/updated (add to --exclude if you need to re-run):'); + $io->writeln(implode(',', $REPOS_WITH_RULESETS_UPDATED)); + $io->writeln(''); +} + +function create_ruleset($type, $additionalBranchConditions = []) +{ + $ruleset = file_get_contents("rulesets/$type-ruleset.json"); + if (!$ruleset) { + error("Could not read ruleset for $type"); + } + $json = json_decode($ruleset, true); + if ($type == 'branch') { + $json['name'] = BRANCH_RULESET_NAME; + } elseif ($type === 'tag') { + $json['name'] = TAG_RULESET_NAME; + } else { + error("Invalid ruleset type: $type"); + } + foreach ($additionalBranchConditions as $value) { + $json['conditions']['ref_name']['include'][] = $value; + } + return $json; +} + /** * Works out which branch in a module to checkout before running scripts on it * diff --git a/rulesets/branch-ruleset.json b/rulesets/branch-ruleset.json new file mode 100644 index 0000000..44e87a6 --- /dev/null +++ b/rulesets/branch-ruleset.json @@ -0,0 +1,44 @@ +{ + "name": "", + "target": "branch", + "enforcement": "active", + "conditions": { + "ref_name": { + "exclude": [], + "include": [ + "refs/heads/[0-9]*" + ] + } + }, + "rules": [ + { + "type": "deletion" + }, + { + "type": "non_fast_forward" + }, + { + "type": "creation" + }, + { + "type": "update" + }, + { + "type": "pull_request", + "parameters": { + "required_approving_review_count": 2, + "dismiss_stale_reviews_on_push": true, + "require_code_owner_review": false, + "require_last_push_approval": true, + "required_review_thread_resolution": false + } + } + ], + "bypass_actors": [ + { + "actor_id": 5, + "actor_type": "RepositoryRole", + "bypass_mode": "always" + } + ] +} diff --git a/rulesets/tag-ruleset.json b/rulesets/tag-ruleset.json new file mode 100644 index 0000000..e6740f2 --- /dev/null +++ b/rulesets/tag-ruleset.json @@ -0,0 +1,34 @@ +{ + "name": "", + "target": "tag", + "enforcement": "active", + "conditions": { + "ref_name": { + "exclude": [], + "include": [ + "~ALL" + ] + } + }, + "rules": [ + { + "type": "deletion" + }, + { + "type": "non_fast_forward" + }, + { + "type": "creation" + }, + { + "type": "update" + } + ], + "bypass_actors": [ + { + "actor_id": 5, + "actor_type": "RepositoryRole", + "bypass_mode": "always" + } + ] +} diff --git a/rulesets_command.php b/rulesets_command.php new file mode 100644 index 0000000..1dc7aba --- /dev/null +++ b/rulesets_command.php @@ -0,0 +1,111 @@ + 'emteknetnz', 'repo' => 'test-thing'], + ['account' => 'emteknetnz', 'repo' => 'old-gha-run-tests'], + ]; + + $branchRuleset = file_get_contents('rulesets/branch-ruleset.json'); + $tagRuleset = file_get_contents('rulesets/tag-ruleset.json'); + + // update rulesets + foreach ($modules as $module) { + $account = $module['account']; + $repo = $module['repo']; + + // Fetch existing rulesets + $rulesets = github_api("https://api.github.com/repos/$account/$repo/rulesets"); + $branchRulesetID = 0; + $tagRulesetID = 0; + foreach ($rulesets as $ruleset) { + $id = $ruleset['id']; + $name = $ruleset['name']; + if ($name === BRANCH_RULESET_NAME) { + $branchRulesetID = $id; + } + if ($name === TAG_RULESET_NAME) { + $tagRulesetID = $id; + } + } + + // Get any additional branches to add + // Assumption is that if the default branch is main/master, then the repo uses + // a non-numeric style branching system (e.g. main, master) and that needs to be protected + // [0-9]* branch protection will still be applied, on the chance that the repo is converted + // to uses a numeric style branch system in the future and we would want branch protection + // to start immediately on the new branches + $additionalBranchConditions = []; + $defaultBranch = github_api("https://api.github.com/repos/$account/$repo")['default_branch']; + if (in_array($defaultBranch, ['main', 'master'])) { + $additionalBranchConditions[] = "refs/heads/$defaultBranch"; + } + + // Create rulesets + $branchRuleset = create_ruleset('branch', $additionalBranchConditions); + $tagRuleset = create_ruleset('tag'); + + // Create new rulesets + if (!$branchRulesetID) { + $url = "https://api.github.com/repos/$account/$repo/rulesets"; + github_api($url, $branchRuleset, 'POST'); + } + if (!$tagRulesetID) { + $url = "https://api.github.com/repos/$account/$repo/rulesets"; + github_api($url, $tagRuleset, 'POST'); + } + + // Update existing ruleset - don't bother to check if the ruleset is the same + // this is a fairly quick update so no need to optimise this + if ($branchRulesetID) { + $url = "https://api.github.com/repos/$account/$repo/rulesets/$branchRulesetID"; + github_api($url, $branchRuleset, 'PUT'); + } + if ($tagRulesetID) { + $url = "https://api.github.com/repos/$account/$repo/rulesets/$tagRulesetID"; + github_api($url, $tagRuleset, 'PUT'); + } + $REPOS_WITH_RULESETS_UPDATED[] = $repo; + } + output_repos_with_rulesets_created_or_updated(); + return Command::SUCCESS; +}; diff --git a/run.php b/run.php index 643909d..d1c2505 100644 --- a/run.php +++ b/run.php @@ -5,6 +5,7 @@ include 'funcs_utils.php'; include 'update_command.php'; include 'labels_command.php'; +include 'rulesets_command.php'; use SilverStripe\SupportedModules\MetaData; use Symfony\Component\Console\Application; @@ -19,6 +20,8 @@ const TOOL_URL = 'https://github.com/silverstripe/module-standardiser'; const PR_TITLE = 'MNT Run module-standardiser'; const PR_DESCRIPTION = 'This pull-request was created automatically by [module-standardiser](' . TOOL_URL . ')'; +const BRANCH_RULESET_NAME = 'Silverstripe branch ruleset'; +const TAG_RULESET_NAME = 'Silverstripe tag ruleset'; // global variables $MODULE_DIR = ''; @@ -26,6 +29,7 @@ $PRS_CREATED = []; $REPOS_WITH_PRS_CREATED = []; $REPOS_WITH_LABELS_UPDATED = []; +$REPOS_WITH_RULESETS_UPDATED = []; $OUT = null; // options @@ -102,6 +106,14 @@ ->addOption(...$optionNoDelete) ->setCode($labelsCommand); +$app->register('rulesets') + ->setDescription('Script to set rulesets on all repos only on the silverstripe account') + ->addOption(...$optionOnly) + ->addOption(...$optionExclude) + ->addOption(...$optionDryRun) + ->addOption(...$optionNoDelete) + ->setCode($rulesetsCommand); + try { $app->run(); } catch (Error|Exception $e) {