Skip to content

Commit 530eaa9

Browse files
Add automated release workflow & fix failing tests (#113)
--------- Co-authored-by: Tunde Afolabi <Babatundeafolabi@pantheon.io>
1 parent 52f11b3 commit 530eaa9

9 files changed

Lines changed: 201 additions & 34 deletions

File tree

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
## Description
2+
3+
<!-- Describe the change and why it's needed. -->
4+
5+
## Related Issues / Tickets
6+
7+
<!-- Link to any related GitHub issues or internal tickets. -->
8+
9+
## Testing
10+
11+
<!-- Describe how you tested this change. -->
12+
13+
---
14+
15+
## Release Reminder
16+
17+
Releases are gated by the version in `pantheon.php`. Before merging:
18+
19+
| Intent | What to do |
20+
|---|---|
21+
| Normal PR (no release) | Leave version as `X.Y.Z-dev` — no action needed. If this is a minor bump but you're not ready to release, Update the version to the target minor but leave the `-dev` suffix. |
22+
| **Ship a release with this PR** | Remove `-dev` from the version (e.g. `1.5.7-dev``1.5.7`) |
23+
| **Ship a minor version** | Update version to the target minor (e.g. `1.5.7-dev``1.6.0`) |
24+
25+
See [CONTRIBUTING.md](CONTRIBUTING.md) for details.

.github/workflows/release.yml

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
concurrency:
9+
group: release
10+
cancel-in-progress: true
11+
12+
permissions:
13+
contents: write
14+
pull-requests: write
15+
16+
jobs:
17+
release:
18+
name: Tag, Release, and Version Bump
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Checkout
22+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
23+
with:
24+
fetch-depth: 0
25+
26+
- name: Read version from pantheon.php
27+
id: version
28+
run: |
29+
VERSION=$(grep -oP "define\( 'PANTHEON_MU_PLUGIN_VERSION', '\K[^']+" pantheon.php)
30+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
31+
echo "Detected version: $VERSION"
32+
33+
- name: Check whether this is a release version
34+
id: check
35+
run: |
36+
VERSION="${{ steps.version.outputs.version }}"
37+
if [[ "$VERSION" == *-dev ]]; then
38+
echo "is_release=false" >> "$GITHUB_OUTPUT"
39+
echo "Version $VERSION ends in -dev — skipping release."
40+
else
41+
echo "is_release=true" >> "$GITHUB_OUTPUT"
42+
echo "Version $VERSION is a release candidate — proceeding."
43+
fi
44+
45+
- name: Configure git identity
46+
if: steps.check.outputs.is_release == 'true'
47+
run: |
48+
git config user.email "bot@getpantheon.com"
49+
git config user.name "Pantheon Robot"
50+
51+
- name: Create Git tag
52+
if: steps.check.outputs.is_release == 'true'
53+
run: |
54+
VERSION="${{ steps.version.outputs.version }}"
55+
git tag "$VERSION"
56+
git push origin "$VERSION"
57+
58+
- name: Create GitHub Release
59+
if: steps.check.outputs.is_release == 'true'
60+
run: |
61+
VERSION="${{ steps.version.outputs.version }}"
62+
gh release create "$VERSION" \
63+
--generate-notes \
64+
--title "$VERSION"
65+
env:
66+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
67+
68+
- name: Compute next dev version
69+
if: steps.check.outputs.is_release == 'true'
70+
id: next
71+
run: |
72+
VERSION="${{ steps.version.outputs.version }}"
73+
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
74+
NEXT_PATCH=$(( PATCH + 1 ))
75+
NEXT_VERSION="${MAJOR}.${MINOR}.${NEXT_PATCH}-dev"
76+
echo "next_version=$NEXT_VERSION" >> "$GITHUB_OUTPUT"
77+
echo "Next dev version: $NEXT_VERSION"
78+
79+
- name: Bump version in pantheon.php
80+
if: steps.check.outputs.is_release == 'true'
81+
run: |
82+
NEXT="${{ steps.next.outputs.next_version }}"
83+
sed -i "s/^\( \* Version: \).*/\1${NEXT}/" pantheon.php
84+
sed -i "s/\(define( 'PANTHEON_MU_PLUGIN_VERSION', '\)[^']*\(.*\)/\1${NEXT}\2/" pantheon.php
85+
86+
- name: Verify both version strings were updated
87+
if: steps.check.outputs.is_release == 'true'
88+
run: |
89+
NEXT="${{ steps.next.outputs.next_version }}"
90+
COUNT=$(grep -c "$NEXT" pantheon.php)
91+
if [ "$COUNT" -lt 2 ]; then
92+
echo "ERROR: Expected at least 2 occurrences of $NEXT in pantheon.php, found $COUNT"
93+
grep -n "Version\|PANTHEON_MU_PLUGIN_VERSION" pantheon.php
94+
exit 1
95+
fi
96+
echo "Both version strings updated to $NEXT"
97+
grep -n "Version\|PANTHEON_MU_PLUGIN_VERSION" pantheon.php
98+
99+
- name: Open version bump PR
100+
if: steps.check.outputs.is_release == 'true'
101+
run: |
102+
NEXT="${{ steps.next.outputs.next_version }}"
103+
BRANCH="chore/bump-version-${NEXT}"
104+
git checkout -b "$BRANCH"
105+
git add pantheon.php
106+
git commit -m "chore: bump version to ${NEXT}"
107+
git push origin "$BRANCH"
108+
gh pr create \
109+
--title "chore: bump version to ${NEXT}" \
110+
--body "Automated version bump after release. Merges automatically." \
111+
--base main \
112+
--head "$BRANCH"
113+
gh pr merge "$BRANCH" --auto --squash
114+
env:
115+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/test.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
name: Test
22
on:
3-
schedule:
4-
- cron: '0 0 * * *'
53
push:
64
branches:
75
- '**'

CONTRIBUTING.md

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,40 @@ Because the WordPress upstream is only updated when a new WordPress release is c
99

1010
## Release Process
1111

12-
When you are ready for a new `pantheon-mu-plugin` release, before cutting a new release, there are a few steps that need to be taken before you do so.
12+
Releases are automated via GitHub Actions and gated by the version string in `pantheon.php`.
1313

14-
1. Update the version number in `pantheon.php` in the plugin header and the `PANTHEON_MU_PLUGIN_VERSION` constant.
15-
1. If there were any new files that were added to the plugin that should be excluded from the WordPress upstream, add them to the `.gitattributes` file with `export-ignore` and be sure to add them to the `$files_to_delete` array in [`update-tool/src/Update/Filters/CopyMuPlugin.php`](https://github.com/pantheon-systems/update-tool/blob/master/src/Update/Filters/CopyMuPlugin.php).
16-
1. Use the GitHub UI to create a new release. The tag should be the version number only (not prefixed with `v`, e.g. `1.2.1`). Use the GitHub tools to autocomplete the title and body of the release with the changelog. The release should be created from the `main` branch.
14+
### How it works
15+
16+
The version appears in two places in `pantheon.php` that must always match:
17+
18+
1. The plugin header comment: `* Version: 1.5.7-dev`
19+
2. The PHP constant: `define( 'PANTHEON_MU_PLUGIN_VERSION', '1.5.7-dev' );`
20+
21+
When a commit lands on `main`, the release workflow reads the version and applies the following logic:
22+
23+
- **Ends in `-dev`** (e.g. `1.5.7-dev`): do nothing. This is the normal working state.
24+
- **Does not end in `-dev`** (e.g. `1.5.7`): the workflow automatically:
25+
1. Creates a Git tag for that version (no `v` prefix, per existing convention).
26+
2. Publishes a GitHub Release with auto-generated release notes.
27+
3. Increments the patch version and appends `-dev` (e.g. `1.5.7``1.5.8-dev`), updating both occurrences in `pantheon.php`.
28+
4. Opens a PR with the bump and enables auto-merge.
29+
30+
### Shipping a release
31+
32+
Update both version strings in `pantheon.php` on your PR before merging:
33+
34+
| Goal | Change in `pantheon.php` |
35+
|---|---|
36+
| No release (normal PR) | Leave as `X.Y.Z-dev` |
37+
| Patch release | `1.5.7-dev``1.5.7` |
38+
| Minor release | `1.5.7-dev``1.6.0` |
39+
| Major release | `1.5.7-dev``2.0.0` |
40+
41+
After a release, automation sets the working version to the next patch `-dev`.
42+
43+
### Manual steps that remain
44+
45+
If new files were added that should be excluded from the WordPress upstream, add them to `.gitattributes` with `export-ignore`.
1746

1847
## Contributing to the Compatibility Layer
1948

inc/cli.php

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -125,22 +125,9 @@ function set_maintenance_mode_command( $args ) {
125125
}
126126

127127
if ( ! defined( 'WP_HOME' ) || strpos( WP_HOME, 'http://' ) === 0 ) {
128-
add_filter( 'option_home', '\\Pantheon\\CLI\\_pantheon_ep_force_https_url' );
128+
add_filter( 'option_home', '\\Pantheon\\_pantheon_ep_force_https_url' );
129129
}
130130
if ( ! defined( 'WP_SITEURL' ) || strpos( WP_SITEURL, 'http://' ) === 0 ) {
131-
add_filter( 'option_siteurl', '\\Pantheon\\CLI\\_pantheon_ep_force_https_url' );
131+
add_filter( 'option_siteurl', '\\Pantheon\\_pantheon_ep_force_https_url' );
132132
}
133133
} );
134-
135-
/**
136-
* Replace http:// with https:// in a URL string.
137-
*
138-
* @param string $url The option value.
139-
* @return string The URL with https:// scheme.
140-
*/
141-
function _pantheon_ep_force_https_url( $url ) {
142-
if ( is_string( $url ) && strpos( $url, 'http://' ) === 0 ) {
143-
return 'https://' . substr( $url, 7 );
144-
}
145-
return $url;
146-
}

inc/functions.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,16 @@ function _pantheon_enqueue_notice_styles() {
170170
);
171171
}
172172
add_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\_pantheon_enqueue_notice_styles' );
173+
174+
/**
175+
* Replace http:// with https:// at the start of a URL string.
176+
*
177+
* @param mixed $url The option value.
178+
* @return mixed The URL with https:// scheme, or the original value if not an http:// string.
179+
*/
180+
function _pantheon_ep_force_https_url( $url ) {
181+
if ( is_string( $url ) && strpos( $url, 'http://' ) === 0 ) {
182+
return 'https://' . substr( $url, 7 );
183+
}
184+
return $url;
185+
}

pantheon.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
* Plugin Name: Pantheon
44
* Plugin URI: https://pantheon.io/
55
* Description: Building on Pantheon's and WordPress's strengths, together.
6-
* Version: 1.5.4
6+
* Version: 1.5.7-dev
77
* Author: Pantheon
88
* Author URI: https://pantheon.io/
99
*
1010
* @package pantheon
1111
*/
1212

13-
define( 'PANTHEON_MU_PLUGIN_VERSION', '1.5.4' );
13+
define( 'PANTHEON_MU_PLUGIN_VERSION', '1.5.7-dev' );
1414

1515
if ( isset( $_ENV['PANTHEON_ENVIRONMENT'] ) ) {
1616
require_once 'inc/functions.php';

tests/phpunit/test-elasticpress-cli.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public function force_https_url_provider() {
4040
public function test_force_https_url( $input, $expected ) {
4141
$this->assertSame(
4242
$expected,
43-
\Pantheon\CLI\_pantheon_ep_force_https_url( $input )
43+
\Pantheon\_pantheon_ep_force_https_url( $input )
4444
);
4545
}
4646

@@ -74,9 +74,9 @@ public function option_filter_provider() {
7474
*/
7575
public function test_option_filter_forces_https( $option ) {
7676
$filter_name = 'option_' . $option;
77-
add_filter( $filter_name, '\\Pantheon\\CLI\\_pantheon_ep_force_https_url' );
77+
add_filter( $filter_name, '\\Pantheon\\_pantheon_ep_force_https_url' );
7878
update_option( $option, 'http://example.com' );
7979
$this->assertEquals( 'https://example.com', get_option( $option ) );
80-
remove_filter( $filter_name, '\\Pantheon\\CLI\\_pantheon_ep_force_https_url' );
80+
remove_filter( $filter_name, '\\Pantheon\\_pantheon_ep_force_https_url' );
8181
}
8282
}

tests/phpunit/test-pantheon-updates.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ public function test_pantheon_upstream_update_notice_core_latest() {
131131
_pantheon_upstream_update_notice();
132132
$output = ob_get_clean();
133133

134-
$this->assertStringContainsString( 'Check for updates on', $output );
134+
$this->assertStringContainsString( 'Check for Updates', $output );
135135
}
136136

137137
/**
@@ -144,26 +144,26 @@ public function test_pantheon_upstream_update_notice_core_not_latest() {
144144

145145
set_current_screen( 'update-core' );
146146

147-
// Simulate that the core is not the latest version.
147+
// Simulate that the core is not the latest version by using a version higher than any installed WP.
148148
set_site_transient(
149149
'update_core',
150150
(object) [
151151
'updates' => [
152152
(object) [
153-
'current' => '6.3.1',
154-
'response' => 'upgrade',
153+
'current' => '99.0.0',
154+
'response' => 'upgrade',
155155
'locale' => 'en_us',
156156
],
157157
],
158-
'version_checked' => '6.2',
158+
'version_checked' => self::$wp_version,
159159
],
160160
);
161-
161+
162162
ob_start();
163163
_pantheon_upstream_update_notice();
164164
$output = ob_get_clean();
165-
166-
$this->assertStringContainsString( 'Check for updates on <a href="https://dashboard.pantheon.io/sites/test-site">your Pantheon dashboard</a>', $output );
165+
166+
$this->assertStringContainsString( 'A new WordPress update is available!', $output );
167167
}
168168

169169
/**

0 commit comments

Comments
 (0)