Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NEW Update GitHub labels #29

Merged
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
39 changes: 33 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ relevant branch e.g. `5` will be used depending on the command-line `--branch` o
It will run all scripts in the `scripts/any` folder and then run all scripts in the applicable
`scripts/<cms-version>` folder depending on the command-line `--branch` option that's passed in.

This tool can also be used to standardise GitHub labels on all supported repositories.

## GitHub Token

This tool creates pull-request via the GitHub API. You need to set the `MS_GITHUB_TOKEN` environment variable in order
Expand All @@ -22,12 +24,17 @@ then you will get a 404 error when attempting to create pull-requests.

Delete this token once you have finished.

## Usage
## Installation

```bash
git clone [email protected]:silverstripe/module-standardiser.git
cd module-standardiser
composer install
```

## Usage - Standardising module files

```bash
MS_GITHUB_TOKEN=<token> php run.php update <options>
```

Expand All @@ -36,11 +43,11 @@ MS_GITHUB_TOKEN=<token> php run.php update <options>
MS_GITHUB_TOKEN=abc123 php run.php update --cms-major=5 --branch=next-minor --dry-run --only=silverstripe-config,silverstripe-assets
```

## Command line options:
### Command line options:

| Flag | Description |
| ---- | ------------|
| --cms-major=[version] | The major version of CMS to use (default: 5) |
| --cms-major=[version] | The major version of CMS to use (default: 5) which determines the list of supported modules to use |
| --branch=[type] | The branch type to use - `next-minor`\|`next-patch`\|`github-default` (default: `next-patch`) |
| --only=[modules] | Only include the specified modules (without account prefix) separated by commas e.g. `silverstripe-config,silverstripe-assets` |
| --exclude=[modules] | Exclude the specified modules (without account prefix) separated by commas e.g. `silverstripe-mfa,silverstripe-totp` |
Expand All @@ -51,15 +58,15 @@ MS_GITHUB_TOKEN=abc123 php run.php update --cms-major=5 --branch=next-minor --dr

**Note** that using `--branch=github-default` will only run scripts in the `scripts/default-branch` directory.

## GitHub API secondary rate limit
### GitHub API secondary rate limit

You may hit a secondary GitHub rate limit because this tool may create too many pull-requests in a short space of time.
To help with this the tool will always output the urls of all pull-requests updated and also the repos that were
updated so you can add them to the --exclude flag on subsequent re-runs.

## Adding new scripts
### Adding new scripts

### Where to add your script
#### Where to add your script

- `scripts/cms-<version>` to run on a specific cms-major
- `scripts/cms-any` to run on any cms-major
Expand All @@ -76,6 +83,26 @@ Do not use functions in `funcs_utils.php` as they are not intended to be used in
Scripts will be automatically wrapped in an anoymous function so you do not need to worry about variables crossing
over into different scripts.

## Usage - Standardising GitHub labels

```bash
MS_GITHUB_TOKEN=<token> php run.php labels <options>
```

**Example usage:**
```bash
MS_GITHUB_TOKEN=abc123 php run.php labels --dry-run --only=silverstripe-config,silverstripe-assets
```

### Command line options:

| Flag | Description |
| ---- | ------------|
| --only=[modules] | Only include the specified modules (without account prefix) separated by commas e.g. `silverstripe-config,silverstripe-assets` |
| --exclude=[modules] | Exclude the specified modules (without account prefix) separated by commas e.g. `silverstripe-mfa,silverstripe-totp` |
| --dry-run | Do not update labels in GitHub, output to terminal only |
| --no-delete | Do not delete `_data` directory before running |

## Updating the tool when a new major version of CMS is updated

Update the `CURRENT_CMS_MAJOR` constant in `run.php`
58 changes: 57 additions & 1 deletion funcs_utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ function github_token()
/**
* Makes a request to the github API
*/
function github_api($url, $data = [])
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);
Expand All @@ -230,6 +230,9 @@ function github_api($url, $data = [])
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, !empty($data));
if ($httpMethod) {
GuySartorelli marked this conversation as resolved.
Show resolved Hide resolved
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $httpMethod);
}
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'User-Agent: silverstripe-module-standardiser',
'Accept: application/vnd.github+json',
Expand Down Expand Up @@ -295,6 +298,24 @@ function output_repos_with_prs_created()
$io->writeln('');
}

/**
* Outputs a list of repos that that had labels 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_labels_updated()
{
if (running_unit_tests()) {
return;
}
global $REPOS_WITH_LABELS_UPDATED;
$io = io();
$io->writeln('');
$io->writeln('Repos with labels created (add to --exclude if you need to re-run):');
$io->writeln(implode(',', $REPOS_WITH_LABELS_UPDATED));
$io->writeln('');
}

/**
* Works out which branch in a module to checkout before running scripts on it
*
Expand Down Expand Up @@ -407,3 +428,38 @@ function current_branch_cms_major(
}
return (string) $cmsMajor;
}

function setup_directories($input, $dirs = [DATA_DIR, MODULES_DIR]) {
if (!$input->getOption('no-delete')) {
foreach ($dirs as $dir) {
remove_dir($dir);
}
}
foreach ($dirs as $dir) {
if (!file_exists($dir)) {
mkdir($dir);
}
}
}

function filtered_modules($cmsMajor, $input) {
$modules = supported_modules($cmsMajor);
if ($cmsMajor === CURRENT_CMS_MAJOR) {
// only include extra_repositories() when using the current CMS major version because the extra rexpositories
// don't have multi majors branches supported e.g. gha-generate-matrix
$modules = array_merge($modules, extra_repositories());
}
if ($input->getOption('only')) {
$only = explode(',', $input->getOption('only'));
$modules = array_filter($modules, function ($module) use ($only) {
return in_array($module['repo'], $only);
});
}
if ($input->getOption('exclude')) {
$exclude = explode(',', $input->getOption('exclude'));
$modules = array_filter($modules, function ($module) use ($exclude) {
return !in_array($module['repo'], $exclude);
});
}
return $modules;
}
153 changes: 153 additions & 0 deletions labels_command.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<?php

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Command\Command;

// ! IMPORTANT !
// - Any labels on the GitHub repo that are not defined in LABELS_COLORS will be deleted
// - Any labels in LABELS_COLORS that do not exist on the GitHub repo will be created
//
// Do not prefix color with hash i.e. use 'cccccc' not '#cccccc'
const LABELS_COLORS = [
'affects/v4' => '5319e7',
'affects/v5' => '0e8a16',
'complexity/low' => 'c2e0c6',
'complexity/medium' => 'fef2c0',
'complexity/high' => 'f9d0c4',
'Epic' => '3e4b9e',
'impact/low' => 'fef2c0',
'impact/medium' => 'f7c6c7',
'impact/high' => 'eb6420',
'impact/critical' => 'e11d21',
'rfc/accepted' => 'dddddd',
'rfc/draft' => 'dddddd',
'type/api-break' => '1d76db',
'type/bug' => 'd93f0b',
'type/docs' => '02d7e1',
'type/enhancement' => '0e8a16',
'type/userhelp' => 'c5def5',
'type/UX' => '006b75',
'type/other' => '975515',
];

// Rename existing labels 'from' => 'to'
const LABELS_RENAME = [
'effort/easy' => 'complexity/low',
'effort/medium' => 'complexity/medium',
'effort/hard' => 'complexity/high',
'change/major' => 'type/api-break',
'type/api-change' => 'type/api-break',
];

$labelsCommand = function(InputInterface $input, OutputInterface $output): int {
// This is the code that is executed when running the 'labels' command

// variables
global $OUT, $REPOS_WITH_LABELS_UPDATED;
$OUT = $output;

// validate system is ready
validate_system();

// setup directories
setup_directories($input, [DATA_DIR]);

// modules
$modules = [];
$repos = [];
$modulesCurrentMajor = filtered_modules(CURRENT_CMS_MAJOR, $input);
$modulesPreviousMajor = filtered_modules(CURRENT_CMS_MAJOR - 1, $input);
foreach ([$modulesCurrentMajor, $modulesPreviousMajor] as $modulesList) {
foreach ($modulesList as $module) {
$repo = $module['repo'];
if (in_array($repo, $repos)) {
continue;
}
$modules[] = $module;
$repos[] = $repo;
}
}

// update labels
foreach ($modules as $module) {
$account = $module['account'];
$repo = $module['repo'];

// Fetch labels
$labels = github_api("https://api.github.com/repos/$account/$repo/labels");

foreach ($labels as $key => $label) {
// $label is an array, for example:
// 'id' => 427423377
// 'node_id' => MDU6TGFiZWw0Mjc0MjMzNzc="
// 'url' => "https://api.github.com/repos/silverstripe/silverstripe-config/labels/affects/v4"
// 'name' => "affects/v4"
// 'color' => "5319e7"
// 'default' => false
// 'description' => NULL
$url = $label['url'];
$name = $label['name']; // e.g. 'affects/v4'

// Rename label
// https://docs.github.com/en/rest/issues/labels#update-a-label
if (array_key_exists($name, LABELS_RENAME)) {
$newName = LABELS_RENAME[$name];
info("Updating label $name to $newName in $repo");
if ($input->getOption('dry-run')) {
info('Not updating label on GitHub because --dry-run option is set');
} else {
github_api($url, ['new_name' => $newName], 'PATCH');
}
$name = $newName;
GuySartorelli marked this conversation as resolved.
Show resolved Hide resolved
// Update $url replacing the $name at the end with $newName
$url = substr($url, 0, strlen($url) - strlen($name)) . $newName;
GuySartorelli marked this conversation as resolved.
Show resolved Hide resolved
$labels[$key]['name'] = $newName;
$labels[$key]['url'] = $url;
}

// Delete label
// https://docs.github.com/en/rest/issues/labels#delete-a-label
if (!array_key_exists($name, LABELS_COLORS)) {
info("Deleting label $name from $repo");
if ($input->getOption('dry-run')) {
info('Not deleting label on GitHub because --dry-run option is set');
} else {
github_api($url, [], 'DELETE');
}
GuySartorelli marked this conversation as resolved.
Show resolved Hide resolved
continue;
}

// Update label color
// https://docs.github.com/en/rest/issues/labels#update-a-label
if (LABELS_COLORS[$name] !== $label['color']) {
info("Updating label color $name on $repo");
if ($input->getOption('dry-run')) {
info('Not updating label color on GitHub because --dry-run option is set');
} else {
github_api($url, ['color' => LABELS_COLORS[$name]], 'PATCH');
}
}
}

// Create missing labels
// https://docs.github.com/en/rest/issues/labels#create-a-label
foreach (LABELS_COLORS as $name => $color) {
foreach ($labels as $label) {
if ($name === $label['name']) {
continue 2;
}
}
info("Creating label $name on $repo");
if ($input->getOption('dry-run')) {
info('Not creating label on GitHub because --dry-run option is set');
} else {
$url = "https://api.github.com/repos/$account/$repo/labels";
github_api($url, ['name' => $name,'color' => $color]);
}
}
$REPOS_WITH_LABELS_UPDATED[] = $repo;
}
output_repos_with_labels_updated();
return Command::SUCCESS;
};
Loading
Loading