Skip to content

Commit

Permalink
Add Github workflow to allow for automatic preview releases (#22367)
Browse files Browse the repository at this point in the history
* Add PHP method to determine next preview version number

* Allow to use preview release branch in tests via an input param

* Allow to fail fast in tests via an input param

* Add preview release workflow

* Use assertEmpty in unit tests to check if there's no preview version

* Apply suggestions from code review

Co-authored-by: Marc Neudert <[email protected]>

* Make tests job conditional on the prepare job outputs

* Fix next preview version generation to correctly bump patch or b/rc if needed

* Adjust release workflow to be able to be reused

* Reuse release action to create preview releases

* Tidy up for consistency

* Fix name reference

* Add a check whether previous non-stable version has been released

* Skip release checks for -alpha as that won't ever be released

* Fix output variable name

* Tweak conditional logic

* Fix regex by adding the missing delimiter

* Use regex assert method

* Fix typo in test value

* Adjust release preview to require password and verify user

* Set ENV variable and remove redundent check

---------

Co-authored-by: Marc Neudert <[email protected]>
Co-authored-by: caddoo <[email protected]>
  • Loading branch information
3 people authored Aug 13, 2024
1 parent f8315a8 commit fac18d8
Show file tree
Hide file tree
Showing 5 changed files with 305 additions and 11 deletions.
14 changes: 12 additions & 2 deletions .github/workflows/matomo-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ on:
- '**.x-dev'
- 'next_release'
workflow_dispatch:
workflow_call:
inputs:
is_preview:
type: boolean
required: false
default: false

permissions:
actions: read
Expand All @@ -34,7 +40,7 @@ jobs:
PHP:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
fail-fast: ${{ inputs.is_preview == true }}
matrix:
type: [ 'UnitTests', 'SystemTestsPlugins', 'SystemTestsCore', 'IntegrationTestsCore', 'IntegrationTestsPlugins' ]
php: [ '7.2', '8.2', '8.3' ]
Expand All @@ -60,6 +66,7 @@ jobs:
persist-credentials: false
submodules: true
path: matomo
ref: ${{ inputs.is_preview == true && '5.x-preview' || github.ref }}
- name: running tests
uses: matomo-org/github-action-tests@main
with:
Expand All @@ -81,6 +88,7 @@ jobs:
persist-credentials: false
submodules: true
path: matomo
ref: ${{ inputs.is_preview == true && '5.x-preview' || github.ref }}
- name: running tests
uses: matomo-org/github-action-tests@main
with:
Expand All @@ -97,6 +105,7 @@ jobs:
persist-credentials: false
submodules: true
path: matomo
ref: ${{ inputs.is_preview == true && '5.x-preview' || github.ref }}
- name: running tests
uses: matomo-org/github-action-tests@main
with:
Expand All @@ -106,7 +115,7 @@ jobs:
UI:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
fail-fast: ${{ inputs.is_preview == true }}
matrix:
parts: [ 0,1,2,3 ]
steps:
Expand All @@ -116,6 +125,7 @@ jobs:
persist-credentials: false
submodules: true
path: matomo
ref: ${{ inputs.is_preview == true && '5.x-preview' || github.ref }}
- name: running tests
uses: matomo-org/github-action-tests@main
with:
Expand Down
169 changes: 169 additions & 0 deletions .github/workflows/release-preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# Matomo release action for automated PREVIEW releases
#
# Required GitHub secrets:
#
# GPG_CERTIFICATE | ASCII armored or Base64 encoded GPG certificate that is used to create the signatures for the archives
# GPG_CERTIFICATE_PASS | Passphrase of the GPG key

name: Build preview release

permissions:
actions: read # required for the tests job
checks: none
contents: write # required to create tag and release
deployments: none
issues: read # required for the tests job
packages: none
pull-requests: read # required for the tests jobs
repository-projects: none
security-events: none
statuses: none

on:
# TODO: remove manual dispatch after testing and enable cron
workflow_dispatch:
branches:
- 5.x-dev
inputs:
password:
description: 'Release password'
required: true
#schedule:
# - cron: '0 1 * * *' # 1am daily
env:
RELEASE_PASSWORD: ${{ secrets.RELEASE_PASSWORD }}
jobs:
prepare_preview_version:
runs-on: ubuntu-latest
outputs:
do_release: ${{ steps.changes.outputs.do_release }}
has_new_version: ${{ steps.version.outputs.has_new_version }}
steps:
- name: "Check release password"
if: ${{ github.event.inputs.password != env.RELEASE_PASSWORD }}
uses: actions/github-script@v6
with:
script: |
core.setFailed('Release password didn\'t match.')
- name: "Check if user is allowed"
if: ${{ github.actor != 'mattab' && github.actor != 'tsteur' && github.actor != 'sgiehl' && github.actor != 'mneudert' && github.actor != 'michalkleiner' && github.actor != 'caddoo'}}
uses: actions/github-script@v6
with:
script: |
core.setFailed('User is not allowed to release.')
- uses: actions/checkout@v4
with:
lfs: false
fetch-tags: true
fetch-depth: 0

- name: Prepare git config
run: |
cat <<- EOF > $HOME/.netrc
machine github.com
login $GITHUB_ACTOR
password $GITHUB_TOKEN
machine api.github.com
login $GITHUB_ACTOR
password $GITHUB_TOKEN
EOF
chmod 600 $HOME/.netrc
git config --global user.email "[email protected]"
git config --global user.name "$GITHUB_ACTOR"
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Check if there are any changes to create a preview release for
id: changes
run: |
LATEST_PREVIEW=$(git tag --sort=-creatordate | grep -E '\.[0-9]{14}$' | head -n 1)
DIFF=""
if [ -n "$LATEST_PREVIEW" ]; then
# using || true to always exit either with a diff or a success exit code to not fail the whole workflow
DIFF=$(git diff $LATEST_PREVIEW..5.x-dev --unified=0 | grep -vE "^\+\+\+|---" | grep "^[+-]" | grep -v "public const VERSION = '.*';" || true)
fi
if [ -z "$DIFF" ]; then
echo "No changes in 5.x-dev since last preview version was created."
DO_RELEASE=0
else
DO_RELEASE=1
fi
echo "do_release=$DO_RELEASE" >> $GITHUB_OUTPUT
- name: Determine new preview version number
id: version
if: steps.changes.outputs.do_release == '1'
run: |
OLD_VERSION=$(php -r "include_once 'core/Version.php'; echo \Piwik\Version::VERSION;")
NEW_VERSION=$(php -r "include_once 'core/Version.php'; \$v = new \Piwik\Version(); echo \$v->nextPreviewVersion(\Piwik\Version::VERSION);")
if [ "$NEW_VERSION" == "" ]; then
HAS_NEW_VERSION=0
else
HAS_NEW_VERSION=1
fi
echo "OLD_VERSION=$OLD_VERSION" >> $GITHUB_ENV
echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV
echo "has_new_version=$HAS_NEW_VERSION" >> $GITHUB_OUTPUT
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
- name: Check if the previous version has been released
if: steps.changes.outputs.do_release == '1' && steps.version.outputs.has_new_version == '1'
run: |
TAG_EXISTS=$( git tag --list "$OLD_VERSION" )
# x.y.z-alpha would not be released, all other versions should have an existing tag (a release)
if [[ ! $OLD_VERSION =~ -alpha$ ]] && [[ -z "$TAG_EXISTS" ]]; then
echo "$OLD_VERSION (as indicated in core/Version.php) has not been released yet."
exit 1
fi
- name: Update 5.x-preview branch to latest 5.x-dev
if: steps.changes.outputs.do_release == '1' && steps.version.outputs.has_new_version == '1'
run: |
git checkout -B 5.x-preview
- name: Update version file with new version
if: steps.changes.outputs.do_release == '1' && steps.version.outputs.has_new_version == '1'
run: |
sed -i "s/VERSION = '${OLD_VERSION}';/VERSION = '${NEW_VERSION}';/g" core/Version.php
- name: Commit version file changes
if: steps.changes.outputs.do_release == '1' && steps.version.outputs.has_new_version == '1'
run: |
git add core/Version.php
git commit -m "Update version to ${NEW_VERSION}"
- name: Push changes to 5.x-preview
if: steps.changes.outputs.do_release == '1' && steps.version.outputs.has_new_version == '1'
run: |
git push -f origin 5.x-preview
run_matomo_tests:
needs: [prepare_preview_version]
uses: ./.github/workflows/matomo-tests.yml
if: |
always() &&
needs.prepare_preview_version.result == 'success' &&
needs.prepare_preview_version.outputs.do_release == '1' &&
needs.prepare_preview_version.outputs.has_new_version == '1'
with:
is_preview: true

release_preview_version:
needs: [run_matomo_tests]
uses: ./.github/workflows/release.yml
if: |
always() &&
needs.prepare_preview_version.result == 'success' &&
needs.run_matomo_tests.result == 'success' &&
needs.prepare_preview_version.outputs.do_release == '1' &&
needs.prepare_preview_version.outputs.has_new_version == '1'
with:
is_preview: true
22 changes: 17 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ on:
password:
description: 'Release password'
required: true
workflow_call:
inputs:
is_preview:
type: boolean
required: false
default: false

env:
RELEASE_PASSWORD: ${{ secrets.RELEASE_PASSWORD }}
Expand All @@ -39,13 +45,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: "Check release password"
if: ${{ github.event.inputs.password != env.RELEASE_PASSWORD }}
if: ${{ inputs.is_preview != true && github.event.inputs.password != env.RELEASE_PASSWORD }}
uses: actions/github-script@v6
with:
script: |
core.setFailed('Release password didn\'t match.')
- name: "Check if user is allowed"
if: ${{ github.actor != 'mattab' && github.actor != 'tsteur' && github.actor != 'sgiehl' && github.actor != 'mneudert' && github.actor != 'michalkleiner' && github.actor != 'caddoo'}}
if: ${{ inputs.is_preview != true && github.actor != 'mattab' && github.actor != 'tsteur' && github.actor != 'sgiehl' && github.actor != 'mneudert' && github.actor != 'michalkleiner' && github.actor != 'caddoo'}}
uses: actions/github-script@v6
with:
script: |
Expand Down Expand Up @@ -97,15 +103,21 @@ jobs:
exit 1
fi
if ! [[ ${GITHUB_REF#refs/heads/} =~ ^[4-9]\.x-dev$ || ${GITHUB_REF#refs/heads/} == "next_release" ]]
if ! [[ ${GITHUB_REF#refs/heads/} =~ ^[4-9]\.x-(dev|preview)$ || ${GITHUB_REF#refs/heads/} == "next_release" ]]
then
echo "A tag can only be created from branches '5.x-dev' and 'next_release'. Please create the tag manually if a release needs to be built from another branch."
echo "A tag can only be created from branches '5.x-dev', '5.x-preview' or 'next_release'. Please create the tag manually if a release needs to be built from another branch."
exit 1
fi
if [[ ${GITHUB_REF#refs/heads/} =~ ^[4-9]\.x-dev$ && $version =~ ^[0-9]+\.[0-9]+\.[0-9]+(-rc[0-9]+)?$ ]]
then
echo "Only beta/preview release tags can be created from ${GITHUB_REF#refs/heads/} branch."
echo "Only beta release tags can be created from ${GITHUB_REF#refs/heads/} branch."
exit 1
fi
if [[ ${GITHUB_REF#refs/heads/} =~ ^[4-9]\.x-preview$ && $version =~ [0-9]{14}$ ]]
then
echo "Only preview release tags can be created from ${GITHUB_REF#refs/heads/} branch."
exit 1
fi
Expand Down
49 changes: 47 additions & 2 deletions core/Version.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ public function isVersionNumber($version): bool

private function isNonStableVersion($version): bool
{
return (bool) preg_match('/^\d+\.\d+\.\d+((-.{1,4}\d+(\.\d{14})?)|(-alpha\.\d{14}))$/i', $version);
return (bool) preg_match('/^\d+\.\d+\.\d+(-((rc|b|beta)\d+|alpha)(\.\d{14})?)$/i', $version);
}

public function isPreviewVersion($version): bool
{
if (\preg_match('/^\d+\.\d+\.\d+((-(rc|b|beta)\d+(\.\d{14})?)|(-alpha\.\d{14}))?$/i', $version)) {
if ($this->isNonStableVersion($version)) {
if (\preg_match('/\.(\d{14})$/', $version, $matches)) {
$dt = DateTime::createFromFormat('YmdHis', $matches[1]);

Expand All @@ -56,4 +56,49 @@ public function isPreviewVersion($version): bool

return false;
}

public function nextPreviewVersion($version): string
{
if (!$this->isVersionNumber($version)) {
return '';
}

$dt = date('YmdHis');

if ($this->isPreviewVersion($version)) {
// already a preview, update dt and check it's newer
$newVersion = substr($version, 0, -14) . $dt;
if (version_compare($version, $newVersion, '<')) {
return $newVersion;
}
return '';
} elseif ($this->isStableVersion($version)) {
// no suffix yet, we need to bump the patch first
$newVersion = preg_replace_callback(
'/^(\d+\.\d+\.)(\d+)$/',
function ($matches) {
$matches[2] = $matches[2] + 1;
return $matches[1] . $matches[2];
},
$version
);

return sprintf('%s-alpha.%s', $newVersion, $dt);
} elseif ('alpha' === substr($version, -5)) {
// -alpha
return $version . '.' . $dt;
} else {
// -b1, -rc1
$newVersion = preg_replace_callback(
'/^(\d+\.\d+\.\d+-(?:rc|b|beta))(\d+)$/i',
function ($matches) {
$matches[2] = $matches[2] + 1;
return $matches[1] . $matches[2];
},
$version
);

return $newVersion . '.' . $dt;
}
}
}
Loading

0 comments on commit fac18d8

Please sign in to comment.