Skip to content

Commit

Permalink
NEW Use supported modules repo for branch-based logic
Browse files Browse the repository at this point in the history
  • Loading branch information
GuySartorelli committed May 2, 2024
1 parent 5e6fcd9 commit f80c990
Show file tree
Hide file tree
Showing 7 changed files with 272 additions and 196 deletions.
16 changes: 16 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# For more information about the properties used in
# this file, please see the EditorConfig documentation:
# http://editorconfig.org/

root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
*.json
!composer.json
.phpunit.result.cache
vendor/
composer.lock
23 changes: 18 additions & 5 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,23 @@ runs:
uses: shivammathur/setup-php@1a18b2267f80291a81ca1d33e7c851fe09e7dfc4 # v2.22.0
with:
php-version: '8.1'
tools: composer:v2

# This is shared between runs, not just jobs. It means the first time the repo runs the job it'll
# need to download requirements for the first time, after that it will be plenty quick
# https://docs.github.com/en/actions/advanced-guides/caching-dependencies-to-speed-up-workflows
- name: Enable shared composer cache
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # @v3.3.1
with:
path: ~/.cache/composer
key: shared-composer-cache

# Install composer dependencies for this action itself
- name: Composer
run: |
cd ${{ github.action_path }}
composer install
cd -
- name: Determine if should merge-up
id: determine
Expand All @@ -17,10 +34,6 @@ runs:
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_REF_NAME: ${{ github.ref_name }}
run: |
# The minimum cms major with commercial support - configured at a global level
# Change this when major version support changes
MINIMUM_CMS_MAJOR=4
# Get the default branch from GitHub API
# We need to make an API call rather than just assume that the current branch is the default
# because this workflow may be triggered by workflow_dispatch on any branch
Expand Down Expand Up @@ -77,7 +90,7 @@ runs:
rm __composer.json
fi
BRANCHES=$(MINIMUM_CMS_MAJOR=$MINIMUM_CMS_MAJOR DEFAULT_BRANCH=$DEFAULT_BRANCH GITHUB_REPOSITORY=$GITHUB_REPOSITORY php ${{ github.action_path }}/branches.php)
BRANCHES=$(DEFAULT_BRANCH=$DEFAULT_BRANCH GITHUB_REPOSITORY=$GITHUB_REPOSITORY php ${{ github.action_path }}/branches.php)
echo "BRANCHES is $BRANCHES"
if [[ $BRANCHES =~ "^FAILURE \- (.+)$" ]]; then
MESSAGE=${BASH_REMATCH[1]}
Expand Down
10 changes: 8 additions & 2 deletions branches.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
<?php

$autoloadPath = __DIR__ . '/vendor/autoload.php';
if (!file_exists($autoloadPath)) {
throw new RuntimeException('Run composer install before this script');
}

require_once $autoloadPath;

include 'funcs.php';

$defaultBranch = getenv('DEFAULT_BRANCH');
$minimumCmsMajor = getenv('MINIMUM_CMS_MAJOR');
$githubRepository = getenv('GITHUB_REPOSITORY');

$branches = branches($defaultBranch, $minimumCmsMajor, $githubRepository);
$branches = branches($defaultBranch, $githubRepository);
echo implode(' ', $branches);
14 changes: 14 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"require-dev": {
"phpunit/phpunit": "^9.6"
},
"require": {
"silverstripe/supported-modules": "dev-pulls/main/combine-all-versions"
},
"repositories": {
"silverstripe/supported-modules": {
"type": "vcs",
"url": "git@github.com:creative-commoners/supported-modules.git"
}
}
}
182 changes: 20 additions & 162 deletions funcs.php
Original file line number Diff line number Diff line change
@@ -1,180 +1,38 @@
<?php

// This should always match default branch of silverstripe/framework
const CURRENT_CMS_MAJOR = 5;
use SilverStripe\SupportedModules\BranchLogic;
use SilverStripe\SupportedModules\MetaData;

// List of major branches to not merge up from
// Add repos in here where the repo was previously unsupported
// Note these are actual major branches, not CMS major versions
const DO_NOT_MERGE_UP_FROM_MAJOR = [
'silverstripe/silverstripe-linkfield' => '3',
];
/**
* The path to the composer.json file - note that the action explicitly downloads the composer.json
* file for the default branch of the repository and saves it to this path, rather than using
* the composer.json file in the "current" branch.
*/
const COMPOSER_JSON_PATH = '__composer.json';

function branches(
string $defaultBranch,
string $minimumCmsMajor,
string $githubRepository,
// The following params are purely for unit testing, for the actual github action it will read json files instead
string $composerJson = '',
string $branchesJson = '',
string $tagsJson = ''
) {
if (!is_numeric($defaultBranch)) {
throw new Exception('Default branch must be a number');
}
if (!ctype_digit($minimumCmsMajor)) {
throw new Exception('Minimum CMS major must be an integer');
}

// work out default major
preg_match('#^([0-9]+)+\.?[0-9]*$#', $defaultBranch, $matches);
$defaultMajor = $matches[1];

// read __composer.json of the current (default) branch
if ($composerJson) {
$contents = $composerJson;
} elseif (file_exists('__composer.json')) {
$contents = file_get_contents('__composer.json');
} else {
// respository such as silverstripe/eslint-config or silverstripe/gha-auto-tag
// make some fake json so that this branch is treated as though it's latest supported major version
$contents = json_encode([
'require' => [
'silverstripe/framework' => '^' . CURRENT_CMS_MAJOR,
],
], JSON_UNESCAPED_SLASHES);
}

$json = json_decode($contents);
if (is_null($json)) {
$lastError = json_last_error();
throw new Exception("Could not parse __composer.json - last error was $lastError");
}
$defaultCmsMajor = '';
$matchedOnBranchThreeLess = false;
$version = '';
if ($githubRepository === 'silverstripe/developer-docs') {
$version = $defaultBranch;
}
if (!$version) {
$version = preg_replace('#[^0-9\.]#', '', $json->require->{'silverstripe/framework'} ?? '');
}
if (!$version) {
$version = preg_replace('#[^0-9\.]#', '', $json->require->{'silverstripe/cms'} ?? '');
}
if (!$version) {
$version = preg_replace('#[^0-9\.]#', '', $json->require->{'silverstripe/mfa'} ?? '');
}
if (!$version) {
$version = preg_replace('#[^0-9\.]#', '', $json->require->{'silverstripe/assets'} ?? '');
if ($version) {
$matchedOnBranchThreeLess = true;
}
}
if (!$version) {
$version = preg_replace('#[^0-9\.]#', '', $json->require->{'cwp/starter-theme'} ?? '');
if ($version) {
$version += 1;
}
}
if (preg_match('#^([0-9]+)+\.?[0-9]*$#', $version, $matches)) {
$defaultCmsMajor = $matches[1];
if ($matchedOnBranchThreeLess) {
$defaultCmsMajor += 3;
): array {
if (file_exists(COMPOSER_JSON_PATH)) {
$contents = file_get_contents(COMPOSER_JSON_PATH);
$composerJson = json_decode($contents);
if (is_null($composerJson)) {
$lastError = json_last_error();
throw new Exception('Could not parse ' . COMPOSER_JSON_PATH . " - last error was $lastError");
}
} else {
$phpVersion = $json->require->{'php'} ?? '';
if (substr($phpVersion,0, 4) === '^7.4') {
$defaultCmsMajor = 4;
} elseif (substr($phpVersion,0, 4) === '^8.1') {
$defaultCmsMajor = 5;
}
}
if ($defaultCmsMajor === '') {
throw new Exception('Could not work out what the default CMS major version this module uses');
}
// work out major diff e.g for silverstripe/admin for CMS 5 => 5 - 2 = 3
$majorDiff = $defaultCmsMajor - $defaultMajor;

$minorsWithStableTags = [];
$contents = $tagsJson ?: file_get_contents('__tags.json');
foreach (json_decode($contents) as $row) {
$tag = $row->name;
if (!preg_match('#^([0-9]+)\.([0-9]+)\.([0-9]+)$#', $tag, $matches)) {
continue;
}
$major = $matches[1];
$minor = $major. '.' . $matches[2];
$minorsWithStableTags[$major][$minor] = true;
}

$branches = [];
$contents = $branchesJson ?: file_get_contents('__branches.json');
foreach (json_decode($contents) as $row) {
$branch = $row->name;
// filter out non-standard branches
if (!preg_match('#^([0-9]+)+\.?[0-9]*$#', $branch, $matches)) {
continue;
}
// filter out majors that are too old
$major = $matches[1];
if (($major + $majorDiff) < $minimumCmsMajor) {
continue;
}
// suffix a temporary .999 minor version to major branches so that it's sorted correctly later
if (preg_match('#^[0-9]+$#', $branch)) {
$branch .= '.999';
}
$branches[] = $branch;
}

// sort so that newest is first
usort($branches, 'version_compare');
$branches = array_reverse($branches);

// remove the temporary .999
array_walk($branches, function(&$branch) {
$branch = preg_replace('#\.999$#', '', $branch);
});

// remove all branches except:
// - the latest major branch in each release line
// - the latest minor branch with a stable tag in each release line
// - any minor branches without stable tags with a higher minor version than the latest minor with a stable tag
$foundMinorInMajor = [];
$foundMinorBranchWithStableTag = [];
foreach ($branches as $i => $branch) {
// only remove minor branches, leave major branches in
if (!preg_match('#^([0-9]+)\.[0-9]+$#', $branch, $matches)) {
continue;
}
$major = $matches[1];
if (isset($foundMinorBranchWithStableTag[$major]) && isset($foundMinorInMajor[$major])) {
unset($branches[$i]);
continue;
}
// for developer-docs which has no tags, pretend that every branch has a tag
if (isset($minorsWithStableTags[$major][$branch]) || $githubRepository === 'silverstripe/developer-docs') {
$foundMinorBranchWithStableTag[$major] = true;
}
$foundMinorInMajor[$major] = true;
}

// remove any branches less than or equal to DO_NOT_MERGE_UP_FROM_MAJOR
if (isset(DO_NOT_MERGE_UP_FROM_MAJOR[$githubRepository])) {
$doNotMergeUpFromMajor = DO_NOT_MERGE_UP_FROM_MAJOR[$githubRepository];
$branches = array_filter($branches, function($branch) use ($doNotMergeUpFromMajor) {
return version_compare($branch, "$doNotMergeUpFromMajor.999999.999999", '>');
});
$composerJson = null;
}

// reverse the array so that oldest is first
$branches = array_reverse($branches);
$repoMetaData = MetaData::getMetaDataForRepository($githubRepository);
$allRepoTags = array_map(fn($x) => $x->name, json_decode(file_get_contents('__tags.json')));
$allRepoBranches = array_map(fn($x) => $x->name, json_decode(file_get_contents('__branches.json')));

$branches = BranchLogic::getBranchesForMergeUp($githubRepository, $repoMetaData, $defaultBranch, $allRepoTags, $allRepoBranches, $composerJson);
// max of 6 branches - also update action.yml if you need to increase this limit
if (count($branches) > 6) {
throw new Exception('More than 6 branches to merge up. Aborting.');
}

return $branches;
}
Loading

0 comments on commit f80c990

Please sign in to comment.