Add github workflow to automatically enforce the code freeze.
This commit is contained in:
parent
f3927c786a
commit
757209ba37
|
@ -0,0 +1,35 @@
|
|||
name: "Enforce release code freeze"
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 16 * * 4' # Run at 1600 UTC on Thursdays.
|
||||
|
||||
jobs:
|
||||
maybe-create-next-milestone-and-release-branch:
|
||||
name: "Maybe create next milestone and release branch"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Get the action script"
|
||||
run: |
|
||||
scripts="post-request-shared.php release-code-freeze.php"
|
||||
for script in $scripts
|
||||
do
|
||||
curl \
|
||||
--silent \
|
||||
--fail \
|
||||
--header 'Authorization: bearer ${{ secrets.GITHUB_TOKEN }}' \
|
||||
--header 'User-Agent: GitHub action to enforce release code freeze' \
|
||||
--header 'Accept: application/vnd.github.v3.raw' \
|
||||
--output $script \
|
||||
--location "$GITHUB_API_URL/repos/${{ github.repository }}/contents/.github/workflows/scripts/$script?ref=$GITHUB_REF"
|
||||
done
|
||||
env:
|
||||
GITHUB_API_URL: ${{ env.GITHUB_API_URL }}
|
||||
GITHUB_REF: ${{ env.GITHUB_REF }}
|
||||
- name: "Install PHP"
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '7.4'
|
||||
- name: "Run the script to enforce the code freeze"
|
||||
run: php release-code-freeze.php
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@ -9,73 +9,7 @@
|
|||
|
||||
require_once __DIR__ . '/post-request-shared.php';
|
||||
|
||||
/*
|
||||
* Select the milestone to be added:
|
||||
*
|
||||
* 1. Get the first 10 milestones sorted by creation date descending.
|
||||
* (we'll never have more than 2 or 3 active milestones but let's get 10 to be sure).
|
||||
* 2. Discard those not open or whose title is not a proper version number ("X.Y.Z").
|
||||
* 3. Sort descending using version_compare.
|
||||
* 4. Get the oldest one that does not have a corresponding "release/X.Y" branch.
|
||||
*/
|
||||
|
||||
echo "Getting the list of milestones...\n";
|
||||
|
||||
$query = "
|
||||
repository(owner:\"$repo_owner\", name:\"$repo_name\") {
|
||||
milestones(first: 10, states: [OPEN], orderBy: {field: CREATED_AT, direction: DESC}) {
|
||||
nodes {
|
||||
id
|
||||
title
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
";
|
||||
$json = do_graphql_api_request( $query );
|
||||
$milestones = $json['data']['repository']['milestones']['nodes'];
|
||||
$milestones = array_map(
|
||||
function( $x ) {
|
||||
return 1 === preg_match( '/^\d+\.\d+\.\d+$/D', $x['title'] ) ? $x : null;
|
||||
},
|
||||
$milestones
|
||||
);
|
||||
$milestones = array_filter( $milestones );
|
||||
usort(
|
||||
$milestones,
|
||||
function( $a, $b ) {
|
||||
return version_compare( $b['title'], $a['title'] );
|
||||
}
|
||||
);
|
||||
|
||||
echo 'Latest open milestone: ' . $milestones[0]['title'] . "\n";
|
||||
|
||||
$chosen_milestone = null;
|
||||
foreach ( $milestones as $milestone ) {
|
||||
$milestone_title_parts = explode( '.', $milestone['title'] );
|
||||
$milestone_release_branch = 'release/' . $milestone_title_parts[0] . '.' . $milestone_title_parts[1];
|
||||
|
||||
$query = "
|
||||
repository(owner:\"$repo_owner\", name:\"$repo_name\") {
|
||||
ref(qualifiedName: \"refs/heads/$milestone_release_branch\") {
|
||||
id
|
||||
}
|
||||
}
|
||||
";
|
||||
$result = do_graphql_api_request( $query );
|
||||
|
||||
if ( is_null( $result['data']['repository']['ref'] ) ) {
|
||||
$chosen_milestone = $milestone;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If all the milestones have a release branch, just take the newest one.
|
||||
if ( is_null( $chosen_milestone ) ) {
|
||||
echo "WARNING: No milestone without release branch found, the newest one will be assigned.\n";
|
||||
$chosen_milestone = $milestones[0];
|
||||
}
|
||||
$chosen_milestone = get_latest_milestone_from_api( true );
|
||||
|
||||
echo 'Milestone that will be assigned: ' . $chosen_milestone['title'] . "\n";
|
||||
|
||||
|
|
|
@ -19,9 +19,184 @@ $repo_name = $repo_parts[1];
|
|||
|
||||
$pr_id = getenv( 'PULL_REQUEST_ID' );
|
||||
$github_token = getenv( 'GITHUB_TOKEN' );
|
||||
$github_api_url = getenv( 'GITHUB_API_URL' );
|
||||
$graphql_api_url = getenv( 'GITHUB_GRAPHQL_URL' );
|
||||
|
||||
|
||||
/**
|
||||
* Function to get the latest milestone.
|
||||
*
|
||||
* @param bool $use_latest_when_null When true, the function returns the latest milestone regardless of release branch status.
|
||||
* @return string The title of the latest milestone.
|
||||
*/
|
||||
function get_latest_milestone_from_api( $use_latest_when_null = false ) {
|
||||
global $repo_owner, $repo_name;
|
||||
|
||||
echo "Getting the list of milestones...\n";
|
||||
|
||||
$query = "
|
||||
repository(owner:\"$repo_owner\", name:\"$repo_name\") {
|
||||
milestones(first: 10, states: [OPEN], orderBy: {field: CREATED_AT, direction: DESC}) {
|
||||
nodes {
|
||||
id
|
||||
title
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
";
|
||||
$json = do_graphql_api_request( $query );
|
||||
$milestones = $json['data']['repository']['milestones']['nodes'];
|
||||
$milestones = array_map(
|
||||
function( $x ) {
|
||||
return 1 === preg_match( '/^\d+\.\d+\.\d+$/D', $x['title'] ) ? $x : null;
|
||||
},
|
||||
$milestones
|
||||
);
|
||||
$milestones = array_filter( $milestones );
|
||||
usort(
|
||||
$milestones,
|
||||
function( $a, $b ) {
|
||||
return version_compare( $b['title'], $a['title'] );
|
||||
}
|
||||
);
|
||||
|
||||
echo 'Latest open milestone: ' . $milestones[0]['title'] . "\n";
|
||||
|
||||
$chosen_milestone = null;
|
||||
foreach ( $milestones as $milestone ) {
|
||||
$milestone_title_parts = explode( '.', $milestone['title'] );
|
||||
$milestone_release_branch = 'release/' . $milestone_title_parts[0] . '.' . $milestone_title_parts[1];
|
||||
|
||||
$query = "
|
||||
repository(owner:\"$repo_owner\", name:\"$repo_name\") {
|
||||
ref(qualifiedName: \"refs/heads/$milestone_release_branch\") {
|
||||
id
|
||||
}
|
||||
}
|
||||
";
|
||||
$result = do_graphql_api_request( $query );
|
||||
|
||||
if ( is_null( $result['data']['repository']['ref'] ) ) {
|
||||
$chosen_milestone = $milestone;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If all the milestones have a release branch, just take the newest one.
|
||||
if ( $use_latest_when_null && is_null( $chosen_milestone ) ) {
|
||||
echo "WARNING: No milestone without release branch found, the newest one will be assigned.\n";
|
||||
$chosen_milestone = $milestones[0];
|
||||
}
|
||||
|
||||
return $chosen_milestone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to retreive the sha1 reference for a given branch name.
|
||||
*
|
||||
* @param string $branch The name of the branch.
|
||||
* @return string Returns the name of the branch, or a falsey value on error.
|
||||
*/
|
||||
function get_ref_from_branch( $branch ) {
|
||||
global $repo_owner, $repo_name;
|
||||
$query = "
|
||||
repository(owner:\"$repo_owner\", name:\"$repo_name\") {
|
||||
ref(qualifiedName: \"refs/heads/{$branch}\") {
|
||||
target {
|
||||
... on Commit {
|
||||
history(first: 1) {
|
||||
edges{ node{ oid } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
";
|
||||
$result = do_graphql_api_request( $query );
|
||||
// Warnings suppressed here because traversing this level of arrays with isset / is_array checks would be messy.
|
||||
return @$result['data']['repository']['ref']['target']['history']['edges'][0]['node']['oid'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to create milestone using the GitHub REST API.
|
||||
*
|
||||
* @param string $title The title of the milestone to be created.
|
||||
* @return bool True on success, False otherwise.
|
||||
*/
|
||||
function create_github_milestone( $title ) {
|
||||
global $repo_owner, $repo_name;
|
||||
|
||||
$result = do_github_api_post_request( "/repos/{$repo_owner}/{$repo_name}/milestones", array(
|
||||
'title' => $title,
|
||||
) );
|
||||
return is_array( $result ) && $result['title'] === $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to create branch using the GitHub REST API.
|
||||
*
|
||||
* @param string $branch The branch to be created.
|
||||
* @param string $sha The sha1 reference for the branch.
|
||||
* @return bool True on success, False otherwise.
|
||||
*/
|
||||
function create_github_branch( $branch, $sha ) {
|
||||
global $repo_owner, $repo_name;
|
||||
|
||||
$ref = "refs/heads/{$branch}";
|
||||
$result = do_github_api_post_request( "/repos/{$repo_owner}/{$repo_name}/git/refs", array(
|
||||
'ref' => $ref,
|
||||
'sha' => $sha,
|
||||
) );
|
||||
return is_array( $result ) && $result['ref'] === $ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to create branch using the GitHub REST API from an existing branch.
|
||||
*
|
||||
* @param string $source The branch from which to create.
|
||||
* @param string $target The branch to be created.
|
||||
* @return bool True on success, False otherwise.
|
||||
*/
|
||||
function create_github_branch_from_branch( $source, $target ) {
|
||||
$ref = get_ref_from_branch( $source );
|
||||
if ( ! $ref ) {
|
||||
return false;
|
||||
}
|
||||
return create_github_branch( $target, $ref );
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to do a GitHub API POST Request.
|
||||
*
|
||||
* @param array $request_url
|
||||
* @param array $body The body of the request to be json encoded.
|
||||
* @return mixed The json-decoded response if a response is received, 'false' (or whatever file_get_contents returns) otherwise.
|
||||
*/
|
||||
function do_github_api_post_request( $request_path, $body ) {
|
||||
global $github_token, $github_api_url;
|
||||
|
||||
$context = stream_context_create(
|
||||
array(
|
||||
'http' => array(
|
||||
'method' => 'POST',
|
||||
'header' => array(
|
||||
'Accept: application/vnd.github.v3+json',
|
||||
'Content-Type: application/json',
|
||||
'User-Agent: GitHub Actions for creation of milestones',
|
||||
'Authorization: bearer ' . $github_token,
|
||||
),
|
||||
'content' => json_encode( $body ),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
$full_request_url = rtrim( $github_api_url, '/' ) . '/' . ltrim( $request_path, '/' );
|
||||
$result = file_get_contents( $full_request_url, false, $context );
|
||||
return is_string( $result ) ? json_decode( $result, true ) : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to query the GitHub GraphQL API.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
/**
|
||||
* Script to automatically enforce the release code freeze.
|
||||
*
|
||||
* @package WooCommerce/GithubActions
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/post-request-shared.php';
|
||||
|
||||
$now = time();
|
||||
if ( getenv( 'TIME_OVERRIDE' ) ) {
|
||||
$now = strtotime( getenv( 'TIME_OVERRIDE' ) );
|
||||
}
|
||||
|
||||
// Code freeze comes 26 days prior to release day.
|
||||
$release_time = strtotime( '+26 days', $now );
|
||||
$release_day_of_week = date( 'l', $release_time );
|
||||
$release_day_of_month = (int) date( 'j', $release_time );
|
||||
|
||||
// If 26 days from now isn't the second Tuesday, then it's not code freeze day.
|
||||
if ( 'Tuesday' !== $release_day_of_week || $release_day_of_month < 8 || $release_day_of_month > 14 ) {
|
||||
echo "Info: Today is not the Thursday of the code freeze.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$latest_milestone = get_latest_milestone_from_api();
|
||||
|
||||
if ( is_null( $latest_milestone ) ) {
|
||||
echo "*** Error: Unable to get latest milestone\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$version_parts = explode( '.', $latest_milestone['title'], 3 );
|
||||
$latest_major_minor = "{$version_parts[0]}.{$version_parts[1]}";
|
||||
|
||||
// Because we go from 5.9 to 6.0, we can get the next major_minor by adding 0.1 and formatting appropriately.
|
||||
$latest_float = (float) $latest_major_minor;
|
||||
$next_major_minor = number_format( $latest_float + 0.1, 1 );
|
||||
|
||||
// We use those values to get the release branch and next milestones that we need to create.
|
||||
$release_branch_to_create = "release/{$latest_major_minor}";
|
||||
$milestone_to_create = "{$next_major_minor}.0";
|
||||
|
||||
if ( getenv( 'DRY_RUN' ) ) {
|
||||
echo "DRY RUN: Skipping actual creation of release branch and milestone...\n";
|
||||
echo "Release Branch: {$release_branch_to_create}\n";
|
||||
echo "Milestone: {$milestone_to_create}\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if ( create_github_milestone( $milestone_to_create ) ) {
|
||||
echo "Created milestone {$milestone_to_create}.\n";
|
||||
} else {
|
||||
echo "*** Error: Unable to create {$milestone_to_create} milestone.\n";
|
||||
}
|
||||
|
||||
if ( create_github_branch_from_branch( 'trunk', $release_branch_to_create ) ) {
|
||||
echo "Created branch {$release_branch_to_create}.\n";
|
||||
} else {
|
||||
echo "*** Error: Unable to create {$release_branch_to_create}.\n";
|
||||
}
|
Loading…
Reference in New Issue