From c088dd7bacbef507dcc03419c81fbb81406799b7 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Tue, 22 Apr 2025 15:03:16 -0600 Subject: [PATCH 01/61] allow pretty permalinks to be handled for rest endpoints by default bypass the issue with bad string replacement by just parsing pretty permalinks natively for rest endpoints even when pretty permalinks aren't active --- web/app/mu-plugins/filters.php | 148 ++++++++++++++++++++++++++++++++- 1 file changed, 147 insertions(+), 1 deletion(-) diff --git a/web/app/mu-plugins/filters.php b/web/app/mu-plugins/filters.php index df9d0984..75a66320 100644 --- a/web/app/mu-plugins/filters.php +++ b/web/app/mu-plugins/filters.php @@ -3,7 +3,7 @@ * Plugin Name: Pantheon WordPress Filters * Plugin URI: https://github.com/pantheon-systems/wordpress-composer-managed * Description: Filters for Composer-managed WordPress sites on Pantheon. - * Version: 1.2.2 + * Version: 1.2.3 * Author: Pantheon Systems * Author URI: https://pantheon.io/ * License: MIT License @@ -235,3 +235,149 @@ function __rebuild_url_from_parts( array $parts ) : string { ( isset( $parts['fragment'] ) ? str_replace( '/', '', "#{$parts['fragment']}" ) : '' ) ); } + +/** + * REST API Plain Permalink Fix + * + * Extracts the REST API endpoint from a potentially malformed path. + * Handles cases like /wp-json/v2/posts or /wp-json/wp/v2/posts. + * + * @since 1.2.3 + * @param string $path The URL path component. + * @return string The extracted endpoint (e.g., /v2/posts) or '/'. + */ +function __extract_rest_endpoint( string $path ) : string { + $rest_route = '/'; // Default to base route + $wp_json_pos = strpos( $path, '/wp-json/' ); + + if ( $wp_json_pos !== false ) { + $extracted_route = substr( $path, $wp_json_pos + strlen( '/wp-json' ) ); // Get everything after /wp-json + // Special case: Handle the originally reported '/wp-json/wp/' malformation + if ( strpos( $extracted_route, 'wp/' ) === 0 ) { + $extracted_route = substr( $extracted_route, strlen( 'wp' ) ); // Remove the extra 'wp' + } + // Ensure the extracted route starts with a slash + if ( ! $extracted_route && $extracted_route[0] !== '/' ) { + $extracted_route = '/' . $extracted_route; + } + $rest_route = $extracted_route ?: '/'; // Use extracted route or default to base + } + return $rest_route; +} + +/** + * Builds the correct plain permalink REST URL. + * + * @since 1.2.3 + * @param string $endpoint The REST endpoint (e.g., /v2/posts). + * @param string|null $query_str The original query string (or null). + * @param string|null $fragment The original fragment (or null). + * @return string The fully constructed plain permalink REST URL. + */ +function __build_plain_rest_url( string $endpoint, ?string $query_str, ?string $fragment ) : string { + $home_url = home_url(); // Should be https://.../wp + // Ensure endpoint starts with / + $endpoint = '/' . ltrim( $endpoint, '/' ); + // Construct the base plain permalink URL + $correct_url = rtrim( $home_url, '/' ) . '/?rest_route=' . $endpoint; + + // Append original query parameters (if any, besides rest_route) + if ( !empty( $query_str ) ) { + parse_str( $query_str, $query_params ); + unset( $query_params['rest_route'] ); // Ensure no leftover rest_route + if ( ! empty( $query_params ) ) { + // Check if $correct_url already has '?' (it should) + $correct_url .= '&' . http_build_query( $query_params ); + } + } + // Append fragment if present + if ( !empty( $fragment ) ) { + $correct_url .= '#' . $fragment; + } + + // Use normalization helper if available + if ( function_exists( __NAMESPACE__ . '\\__normalize_wp_url' ) ) { + return __normalize_wp_url( $correct_url ); + } else { + return $correct_url; // Return without full normalization as fallback + } +} + +/** + * Corrects generated REST API URL when plain permalinks are active but WordPress + * incorrectly generates a pretty-permalink-style path. Forces the URL + * back to the expected ?rest_route= format using helpers. + * + * @since 1.2.3 // TODO: Update version before release + * @param string $url The potentially incorrect REST URL generated by WP. + * @return string The corrected REST URL in plain permalink format. + */ +function filter_force_plain_rest_url_format( string $url ) : string { + $parsed_url = parse_url($url); + + // Check if it looks like a pretty permalink URL (has /wp-json/ in path) + // AND lacks the ?rest_route= query parameter. + $has_wp_json_path = isset( $parsed_url['path'] ) && strpos( $parsed_url['path'], '/wp-json/' ) !== false; + $has_rest_route_query = isset( $parsed_url['query'] ) && strpos( $parsed_url['query'], 'rest_route=' ) !== false; + + if ( $has_wp_json_path && ! $has_rest_route_query ) { + // It's using a pretty path format when it shouldn't be. + $endpoint = __extract_rest_endpoint( $parsed_url['path'] ); + return __build_plain_rest_url( $endpoint, $parsed_url['query'] ?? null, $parsed_url['fragment'] ?? null ); + } + + // If the URL didn't match the problematic pattern, return it normalized. + return __normalize_wp_url($url); +} + +/** + * Handles incoming requests using a pretty REST API path format when plain + * permalinks are active. It sets the correct 'rest_route' query variable + * internally instead of performing an external redirect. + * + * @since 1.2.3 + * @param \WP $wp The WP object, passed by reference. + */ +function handle_pretty_rest_request_on_plain_permalinks( \WP &$wp ) { + // Only run if it's not an admin request. Permalink structure checked by the hook caller. + if ( is_admin() ) { + return; + } + + // Use REQUEST_URI as it's more reliable for the raw request path before WP parsing. + $request_uri = $_SERVER['REQUEST_URI'] ?? ''; + // Get the path part before any query string. + $request_path = strtok($request_uri, '?'); + + // Define the pretty permalink base path we expect if pretty permalinks *were* active. + $home_url_path = rtrim( parse_url( home_url(), PHP_URL_PATH ) ?: '', '/' ); // e.g., /wp + $pretty_rest_path_base = $home_url_path . '/wp-json/'; // e.g., /wp/wp-json/ + + // Check if the actual request path starts with this pretty base. + if ( strpos( $request_path, $pretty_rest_path_base ) === 0 ) { + // Extract the endpoint part *after* the base. + $endpoint = substr( $request_path, strlen( $pretty_rest_path_base ) ); + // Ensure endpoint starts with a slash, default to base if empty. + $endpoint = '/' . ltrim($endpoint, '/'); + // If the result is just '/', set it back to empty string for root endpoint ?rest_route=/ + $endpoint = ($endpoint === '/') ? '' : $endpoint; + + // Check if rest_route is already set (e.g., from query string), if so, don't overwrite. + // This prevents conflicts if someone manually crafts a URL like /wp/wp-json/posts?rest_route=/users + if ( ! isset( $wp->query_vars['rest_route'] ) ) { + // Directly set the query variable for the REST API. + $wp->query_vars['rest_route'] = $endpoint; + + // Optional: Unset other query vars WP might have incorrectly parsed. + // unset($wp->query_vars['pagename'], $wp->query_vars['error']); + } + // No redirect, no exit. Let WP continue processing with the modified query vars. + } +} + +// Only add the REST URL *generation* fix and the request handler if plain permalinks are enabled. +if ( ! get_option('permalink_structure') ) { + add_filter('rest_url', __NAMESPACE__ . '\\filter_force_plain_rest_url_format', 10, 1); + // Hook the request handling logic to parse_request. Pass the $wp object by reference. + add_action('parse_request', __NAMESPACE__ . '\\handle_pretty_rest_request_on_plain_permalinks', 1, 1); +} From 710b50c64f1232c28cb6cf4811b22a68fa58068b Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Tue, 22 Apr 2025 15:03:16 -0600 Subject: [PATCH 02/61] DEVREL-29: handle pretty permalinks for rest endpoints by default (#182) bypass the issue with bad string replacement by just parsing pretty permalinks natively for rest endpoints even when pretty permalinks aren't active --- web/app/mu-plugins/filters.php | 148 ++++++++++++++++++++++++++++++++- 1 file changed, 147 insertions(+), 1 deletion(-) diff --git a/web/app/mu-plugins/filters.php b/web/app/mu-plugins/filters.php index df9d0984..75a66320 100644 --- a/web/app/mu-plugins/filters.php +++ b/web/app/mu-plugins/filters.php @@ -3,7 +3,7 @@ * Plugin Name: Pantheon WordPress Filters * Plugin URI: https://github.com/pantheon-systems/wordpress-composer-managed * Description: Filters for Composer-managed WordPress sites on Pantheon. - * Version: 1.2.2 + * Version: 1.2.3 * Author: Pantheon Systems * Author URI: https://pantheon.io/ * License: MIT License @@ -235,3 +235,149 @@ function __rebuild_url_from_parts( array $parts ) : string { ( isset( $parts['fragment'] ) ? str_replace( '/', '', "#{$parts['fragment']}" ) : '' ) ); } + +/** + * REST API Plain Permalink Fix + * + * Extracts the REST API endpoint from a potentially malformed path. + * Handles cases like /wp-json/v2/posts or /wp-json/wp/v2/posts. + * + * @since 1.2.3 + * @param string $path The URL path component. + * @return string The extracted endpoint (e.g., /v2/posts) or '/'. + */ +function __extract_rest_endpoint( string $path ) : string { + $rest_route = '/'; // Default to base route + $wp_json_pos = strpos( $path, '/wp-json/' ); + + if ( $wp_json_pos !== false ) { + $extracted_route = substr( $path, $wp_json_pos + strlen( '/wp-json' ) ); // Get everything after /wp-json + // Special case: Handle the originally reported '/wp-json/wp/' malformation + if ( strpos( $extracted_route, 'wp/' ) === 0 ) { + $extracted_route = substr( $extracted_route, strlen( 'wp' ) ); // Remove the extra 'wp' + } + // Ensure the extracted route starts with a slash + if ( ! $extracted_route && $extracted_route[0] !== '/' ) { + $extracted_route = '/' . $extracted_route; + } + $rest_route = $extracted_route ?: '/'; // Use extracted route or default to base + } + return $rest_route; +} + +/** + * Builds the correct plain permalink REST URL. + * + * @since 1.2.3 + * @param string $endpoint The REST endpoint (e.g., /v2/posts). + * @param string|null $query_str The original query string (or null). + * @param string|null $fragment The original fragment (or null). + * @return string The fully constructed plain permalink REST URL. + */ +function __build_plain_rest_url( string $endpoint, ?string $query_str, ?string $fragment ) : string { + $home_url = home_url(); // Should be https://.../wp + // Ensure endpoint starts with / + $endpoint = '/' . ltrim( $endpoint, '/' ); + // Construct the base plain permalink URL + $correct_url = rtrim( $home_url, '/' ) . '/?rest_route=' . $endpoint; + + // Append original query parameters (if any, besides rest_route) + if ( !empty( $query_str ) ) { + parse_str( $query_str, $query_params ); + unset( $query_params['rest_route'] ); // Ensure no leftover rest_route + if ( ! empty( $query_params ) ) { + // Check if $correct_url already has '?' (it should) + $correct_url .= '&' . http_build_query( $query_params ); + } + } + // Append fragment if present + if ( !empty( $fragment ) ) { + $correct_url .= '#' . $fragment; + } + + // Use normalization helper if available + if ( function_exists( __NAMESPACE__ . '\\__normalize_wp_url' ) ) { + return __normalize_wp_url( $correct_url ); + } else { + return $correct_url; // Return without full normalization as fallback + } +} + +/** + * Corrects generated REST API URL when plain permalinks are active but WordPress + * incorrectly generates a pretty-permalink-style path. Forces the URL + * back to the expected ?rest_route= format using helpers. + * + * @since 1.2.3 // TODO: Update version before release + * @param string $url The potentially incorrect REST URL generated by WP. + * @return string The corrected REST URL in plain permalink format. + */ +function filter_force_plain_rest_url_format( string $url ) : string { + $parsed_url = parse_url($url); + + // Check if it looks like a pretty permalink URL (has /wp-json/ in path) + // AND lacks the ?rest_route= query parameter. + $has_wp_json_path = isset( $parsed_url['path'] ) && strpos( $parsed_url['path'], '/wp-json/' ) !== false; + $has_rest_route_query = isset( $parsed_url['query'] ) && strpos( $parsed_url['query'], 'rest_route=' ) !== false; + + if ( $has_wp_json_path && ! $has_rest_route_query ) { + // It's using a pretty path format when it shouldn't be. + $endpoint = __extract_rest_endpoint( $parsed_url['path'] ); + return __build_plain_rest_url( $endpoint, $parsed_url['query'] ?? null, $parsed_url['fragment'] ?? null ); + } + + // If the URL didn't match the problematic pattern, return it normalized. + return __normalize_wp_url($url); +} + +/** + * Handles incoming requests using a pretty REST API path format when plain + * permalinks are active. It sets the correct 'rest_route' query variable + * internally instead of performing an external redirect. + * + * @since 1.2.3 + * @param \WP $wp The WP object, passed by reference. + */ +function handle_pretty_rest_request_on_plain_permalinks( \WP &$wp ) { + // Only run if it's not an admin request. Permalink structure checked by the hook caller. + if ( is_admin() ) { + return; + } + + // Use REQUEST_URI as it's more reliable for the raw request path before WP parsing. + $request_uri = $_SERVER['REQUEST_URI'] ?? ''; + // Get the path part before any query string. + $request_path = strtok($request_uri, '?'); + + // Define the pretty permalink base path we expect if pretty permalinks *were* active. + $home_url_path = rtrim( parse_url( home_url(), PHP_URL_PATH ) ?: '', '/' ); // e.g., /wp + $pretty_rest_path_base = $home_url_path . '/wp-json/'; // e.g., /wp/wp-json/ + + // Check if the actual request path starts with this pretty base. + if ( strpos( $request_path, $pretty_rest_path_base ) === 0 ) { + // Extract the endpoint part *after* the base. + $endpoint = substr( $request_path, strlen( $pretty_rest_path_base ) ); + // Ensure endpoint starts with a slash, default to base if empty. + $endpoint = '/' . ltrim($endpoint, '/'); + // If the result is just '/', set it back to empty string for root endpoint ?rest_route=/ + $endpoint = ($endpoint === '/') ? '' : $endpoint; + + // Check if rest_route is already set (e.g., from query string), if so, don't overwrite. + // This prevents conflicts if someone manually crafts a URL like /wp/wp-json/posts?rest_route=/users + if ( ! isset( $wp->query_vars['rest_route'] ) ) { + // Directly set the query variable for the REST API. + $wp->query_vars['rest_route'] = $endpoint; + + // Optional: Unset other query vars WP might have incorrectly parsed. + // unset($wp->query_vars['pagename'], $wp->query_vars['error']); + } + // No redirect, no exit. Let WP continue processing with the modified query vars. + } +} + +// Only add the REST URL *generation* fix and the request handler if plain permalinks are enabled. +if ( ! get_option('permalink_structure') ) { + add_filter('rest_url', __NAMESPACE__ . '\\filter_force_plain_rest_url_format', 10, 1); + // Hook the request handling logic to parse_request. Pass the $wp object by reference. + add_action('parse_request', __NAMESPACE__ . '\\handle_pretty_rest_request_on_plain_permalinks', 1, 1); +} From 05feec79a9272941521001c2d9af7cf2dc2445fa Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Tue, 22 Apr 2025 15:31:11 -0600 Subject: [PATCH 03/61] DEVREL-29: Update tests for handling plain permalinks (#182) --- .github/tests/2-rest-url-fix.bats | 98 +++++++++++++++++++ .github/workflows/ci.yml | 2 +- .../workflows/{playwright.yml => test.yml} | 58 ++++++++++- 3 files changed, 153 insertions(+), 5 deletions(-) create mode 100644 .github/tests/2-rest-url-fix.bats rename .github/workflows/{playwright.yml => test.yml} (88%) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats new file mode 100644 index 00000000..cb3a5e3f --- /dev/null +++ b/.github/tests/2-rest-url-fix.bats @@ -0,0 +1,98 @@ +#!/usr/bin/env bats + +# wp wrapper function +_wp() { + terminus wp -- ${SITE_ID}.dev "$@" +} + +# Helper function to get REST URL via WP-CLI +get_rest_url() { + _wp eval 'echo get_rest_url();' +} + +# Helper function to get home_url path via WP-CLI +get_home_url_path() { + _wp eval 'echo rtrim(parse_url(home_url(), PHP_URL_PATH) ?: "", "/");' +} + +setup_suite() { + # Ensure WP is installed and we are in the right directory + _wp core is-installed || (echo "WordPress not installed. Run setup script first." && exit 1) +} + +@test "Check REST URL with default (pretty) permalinks (after setup script flush)" { + run get_rest_url + assert_success + # Default setup script sets /%postname%/ and flushes. + # Expecting /wp/wp-json/ because home_url path should be /wp + assert_output --partial "/wp/wp-json/" +} + +@test "Check REST URL with plain permalinks" { + # Set plain permalinks and flush + _wp option update permalink_structure '' --quiet + _wp rewrite flush --hard --quiet + run get_rest_url + assert_success + # With plain permalinks, expect ?rest_route= based on home_url + # Check if it contains the problematic /wp-json/wp/ segment (it shouldn't) + refute_output --partial "/wp-json/wp/" + # Check if it contains the expected ?rest_route= + assert_output --partial "?rest_route=/" + + # Restore pretty permalinks for subsequent tests + _wp option update permalink_structure '/%postname%/' --quiet + _wp rewrite flush --hard --quiet +} + +@test "Check REST URL with pretty permalinks *before* flush (Simulates new site)" { + # Set pretty permalinks *without* flushing + _wp option update permalink_structure '/%postname%/' --quiet + # DO NOT FLUSH HERE + + # Check home_url path to confirm /wp setup + run get_home_url_path + assert_success + assert_output "/wp" + + # Now check get_rest_url() - this is where the original issue might occur + run get_rest_url + assert_success + # Assert that the output *should* be the correct /wp/wp-json/ even before flush, + # assuming the fix (either integrated or separate filter) is in place. + # If the fix is NOT in place, this might output /wp-json/ and fail. + # If the plain permalink fix was active, it might output /wp/wp-json/wp/ and fail. + assert_output --partial "/wp/wp-json/" + refute_output --partial "/wp-json/wp/" # Ensure the bad structure isn't present + + # Clean up: Flush permalinks + _wp rewrite flush --hard --quiet +} + +@test "Access pretty REST API path directly with plain permalinks active" { + # Set plain permalinks and flush + _wp option update permalink_structure '' --quiet + _wp rewrite flush --hard --quiet + + # Get the full home URL to construct the test URL + SITE_URL=$( _wp option get home ) + # Construct the pretty-style REST API URL + # Note: home_url() includes /wp, so we append /wp-json/... directly + TEST_URL="${SITE_URL}/wp-json/wp/v2/posts" + + # Make a curl request to the pretty URL + # -s: silent, -o /dev/null: discard body, -w '%{http_code}': output only HTTP code + # -L: follow redirects (we expect NO redirect, so this helps verify) + # We expect a 200 OK if the internal handling works, or maybe 404 if not found, + # but crucially NOT a 301/302 redirect. + run curl -s -o /dev/null -w '%{http_code}' -L "${TEST_URL}" + assert_success + # Assert that the final HTTP status code is 200 (OK) + # If it were redirecting, -L would follow, but the *initial* code wouldn't be 200. + # If the internal handling fails, it might be 404 or other error. + assert_output "200" + + # Restore pretty permalinks for subsequent tests + _wp option update permalink_structure '/%postname%/' --quiet + _wp rewrite flush --hard --quiet +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 77eb6278..5f53834e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,7 +77,7 @@ jobs: env: CI: 1 run: | - bats -p -t .github/tests + bats -p -t .github/tests/1-test-update-php.bats - name: Create failure status artifact if: failure() diff --git a/.github/workflows/playwright.yml b/.github/workflows/test.yml similarity index 88% rename from .github/workflows/playwright.yml rename to .github/workflows/test.yml index 33793be1..5a7b306d 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: WordPress Composer Playwright Tests +name: WordPress Composer Tests on: pull_request: paths-ignore: @@ -29,13 +29,16 @@ permissions: jobs: - playwright-single: + test-single: name: Single site runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Install Bats + uses: bats-core/bats-action@2.0.0 + - name: Wait for status artifacts env: GH_TOKEN: ${{ github.token }} @@ -154,18 +157,31 @@ jobs: SITE_URL: ${{ env.SITE_URL }} run: npm run test .github/tests/wpcm.spec.ts + - name: Run Bats tests for URL fixes (Single Site) + env: + SITE_ID: wpcm-playwright-tests + TERMINUS_TOKEN: ${{ secrets.TERMINUS_TOKEN }} + run: | + echo "Running REST URL Bats tests..." + terminus auth:login --machine-token="${TERMINUS_TOKEN}" || echo "Terminus already logged in." + # Corrected path to the test file + bats -p -t .github/tests/rest-url-fix.bats + - name: Delete Site if: success() shell: bash run: terminus site:delete wpcm-playwright-tests -y - playwright-subdir: + test-subdir: name: Subdirectory multisite runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Install Bats + uses: bats-core/bats-action@2.0.0 + - name: Wait for status artifacts env: GH_TOKEN: ${{ github.token }} @@ -287,18 +303,35 @@ jobs: echo "Running Playwright tests on WordPress subdirectory subsite" npm run test .github/tests/wpcm.spec.ts + - name: Run Bats tests for URL fixes + # This step runs *after* the site setup, including the initial permalink flush. + # The Bats test itself handles permalink changes for specific test cases. + # We need to pass the SITE_ID to the Bats test environment so WP-CLI commands work via Terminus. + env: + SITE_ID: wpcm-subdir-playwright-tests + SUBSITE: foo + TERMINUS_TOKEN: ${{ secrets.TERMINUS_TOKEN }} + run: | + echo "Running REST URL Bats tests..." + # Ensure Terminus is logged in if needed by Bats WP-CLI calls + terminus auth:login --machine-token="${TERMINUS_TOKEN}" || echo "Terminus already logged in." + bats -p -t .github/tests/2-rest-url-fix.bats + - name: Delete Site if: success() shell: bash run: terminus site:delete wpcm-subdir-playwright-tests -y - playwright-subdom: + test-subdom: name: Subdomain multisite runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Install Bats + uses: bats-core/bats-action@2.0.0 + - name: Wait for status artifacts env: GH_TOKEN: ${{ github.token }} @@ -464,3 +497,20 @@ jobs: SITE_URL: ${{ env.SUBDOMAIN_URL }} GRAPHQL_ENDPOINT: ${{ env.SUBDOMAIN_URL }}/wp/graphql run: npm run test .github/tests/wpcm.spec.ts + + - name: Run Bats tests for URL fixes (Subdomain) + env: + SITE_ID: wpcm-subdom-playwright-tests + SUBSITE: foo + TERMINUS_TOKEN: ${{ secrets.TERMINUS_TOKEN }} + run: | + echo "Running REST URL Bats tests..." + terminus auth:login --machine-token="${TERMINUS_TOKEN}" || echo "Terminus already logged in." + # Corrected path to the test file + bats -p -t .github/tests/rest-url-fix.bats + + - name: Delete Site + # Run always to ensure cleanup + if: always() + shell: bash + run: terminus site:delete wpcm-subdom-playwright-tests -y From ef77ab290cbcd0b2e434f94223d8909c182c4f44 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Tue, 6 May 2025 13:23:00 -0600 Subject: [PATCH 04/61] remove todo Co-authored-by: Phil Tyler --- web/app/mu-plugins/filters.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/mu-plugins/filters.php b/web/app/mu-plugins/filters.php index 75a66320..54b8a9ee 100644 --- a/web/app/mu-plugins/filters.php +++ b/web/app/mu-plugins/filters.php @@ -308,7 +308,7 @@ function __build_plain_rest_url( string $endpoint, ?string $query_str, ?string $ * incorrectly generates a pretty-permalink-style path. Forces the URL * back to the expected ?rest_route= format using helpers. * - * @since 1.2.3 // TODO: Update version before release + * @since 1.2.3 * @param string $url The potentially incorrect REST URL generated by WP. * @return string The corrected REST URL in plain permalink format. */ From 40df9d1068dcf3bfd58c97d55154eec13815d41b Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Tue, 6 May 2025 13:24:21 -0600 Subject: [PATCH 05/61] remove commented-out code Co-authored-by: Phil Tyler --- web/app/mu-plugins/filters.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/web/app/mu-plugins/filters.php b/web/app/mu-plugins/filters.php index 54b8a9ee..f07a0448 100644 --- a/web/app/mu-plugins/filters.php +++ b/web/app/mu-plugins/filters.php @@ -368,8 +368,6 @@ function handle_pretty_rest_request_on_plain_permalinks( \WP &$wp ) { // Directly set the query variable for the REST API. $wp->query_vars['rest_route'] = $endpoint; - // Optional: Unset other query vars WP might have incorrectly parsed. - // unset($wp->query_vars['pagename'], $wp->query_vars['error']); } // No redirect, no exit. Let WP continue processing with the modified query vars. } From 38d26a57bd1783de51462b009730b69553fbede0 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Tue, 6 May 2025 13:44:13 -0600 Subject: [PATCH 06/61] fix bats test --- .github/workflows/test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5a7b306d..af7b9ca8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -165,7 +165,7 @@ jobs: echo "Running REST URL Bats tests..." terminus auth:login --machine-token="${TERMINUS_TOKEN}" || echo "Terminus already logged in." # Corrected path to the test file - bats -p -t .github/tests/rest-url-fix.bats + bats -p -t .github/tests/2-rest-url-fix.bats - name: Delete Site if: success() @@ -315,7 +315,7 @@ jobs: echo "Running REST URL Bats tests..." # Ensure Terminus is logged in if needed by Bats WP-CLI calls terminus auth:login --machine-token="${TERMINUS_TOKEN}" || echo "Terminus already logged in." - bats -p -t .github/tests/2-rest-url-fix.bats + bats -p -t .github/tests/2-2-rest-url-fix.bats - name: Delete Site if: success() @@ -507,7 +507,7 @@ jobs: echo "Running REST URL Bats tests..." terminus auth:login --machine-token="${TERMINUS_TOKEN}" || echo "Terminus already logged in." # Corrected path to the test file - bats -p -t .github/tests/rest-url-fix.bats + bats -p -t .github/tests/2-rest-url-fix.bats - name: Delete Site # Run always to ensure cleanup From 9f64264c0003b1adc4dde64764a4a736b2a6dfc7 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Tue, 6 May 2025 13:45:57 -0600 Subject: [PATCH 07/61] return early --- web/app/mu-plugins/filters.php | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/web/app/mu-plugins/filters.php b/web/app/mu-plugins/filters.php index f07a0448..0adf4419 100644 --- a/web/app/mu-plugins/filters.php +++ b/web/app/mu-plugins/filters.php @@ -250,18 +250,21 @@ function __extract_rest_endpoint( string $path ) : string { $rest_route = '/'; // Default to base route $wp_json_pos = strpos( $path, '/wp-json/' ); - if ( $wp_json_pos !== false ) { - $extracted_route = substr( $path, $wp_json_pos + strlen( '/wp-json' ) ); // Get everything after /wp-json - // Special case: Handle the originally reported '/wp-json/wp/' malformation - if ( strpos( $extracted_route, 'wp/' ) === 0 ) { - $extracted_route = substr( $extracted_route, strlen( 'wp' ) ); // Remove the extra 'wp' - } - // Ensure the extracted route starts with a slash - if ( ! $extracted_route && $extracted_route[0] !== '/' ) { - $extracted_route = '/' . $extracted_route; - } - $rest_route = $extracted_route ?: '/'; // Use extracted route or default to base - } + if ( $wp_json_pos === false ) { + return $rest_route; // Return base if /wp-json/ not found + } + + $extracted_route = substr( $path, $wp_json_pos + strlen( '/wp-json' ) ); // Get everything after /wp-json + // Special case: Handle the originally reported '/wp-json/wp/' malformation + if ( strpos( $extracted_route, 'wp/' ) === 0 ) { + $extracted_route = substr( $extracted_route, strlen( 'wp' ) ); // Remove the extra 'wp' + } + // Ensure the extracted route starts with a slash + if ( ! $extracted_route && $extracted_route[0] !== '/' ) { + $extracted_route = '/' . $extracted_route; + } + $rest_route = $extracted_route ?: '/'; // Use extracted route or default to base + return $rest_route; } From 5a9df6d3d8298ce209ea78a18b0498e80711ffd5 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Tue, 6 May 2025 13:47:03 -0600 Subject: [PATCH 08/61] remove redundant else --- web/app/mu-plugins/filters.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/app/mu-plugins/filters.php b/web/app/mu-plugins/filters.php index 0adf4419..b43e665d 100644 --- a/web/app/mu-plugins/filters.php +++ b/web/app/mu-plugins/filters.php @@ -301,9 +301,9 @@ function __build_plain_rest_url( string $endpoint, ?string $query_str, ?string $ // Use normalization helper if available if ( function_exists( __NAMESPACE__ . '\\__normalize_wp_url' ) ) { return __normalize_wp_url( $correct_url ); - } else { - return $correct_url; // Return without full normalization as fallback } + + return $correct_url; // Return without full normalization as fallback } /** From 3dafef0209ad3abd779291d265b094162a7861c1 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Tue, 6 May 2025 13:47:15 -0600 Subject: [PATCH 09/61] fix spacing --- web/app/mu-plugins/filters.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/app/mu-plugins/filters.php b/web/app/mu-plugins/filters.php index b43e665d..ab6a51bd 100644 --- a/web/app/mu-plugins/filters.php +++ b/web/app/mu-plugins/filters.php @@ -285,7 +285,7 @@ function __build_plain_rest_url( string $endpoint, ?string $query_str, ?string $ $correct_url = rtrim( $home_url, '/' ) . '/?rest_route=' . $endpoint; // Append original query parameters (if any, besides rest_route) - if ( !empty( $query_str ) ) { + if ( ! empty( $query_str ) ) { parse_str( $query_str, $query_params ); unset( $query_params['rest_route'] ); // Ensure no leftover rest_route if ( ! empty( $query_params ) ) { @@ -294,7 +294,7 @@ function __build_plain_rest_url( string $endpoint, ?string $query_str, ?string $ } } // Append fragment if present - if ( !empty( $fragment ) ) { + if ( ! empty( $fragment ) ) { $correct_url .= '#' . $fragment; } From 25d993fa8ca8d28e6a59e512dd9948e2336bcdb1 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 08:13:33 -0600 Subject: [PATCH 10/61] add helper functions --- .github/tests/2-rest-url-fix.bats | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index cb3a5e3f..2cf3618a 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -15,6 +15,17 @@ get_home_url_path() { _wp eval 'echo rtrim(parse_url(home_url(), PHP_URL_PATH) ?: "", "/");' } +set_permalinks_to_pretty() { + _wp option update permalink_structure '/%postname%/' --quiet +} + +unset_pretty_permalinks() { + _wp option update permalink_structure '' --quiet +} + +flush_rewrites() { + _wp rewrite flush --hard --quiet +} setup_suite() { # Ensure WP is installed and we are in the right directory _wp core is-installed || (echo "WordPress not installed. Run setup script first." && exit 1) From 7a0418ed61dc5c7e2472d3e15a0c312823acd7cd Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 08:14:46 -0600 Subject: [PATCH 11/61] add teardown --- .github/tests/2-rest-url-fix.bats | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index 2cf3618a..e858e7c5 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -26,11 +26,16 @@ unset_pretty_permalinks() { flush_rewrites() { _wp rewrite flush --hard --quiet } + setup_suite() { # Ensure WP is installed and we are in the right directory _wp core is-installed || (echo "WordPress not installed. Run setup script first." && exit 1) } +teardown_test() { + flush_rewrites # Call your helper +} + @test "Check REST URL with default (pretty) permalinks (after setup script flush)" { run get_rest_url assert_success From 1d159f2581dfd39c03ebf5455d65d17ee5e687d6 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 08:18:02 -0600 Subject: [PATCH 12/61] use helper functions --- .github/tests/2-rest-url-fix.bats | 32 +++++++++++-------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index e858e7c5..7aa84e5f 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -46,8 +46,9 @@ teardown_test() { @test "Check REST URL with plain permalinks" { # Set plain permalinks and flush - _wp option update permalink_structure '' --quiet - _wp rewrite flush --hard --quiet + unset_pretty_permalinks + flush_rewrites + run get_rest_url assert_success # With plain permalinks, expect ?rest_route= based on home_url @@ -57,13 +58,12 @@ teardown_test() { assert_output --partial "?rest_route=/" # Restore pretty permalinks for subsequent tests - _wp option update permalink_structure '/%postname%/' --quiet - _wp rewrite flush --hard --quiet + set_permalinks_to_pretty } @test "Check REST URL with pretty permalinks *before* flush (Simulates new site)" { # Set pretty permalinks *without* flushing - _wp option update permalink_structure '/%postname%/' --quiet + set_permalinks_to_pretty # DO NOT FLUSH HERE # Check home_url path to confirm /wp setup @@ -80,15 +80,12 @@ teardown_test() { # If the plain permalink fix was active, it might output /wp/wp-json/wp/ and fail. assert_output --partial "/wp/wp-json/" refute_output --partial "/wp-json/wp/" # Ensure the bad structure isn't present - - # Clean up: Flush permalinks - _wp rewrite flush --hard --quiet } @test "Access pretty REST API path directly with plain permalinks active" { # Set plain permalinks and flush - _wp option update permalink_structure '' --quiet - _wp rewrite flush --hard --quiet + unset_pretty_permalinks + flush_rewrites # Get the full home URL to construct the test URL SITE_URL=$( _wp option get home ) @@ -97,18 +94,11 @@ teardown_test() { TEST_URL="${SITE_URL}/wp-json/wp/v2/posts" # Make a curl request to the pretty URL - # -s: silent, -o /dev/null: discard body, -w '%{http_code}': output only HTTP code - # -L: follow redirects (we expect NO redirect, so this helps verify) - # We expect a 200 OK if the internal handling works, or maybe 404 if not found, - # but crucially NOT a 301/302 redirect. - run curl -s -o /dev/null -w '%{http_code}' -L "${TEST_URL}" + run curl -s -o /dev/null -w '%{http_code}:%{content_type}' -L "${TEST_URL}" assert_success - # Assert that the final HTTP status code is 200 (OK) - # If it were redirecting, -L would follow, but the *initial* code wouldn't be 200. - # If the internal handling fails, it might be 404 or other error. - assert_output "200" + # Assert that the final HTTP status code is 200 (OK) and application/json + assert_output --partial "200:application/json" # Restore pretty permalinks for subsequent tests - _wp option update permalink_structure '/%postname%/' --quiet - _wp rewrite flush --hard --quiet + set_permalinks_to_pretty } From 9667586417a84891fc25f180ed5f88ecb8e6f0b7 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 08:25:13 -0600 Subject: [PATCH 13/61] add a test that checks the json body of the api endpoint for hello-world post --- .github/tests/2-rest-url-fix.bats | 44 +++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index 7aa84e5f..89747036 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -102,3 +102,47 @@ teardown_test() { # Restore pretty permalinks for subsequent tests set_permalinks_to_pretty } + +@test "Validate REST API JSON output for 'hello-world' post (with plain permalinks)" { + unset_pretty_permalinks + + SITE_URL=$( _wp option get home ) + # Get the ID of the 'hello-world' post. + POST_ID=$( _wp post list --post_type=post --name=hello-world --field=ID --format=ids ) + assert_not_empty "$POST_ID" "The 'Hello world!' post (slug: hello-world) was not found." + + # Even with plain permalinks, we test accessing the pretty REST API path + HELLO_WORLD_API_URL="${SITE_URL}/wp-json/wp/v2/posts/${POST_ID}" + BODY_FILE=$(mktemp) + trap 'rm -f "$BODY_FILE"' EXIT # Ensure cleanup + + # curl writes body to BODY_FILE, metadata to stdout (captured by 'run') + run curl -s -L -o "$BODY_FILE" \ + -w "HTTP_STATUS:%{http_code}\nCONTENT_TYPE:%{content_type}" \ + "${HELLO_WORLD_API_URL}" + + assert_success "curl command failed for ${HELLO_WORLD_API_URL}. Output: $output" + + # Parse and assert metadata from $output + HTTP_STATUS=$(echo "$output" | grep "HTTP_STATUS:" | cut -d: -f2) + CONTENT_TYPE=$(echo "$output" | grep "CONTENT_TYPE:" | cut -d: -f2-) + + assert_equal "$HTTP_STATUS" "200" "HTTP status was '$HTTP_STATUS', expected '200'. Full metadata: $output" + assert_match "$CONTENT_TYPE" "application/json" "Content-Type was '$ CONTENT_TYPE', expected to contain 'application/json'. Full metadata: $output" + + JSON_BODY=$(cat "$BODY_FILE") + + echo "$JSON_BODY" | jq -e . > /dev/null + assert_success "Response body is not valid JSON. Body: $JSON_BODY" + + run jq -e ".id == ${POST_ID}" <<< "$JSON_BODY" + assert_success "JSON .id mismatch. Expected ${POST_ID}. Body: $JSON_BODY" + + run jq -e '.slug == "hello-world"' <<< "$JSON_BODY" + assert_success "JSON .slug mismatch. Expected 'hello-world'. Body: $JSON_BODY" + + run jq -e '.title.rendered == "Hello world!"' <<< "$JSON_BODY" + assert_success "JSON .title.rendered mismatch. Expected 'Hello world!'. Body: $JSON_BODY" + + set_permalinks_to_pretty +} From 34307e8fc31cc06ab5474845adb23a73217c368a Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 08:28:29 -0600 Subject: [PATCH 14/61] change delete site step to only run on success --- .github/workflows/test.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index af7b9ca8..7df32149 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -510,7 +510,6 @@ jobs: bats -p -t .github/tests/2-rest-url-fix.bats - name: Delete Site - # Run always to ensure cleanup - if: always() + if: success() shell: bash run: terminus site:delete wpcm-subdom-playwright-tests -y From 7f719784bcf846e53a9866fa3399181c953883f5 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 08:30:57 -0600 Subject: [PATCH 15/61] remove the hard failure for mixed PRs --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f53834e..3b7dcf1a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,8 +53,7 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | - gh pr comment ${{ github.event.pull_request.number }} -b "Hi from your friendly robot! :robot: It looks like there might be commits to both release and non-release files in this PR. Please review and remove any commits that don't belong." - exit 1 + gh pr comment ${{ github.event.pull_request.number }} -b "Hi from your friendly robot! :robot: It looks like there might be commits to both release and non-release files in this PR. Please review and make sure to use **rebase and merge** rather than **squash** when you merge." - name: Check Composer lock file is up to date run: composer validate --no-check-all From 6e6f5de1cf441dbb4d29318214788a83e3711539 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 08:42:48 -0600 Subject: [PATCH 16/61] add a manual delete test sites workflow --- .../workflows/manual-delete-test-sites.yml | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 .github/workflows/manual-delete-test-sites.yml diff --git a/.github/workflows/manual-delete-test-sites.yml b/.github/workflows/manual-delete-test-sites.yml new file mode 100644 index 00000000..206c998f --- /dev/null +++ b/.github/workflows/manual-delete-test-sites.yml @@ -0,0 +1,60 @@ +name: (Manual) Delete test sites +on: + workflow_dispatch: + # Use inputs to specify whether to delete a specific site or all sites. + inputs: + site: + description: 'Site to delete ' + required: true + default: 'all' + type: choice + options: + - all + - wpcm-playwright-tests + - wpcm-subdir-playwright-tests + - wpcm-subdom-playwright-tests + + +jobs: + delete-test-sites: + runs-on: ubuntu-latest + steps: + - name: Install SSH keys + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Validate Pantheon Host Key + shell: bash + run: | + echo "Host *.drush.in HostKeyAlgorithms +ssh-rsa" >> ~/.ssh/config + echo "Host *.drush.in PubkeyAcceptedKeyTypes +ssh-rsa" >> ~/.ssh/config + echo "StrictHostKeyChecking no" >> ~/.ssh/config + + - name: Install Terminus + uses: pantheon-systems/terminus-github-actions@v1 + with: + pantheon-machine-token: ${{ secrets.TERMINUS_TOKEN }} + + - name: Delete test sites + run: | + # Check if the input is 'all' or a specific site + if [[ "${{ github.event.inputs.site }}" == "all" ]]; then + echo "Deleting all test sites..." + # List of all test sites to delete + sites_to_delete=( + "wpcm-playwright-tests" + "wpcm-subdir-playwright-tests" + "wpcm-subdom-playwright-tests" + ) + else + echo "Single site selected: ${{ github.event.inputs.site }}" + # If a specific site is provided, use it + sites_to_delete=("${{ github.event.inputs.site }}") + fi + + for site in "${sites_to_delete[@]}"; do + echo "Deleting site: $site" + terminus site:delete $site -y + done + echo "All done! 🧹" From b73befddf43f370ee8f80810b73befc58c4d61a6 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 08:57:57 -0600 Subject: [PATCH 17/61] fix path to bats test --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7df32149..772bd73c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -315,7 +315,7 @@ jobs: echo "Running REST URL Bats tests..." # Ensure Terminus is logged in if needed by Bats WP-CLI calls terminus auth:login --machine-token="${TERMINUS_TOKEN}" || echo "Terminus already logged in." - bats -p -t .github/tests/2-2-rest-url-fix.bats + bats -p -t .github/tests/2-rest-url-fix.bats - name: Delete Site if: success() From 0f1a1434770b99e3420b24908dd18e2569a18598 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 08:58:26 -0600 Subject: [PATCH 18/61] we can't actually delete the subdomain test site because we need a site plan to actually use subdomains --- .github/workflows/manual-delete-test-sites.yml | 3 --- .github/workflows/test.yml | 5 ----- 2 files changed, 8 deletions(-) diff --git a/.github/workflows/manual-delete-test-sites.yml b/.github/workflows/manual-delete-test-sites.yml index 206c998f..f498305d 100644 --- a/.github/workflows/manual-delete-test-sites.yml +++ b/.github/workflows/manual-delete-test-sites.yml @@ -12,8 +12,6 @@ on: - all - wpcm-playwright-tests - wpcm-subdir-playwright-tests - - wpcm-subdom-playwright-tests - jobs: delete-test-sites: @@ -45,7 +43,6 @@ jobs: sites_to_delete=( "wpcm-playwright-tests" "wpcm-subdir-playwright-tests" - "wpcm-subdom-playwright-tests" ) else echo "Single site selected: ${{ github.event.inputs.site }}" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 772bd73c..7442afaf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -508,8 +508,3 @@ jobs: terminus auth:login --machine-token="${TERMINUS_TOKEN}" || echo "Terminus already logged in." # Corrected path to the test file bats -p -t .github/tests/2-rest-url-fix.bats - - - name: Delete Site - if: success() - shell: bash - run: terminus site:delete wpcm-subdom-playwright-tests -y From 8677a46b06129ada20cb272d598e8adc6e6d0bad Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 09:03:40 -0600 Subject: [PATCH 19/61] use bats-assert --- .github/tests/2-rest-url-fix.bats | 2 ++ .github/workflows/test.yml | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index 89747036..43e83214 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -1,5 +1,7 @@ #!/usr/bin/env bats +load 'bats-assert/load.bash' + # wp wrapper function _wp() { terminus wp -- ${SITE_ID}.dev "$@" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7442afaf..99b9589c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -39,6 +39,13 @@ jobs: - name: Install Bats uses: bats-core/bats-action@2.0.0 + - name: Install bats-assert and bats-support + run: | + git clone https://github.com/bats-core/bats-support.git /tmp/bats-support + git clone https://github.com/bats-core/bats-assert.git /tmp/bats-assert + echo "/tmp/bats-support/bin" >> $GITHUB_PATH + echo "/tmp/bats-assert/bin" >> $GITHUB_PATH + - name: Wait for status artifacts env: GH_TOKEN: ${{ github.token }} @@ -182,6 +189,13 @@ jobs: - name: Install Bats uses: bats-core/bats-action@2.0.0 + - name: Install bats-assert and bats-support + run: | + git clone https://github.com/bats-core/bats-support.git /tmp/bats-support + git clone https://github.com/bats-core/bats-assert.git /tmp/bats-assert + echo "/tmp/bats-support/bin" >> $GITHUB_PATH + echo "/tmp/bats-assert/bin" >> $GITHUB_PATH + - name: Wait for status artifacts env: GH_TOKEN: ${{ github.token }} @@ -332,6 +346,13 @@ jobs: - name: Install Bats uses: bats-core/bats-action@2.0.0 + - name: Install bats-assert and bats-support + run: | + git clone https://github.com/bats-core/bats-support.git /tmp/bats-support + git clone https://github.com/bats-core/bats-assert.git /tmp/bats-assert + echo "/tmp/bats-support/bin" >> $GITHUB_PATH + echo "/tmp/bats-assert/bin" >> $GITHUB_PATH + - name: Wait for status artifacts env: GH_TOKEN: ${{ github.token }} From d061302911dfafc04df3c809ddd6efcd0813ad9d Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 09:08:11 -0600 Subject: [PATCH 20/61] log, but don't display errors --- .github/fixtures/config/application.subdom.php | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/.github/fixtures/config/application.subdom.php b/.github/fixtures/config/application.subdom.php index 4e62aed5..3fb643e5 100644 --- a/.github/fixtures/config/application.subdom.php +++ b/.github/fixtures/config/application.subdom.php @@ -137,17 +137,10 @@ /** * Debugging Settings */ -if ( ( isset( $_ENV['PANTHEON_ENVIRONMENT'] ) && $_ENV['PANTHEON_ENVIRONMENT'] === 'dev' ) || isset( $_ENV['LANDO'] ) ) { - Config::define( 'WP_DEBUG_DISPLAY', true ); - Config::define( 'WP_DEBUG_LOG', true ); - Config::define( 'SCRIPT_DEBUG', true ); - ini_set( 'display_errors', '1' ); -} else { - Config::define( 'WP_DEBUG_DISPLAY', false ); - Config::define( 'WP_DEBUG_LOG', false ); - Config::define( 'SCRIPT_DEBUG', false ); - ini_set( 'display_errors', '0' ); -} +Config::define( 'WP_DEBUG_DISPLAY', false ); +Config::define( 'WP_DEBUG_LOG', true ); +Config::define( 'SCRIPT_DEBUG', false ); +ini_set( 'display_errors', '0' ); /** * Allow WordPress to detect HTTPS when used behind a reverse proxy or a load balancer From 1f0894529a2b02629771edcf3e6a86e400621c53 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 09:10:16 -0600 Subject: [PATCH 21/61] fix path to bats-assert --- .github/tests/2-rest-url-fix.bats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index 43e83214..638c71fe 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -1,6 +1,6 @@ #!/usr/bin/env bats -load 'bats-assert/load.bash' +load '/tmp/bats-assert/load.bash' # wp wrapper function _wp() { From 2e8cb9d42e6d9a1e0260bca3a9a90ad00ba70447 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 09:17:28 -0600 Subject: [PATCH 22/61] load bats-support --- .github/tests/2-rest-url-fix.bats | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index 638c71fe..030b1f94 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -1,5 +1,6 @@ #!/usr/bin/env bats +load '/tmp/bats-support/load.bash' load '/tmp/bats-assert/load.bash' # wp wrapper function From 21c356ed2cf4f68dbc31383b7dcc5000b97d1f00 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 09:26:37 -0600 Subject: [PATCH 23/61] bats-assert and bats-support should be installed by the action --- .github/tests/2-rest-url-fix.bats | 4 ++-- .github/workflows/test.yml | 27 +++------------------------ 2 files changed, 5 insertions(+), 26 deletions(-) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index 030b1f94..5007fa65 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -1,7 +1,7 @@ #!/usr/bin/env bats -load '/tmp/bats-support/load.bash' -load '/tmp/bats-assert/load.bash' +load 'bats-support/load.bash' +load 'bats-assert/load.bash' # wp wrapper function _wp() { diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 99b9589c..ec580839 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,14 +37,7 @@ jobs: - uses: actions/checkout@v4 - name: Install Bats - uses: bats-core/bats-action@2.0.0 - - - name: Install bats-assert and bats-support - run: | - git clone https://github.com/bats-core/bats-support.git /tmp/bats-support - git clone https://github.com/bats-core/bats-assert.git /tmp/bats-assert - echo "/tmp/bats-support/bin" >> $GITHUB_PATH - echo "/tmp/bats-assert/bin" >> $GITHUB_PATH + uses: bats-core/bats-action@3.0.1 - name: Wait for status artifacts env: @@ -187,14 +180,7 @@ jobs: - uses: actions/checkout@v4 - name: Install Bats - uses: bats-core/bats-action@2.0.0 - - - name: Install bats-assert and bats-support - run: | - git clone https://github.com/bats-core/bats-support.git /tmp/bats-support - git clone https://github.com/bats-core/bats-assert.git /tmp/bats-assert - echo "/tmp/bats-support/bin" >> $GITHUB_PATH - echo "/tmp/bats-assert/bin" >> $GITHUB_PATH + uses: bats-core/bats-action@3.0.1 - name: Wait for status artifacts env: @@ -344,14 +330,7 @@ jobs: - uses: actions/checkout@v4 - name: Install Bats - uses: bats-core/bats-action@2.0.0 - - - name: Install bats-assert and bats-support - run: | - git clone https://github.com/bats-core/bats-support.git /tmp/bats-support - git clone https://github.com/bats-core/bats-assert.git /tmp/bats-assert - echo "/tmp/bats-support/bin" >> $GITHUB_PATH - echo "/tmp/bats-assert/bin" >> $GITHUB_PATH + uses: bats-core/bats-action@3.0.1 - name: Wait for status artifacts env: From 286ca4453776cd78d1fd2d038cfc681c0d40c588 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 10:08:40 -0600 Subject: [PATCH 24/61] use bats-action standard way of loading libraries --- .github/tests/2-rest-url-fix.bats | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index 5007fa65..b86fb372 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -1,7 +1,15 @@ #!/usr/bin/env bats -load 'bats-support/load.bash' -load 'bats-assert/load.bash' +# Function to load necessary BATS helper libraries +_load_bats_helpers() { + # Ensure BATS_LIB_PATH includes /usr/lib if not already set, + # as bats-core/bats-action installs helpers there. + export BATS_LIB_PATH="${BATS_LIB_PATH:-/usr/lib}" + + bats_load_library bats-support + bats_load_library bats-assert +} +_load_bats_helpers # wp wrapper function _wp() { From af5527c7708e672ac1f9f0112c7e6274404a4c23 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 10:17:57 -0600 Subject: [PATCH 25/61] remove export for BATS_LIB_PATH --- .github/tests/2-rest-url-fix.bats | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index b86fb372..9c68a4e8 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -2,10 +2,6 @@ # Function to load necessary BATS helper libraries _load_bats_helpers() { - # Ensure BATS_LIB_PATH includes /usr/lib if not already set, - # as bats-core/bats-action installs helpers there. - export BATS_LIB_PATH="${BATS_LIB_PATH:-/usr/lib}" - bats_load_library bats-support bats_load_library bats-assert } From 79cec77f99279df64edced1b897ee6aa02334029 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 10:29:47 -0600 Subject: [PATCH 26/61] don't use bats_load_library --- .github/tests/2-rest-url-fix.bats | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index 9c68a4e8..b95805bf 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -1,11 +1,7 @@ #!/usr/bin/env bats -# Function to load necessary BATS helper libraries -_load_bats_helpers() { - bats_load_library bats-support - bats_load_library bats-assert -} -_load_bats_helpers +load 'bats-support/load' +load 'bats-assert/load' # wp wrapper function _wp() { From 891ed5d6ed77f0d7084ed5d84a14b2ca9f2fe1dc Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 11:03:38 -0600 Subject: [PATCH 27/61] set the lib path in the action env --- .github/workflows/test.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ec580839..8c2cc9ba 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -38,6 +38,12 @@ jobs: - name: Install Bats uses: bats-core/bats-action@3.0.1 + id: setup-bats + + - name: Run REST URL fix tests + run: bats .github/tests/2-rest-url-fix.bats + env: + BATS_LIB_PATH: ${{ steps.setup-bats.outputs.lib-path }} - name: Wait for status artifacts env: @@ -181,6 +187,12 @@ jobs: - name: Install Bats uses: bats-core/bats-action@3.0.1 + id: setup-bats + + - name: Run REST URL fix tests + run: bats .github/tests/2-rest-url-fix.bats + env: + BATS_LIB_PATH: ${{ steps.setup-bats.outputs.lib-path }} - name: Wait for status artifacts env: @@ -331,6 +343,12 @@ jobs: - name: Install Bats uses: bats-core/bats-action@3.0.1 + id: setup-bats + + - name: Run REST URL fix tests + run: bats .github/tests/2-rest-url-fix.bats + env: + BATS_LIB_PATH: ${{ steps.setup-bats.outputs.lib-path }} - name: Wait for status artifacts env: From 36a84636fe9f935e4e01c4658fc57d9d5864c18e Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 11:12:48 -0600 Subject: [PATCH 28/61] move the BATS_LIB_PATH into the run command --- .github/workflows/test.yml | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8c2cc9ba..5f21114c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,11 +40,6 @@ jobs: uses: bats-core/bats-action@3.0.1 id: setup-bats - - name: Run REST URL fix tests - run: bats .github/tests/2-rest-url-fix.bats - env: - BATS_LIB_PATH: ${{ steps.setup-bats.outputs.lib-path }} - - name: Wait for status artifacts env: GH_TOKEN: ${{ github.token }} @@ -170,7 +165,7 @@ jobs: run: | echo "Running REST URL Bats tests..." terminus auth:login --machine-token="${TERMINUS_TOKEN}" || echo "Terminus already logged in." - # Corrected path to the test file + export BATS_LIB_PATH=${{ steps.setup-bats.outputs.lib-path }} bats -p -t .github/tests/2-rest-url-fix.bats - name: Delete Site @@ -189,11 +184,6 @@ jobs: uses: bats-core/bats-action@3.0.1 id: setup-bats - - name: Run REST URL fix tests - run: bats .github/tests/2-rest-url-fix.bats - env: - BATS_LIB_PATH: ${{ steps.setup-bats.outputs.lib-path }} - - name: Wait for status artifacts env: GH_TOKEN: ${{ github.token }} @@ -327,6 +317,7 @@ jobs: echo "Running REST URL Bats tests..." # Ensure Terminus is logged in if needed by Bats WP-CLI calls terminus auth:login --machine-token="${TERMINUS_TOKEN}" || echo "Terminus already logged in." + export BATS_LIB_PATH=${{ steps.setup-bats.outputs.lib-path }} bats -p -t .github/tests/2-rest-url-fix.bats - name: Delete Site @@ -345,11 +336,6 @@ jobs: uses: bats-core/bats-action@3.0.1 id: setup-bats - - name: Run REST URL fix tests - run: bats .github/tests/2-rest-url-fix.bats - env: - BATS_LIB_PATH: ${{ steps.setup-bats.outputs.lib-path }} - - name: Wait for status artifacts env: GH_TOKEN: ${{ github.token }} @@ -524,5 +510,5 @@ jobs: run: | echo "Running REST URL Bats tests..." terminus auth:login --machine-token="${TERMINUS_TOKEN}" || echo "Terminus already logged in." - # Corrected path to the test file + export BATS_LIB_PATH=${{ steps.setup-bats.outputs.lib-path }} bats -p -t .github/tests/2-rest-url-fix.bats From 593d190e9762ab93f1ad79cd67d215b24db75bce Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 11:21:09 -0600 Subject: [PATCH 29/61] try exporting locally again --- .github/tests/2-rest-url-fix.bats | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index b95805bf..3649b691 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -1,7 +1,8 @@ #!/usr/bin/env bats -load 'bats-support/load' -load 'bats-assert/load' +export BATS_LIB_PATH="${BATS_LIB_PATH:-/usr/lib}" +bats_load_library bats-support +bats_load_library bats-assert # wp wrapper function _wp() { From bdfe05a163954f88efcf528746d0804aaf2c1e2c Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 11:30:04 -0600 Subject: [PATCH 30/61] filter out terminus noise from wp commands --- .github/tests/2-rest-url-fix.bats | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index 3649b691..a7600088 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -6,7 +6,19 @@ bats_load_library bats-assert # wp wrapper function _wp() { - terminus wp -- ${SITE_ID}.dev "$@" + # Capture all output, then filter. + # The `|| true` prevents the command from exiting the script if terminus + # returns a non-zero exit code but still outputs something we might want to + # see for debugging (though BATS will catch the failure). + local raw_output + raw_output=$(terminus wp -- ${SITE_ID}.dev "$@" 2>&1 || true) + + # Filter out known Terminus noise and extract the relevant line. + # This tries to get the last line that is NOT a warning or a command notice. + echo "$raw_output" | \ + grep -vE "(^\[warning\]|^\[notice\] Command:)" | \ + grep -vE "^Deprecated: rtrim\(\): Passing null to parameter #1" | \ + tail -n 1 } # Helper function to get REST URL via WP-CLI From 58780a79a8b55abe5a107c69ad7fcd1234b3c530 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 11:45:19 -0600 Subject: [PATCH 31/61] use partial assertion for home url path --- .github/tests/2-rest-url-fix.bats | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index a7600088..18204126 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -6,19 +6,7 @@ bats_load_library bats-assert # wp wrapper function _wp() { - # Capture all output, then filter. - # The `|| true` prevents the command from exiting the script if terminus - # returns a non-zero exit code but still outputs something we might want to - # see for debugging (though BATS will catch the failure). - local raw_output - raw_output=$(terminus wp -- ${SITE_ID}.dev "$@" 2>&1 || true) - - # Filter out known Terminus noise and extract the relevant line. - # This tries to get the last line that is NOT a warning or a command notice. - echo "$raw_output" | \ - grep -vE "(^\[warning\]|^\[notice\] Command:)" | \ - grep -vE "^Deprecated: rtrim\(\): Passing null to parameter #1" | \ - tail -n 1 + terminus wp -- ${SITE_ID}.dev "$@" } # Helper function to get REST URL via WP-CLI @@ -53,6 +41,8 @@ teardown_test() { } @test "Check REST URL with default (pretty) permalinks (after setup script flush)" { + set_permalinks_to_pretty + flush_rewrites run get_rest_url assert_success # Default setup script sets /%postname%/ and flushes. @@ -85,7 +75,7 @@ teardown_test() { # Check home_url path to confirm /wp setup run get_home_url_path assert_success - assert_output "/wp" + assert_output --partial "/wp" # Now check get_rest_url() - this is where the original issue might occur run get_rest_url From b2cd0a3ffb0bee376787a8d50fd4de4f129d1cc3 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 11:53:37 -0600 Subject: [PATCH 32/61] setup permalinks always --- devops/scripts/setup-playwright-tests.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devops/scripts/setup-playwright-tests.sh b/devops/scripts/setup-playwright-tests.sh index e22bbb6b..2349057f 100755 --- a/devops/scripts/setup-playwright-tests.sh +++ b/devops/scripts/setup-playwright-tests.sh @@ -94,7 +94,9 @@ install_wp() { fi terminus wp "${site_id}".dev -- core multisite-install --title="${site_name}" --admin_user=wpcm --admin_email=test@dev.null --subdomains="$is_subdomains" --url="${site_url}" +} +setup_permalinks() { terminus wp "${site_id}".dev -- option update permalink_structure '/%postname%/' terminus wp "${site_id}".dev -- rewrite flush terminus wp "${site_id}".dev -- cache flush @@ -182,4 +184,5 @@ install_wp status_check set_up_subsite install_wp_graphql +setup_permalinks echo -e "${GREEN}Done${RESET} ✨" From 185dde311e3890854ae5af14fed261a485b01279 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 12:02:26 -0600 Subject: [PATCH 33/61] run a full env:clear-cache and wait before moving on --- devops/scripts/setup-playwright-tests.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devops/scripts/setup-playwright-tests.sh b/devops/scripts/setup-playwright-tests.sh index 2349057f..dfc91009 100755 --- a/devops/scripts/setup-playwright-tests.sh +++ b/devops/scripts/setup-playwright-tests.sh @@ -97,9 +97,12 @@ install_wp() { } setup_permalinks() { + echo "Setting permalink structure" terminus wp "${site_id}".dev -- option update permalink_structure '/%postname%/' terminus wp "${site_id}".dev -- rewrite flush terminus wp "${site_id}".dev -- cache flush + terminus env:clear-cache "${site_id}".dev + terminus workflow:wait --max=30 } status_check() { From 8bb2f2162e46bad5ab7654f2e7e499ed44ae6d8f Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 12:10:02 -0600 Subject: [PATCH 34/61] ensure permalinks are pretty for playwright tests subdom installs don't run setup playwright tests script (unless the site doesn't exist) so we need to at least clean up after the bats tests maybe set permalinks to plain --- .github/workflows/test.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5f21114c..f3d2a76c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -489,6 +489,14 @@ jobs: echo "${{ env.SUBDOMAIN_URL }} - ${SUBDOMAIN_URL_TEST}" exit 1 fi + + # Ensure permalinks are pretty for playwright tests + echo "Setting permalink structure" + terminus wp "${site_id}".dev -- option update permalink_structure '/%postname%/' + terminus wp "${site_id}".dev -- rewrite flush + terminus wp "${site_id}".dev -- cache flush + terminus env:clear-cache "${site_id}".dev + terminus workflow:wait --max=30 - name: Run Playwright tests on main site env: SITE_NAME: ${{ env.SITE_NAME }} From f6c7fa4113e97acf668c09042003e7fe794550ad Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 12:15:35 -0600 Subject: [PATCH 35/61] fix terminus site name copy pasta from setup script failed --- .github/workflows/test.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f3d2a76c..20fe60b5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -492,10 +492,10 @@ jobs: # Ensure permalinks are pretty for playwright tests echo "Setting permalink structure" - terminus wp "${site_id}".dev -- option update permalink_structure '/%postname%/' - terminus wp "${site_id}".dev -- rewrite flush - terminus wp "${site_id}".dev -- cache flush - terminus env:clear-cache "${site_id}".dev + terminus wp "${{ env.SITE_ID }}".dev -- option update permalink_structure '/%postname%/' + terminus wp "${{ env.SITE_ID }}".dev -- rewrite flush + terminus wp "${{ env.SITE_ID }}".dev -- cache flush + terminus env:clear-cache "${{ env.SITE_ID }}".dev terminus workflow:wait --max=30 - name: Run Playwright tests on main site env: From 208d41c48970df00b0b3006f2e8946ff7aa9bc26 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 12:22:32 -0600 Subject: [PATCH 36/61] fix workflow:wait --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 20fe60b5..e3e097a8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -496,7 +496,7 @@ jobs: terminus wp "${{ env.SITE_ID }}".dev -- rewrite flush terminus wp "${{ env.SITE_ID }}".dev -- cache flush terminus env:clear-cache "${{ env.SITE_ID }}".dev - terminus workflow:wait --max=30 + terminus workflow:wait -- "${{ env.SITE_ID }}" --max 30 - name: Run Playwright tests on main site env: SITE_NAME: ${{ env.SITE_NAME }} From 8c17c929439b512ce32fa7be8a290971d37f30a1 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 12:30:04 -0600 Subject: [PATCH 37/61] need site env and move wait --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e3e097a8..1dcbcbd2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -496,7 +496,7 @@ jobs: terminus wp "${{ env.SITE_ID }}".dev -- rewrite flush terminus wp "${{ env.SITE_ID }}".dev -- cache flush terminus env:clear-cache "${{ env.SITE_ID }}".dev - terminus workflow:wait -- "${{ env.SITE_ID }}" --max 30 + terminus workflow:wait --max=10 -- "${{ env.SITE_ID }}".dev - name: Run Playwright tests on main site env: SITE_NAME: ${{ env.SITE_NAME }} From 8f10153441192ff6237985562b5cd78bbbed2818 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 12:47:52 -0600 Subject: [PATCH 38/61] fix tests --- .github/tests/2-rest-url-fix.bats | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index 18204126..12492010 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -43,11 +43,13 @@ teardown_test() { @test "Check REST URL with default (pretty) permalinks (after setup script flush)" { set_permalinks_to_pretty flush_rewrites - run get_rest_url - assert_success - # Default setup script sets /%postname%/ and flushes. - # Expecting /wp/wp-json/ because home_url path should be /wp - assert_output --partial "/wp/wp-json/" + + EXPECTED_URL="https://dev-${SITE_ID}.pantheonsite.io/wp/wp-json/" + run curl -s -o /dev/null -w '%{http_code}:%{content_type}' -L "${EXPECTED_URL}" + assert_success "curl command failed to access ${EXPECTED_URL}" + # Assert that the final HTTP status code is 200 (OK) and application/json + assert_output --partial "200:" "Expected HTTP 200 for ${EXPECTED_URL}. Output: $output" + assert_output --partial ":application/json" "Expected Content-Type application/json for ${EXPECTED_URL}. Output: $output" } @test "Check REST URL with plain permalinks" { @@ -77,15 +79,13 @@ teardown_test() { assert_success assert_output --partial "/wp" - # Now check get_rest_url() - this is where the original issue might occur - run get_rest_url - assert_success - # Assert that the output *should* be the correct /wp/wp-json/ even before flush, - # assuming the fix (either integrated or separate filter) is in place. - # If the fix is NOT in place, this might output /wp-json/ and fail. - # If the plain permalink fix was active, it might output /wp/wp-json/wp/ and fail. - assert_output --partial "/wp/wp-json/" - refute_output --partial "/wp-json/wp/" # Ensure the bad structure isn't present + EXPECTED_URL="https://dev-${SITE_ID}.pantheonsite.io/wp/wp-json/" + run curl -s -o /dev/null -w '%{http_code}:%{content_type}' -L "${EXPECTED_URL}" + assert_success "curl command failed to access ${EXPECTED_URL} (before flush)" + # Assert that the final HTTP status code is 200 (OK) and application/json + # This assumes the fix ensures the correct URL works even before flushing. + assert_output --partial "200:" "Expected HTTP 200 for ${EXPECTED_URL} (before flush). Output: $output" + assert_output --partial ":application/json" "Expected Content-Type application/json for ${EXPECTED_URL} (before flush). Output: $output" } @test "Access pretty REST API path directly with plain permalinks active" { From a3c7444be7d4f782973d1954f1549beee59be23a Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 13:00:18 -0600 Subject: [PATCH 39/61] standardize vars --- .github/tests/2-rest-url-fix.bats | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index 12492010..da17ee08 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -44,12 +44,12 @@ teardown_test() { set_permalinks_to_pretty flush_rewrites - EXPECTED_URL="https://dev-${SITE_ID}.pantheonsite.io/wp/wp-json/" - run curl -s -o /dev/null -w '%{http_code}:%{content_type}' -L "${EXPECTED_URL}" - assert_success "curl command failed to access ${EXPECTED_URL}" + SITE_URL="https://dev-${SITE_ID}.pantheonsite.io/wp/wp-json/" + run curl -s -o /dev/null -w '%{http_code}:%{content_type}' -L "${SITE_URL}" + assert_success "curl command failed to access ${SITE_URL}" # Assert that the final HTTP status code is 200 (OK) and application/json - assert_output --partial "200:" "Expected HTTP 200 for ${EXPECTED_URL}. Output: $output" - assert_output --partial ":application/json" "Expected Content-Type application/json for ${EXPECTED_URL}. Output: $output" + assert_output --partial "200:" "Expected HTTP 200 for ${SITE_URL}. Output: $output" + assert_output --partial ":application/json" "Expected Content-Type application/json for ${SITE_URL}. Output: $output" } @test "Check REST URL with plain permalinks" { @@ -79,13 +79,13 @@ teardown_test() { assert_success assert_output --partial "/wp" - EXPECTED_URL="https://dev-${SITE_ID}.pantheonsite.io/wp/wp-json/" - run curl -s -o /dev/null -w '%{http_code}:%{content_type}' -L "${EXPECTED_URL}" - assert_success "curl command failed to access ${EXPECTED_URL} (before flush)" + SITE_URL="https://dev-${SITE_ID}.pantheonsite.io/wp/wp-json/" + run curl -s -o /dev/null -w '%{http_code}:%{content_type}' -L "${SITE_URL}" + assert_success "curl command failed to access ${SITE_URL} (before flush)" # Assert that the final HTTP status code is 200 (OK) and application/json # This assumes the fix ensures the correct URL works even before flushing. - assert_output --partial "200:" "Expected HTTP 200 for ${EXPECTED_URL} (before flush). Output: $output" - assert_output --partial ":application/json" "Expected Content-Type application/json for ${EXPECTED_URL} (before flush). Output: $output" + assert_output --partial "200:" "Expected HTTP 200 for ${SITE_URL} (before flush). Output: $output" + assert_output --partial ":application/json" "Expected Content-Type application/json for ${SITE_URL} (before flush). Output: $output" } @test "Access pretty REST API path directly with plain permalinks active" { From 66deb3bd74e9b36f4117c0554bfe5724f0fbe809 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 13:00:36 -0600 Subject: [PATCH 40/61] don't require wp-cli commands for post id and url --- .github/tests/2-rest-url-fix.bats | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index da17ee08..9c746ae9 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -112,13 +112,14 @@ teardown_test() { @test "Validate REST API JSON output for 'hello-world' post (with plain permalinks)" { unset_pretty_permalinks - SITE_URL=$( _wp option get home ) - # Get the ID of the 'hello-world' post. - POST_ID=$( _wp post list --post_type=post --name=hello-world --field=ID --format=ids ) - assert_not_empty "$POST_ID" "The 'Hello world!' post (slug: hello-world) was not found." - - # Even with plain permalinks, we test accessing the pretty REST API path - HELLO_WORLD_API_URL="${SITE_URL}/wp-json/wp/v2/posts/${POST_ID}" + # Hardcode known post ID and construct URL from SITE_ID env var + # This avoids issues with noisy output from _wp option get home / _wp post list + local POST_ID=1 + local BASE_URL="https://dev-${SITE_ID}.pantheonsite.io/wp/wp-json/" + local HELLO_WORLD_API_URL="${BASE_URL}wp/v2/posts/${POST_ID}" + + # Create temp file for body + local BODY_FILE BODY_FILE=$(mktemp) trap 'rm -f "$BODY_FILE"' EXIT # Ensure cleanup From 10f13abbe715feeda463e252a62e1c4a0bec8f3f Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 13:38:24 -0600 Subject: [PATCH 41/61] just remove clear-cache --- .github/workflows/test.yml | 1 - devops/scripts/setup-playwright-tests.sh | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1dcbcbd2..d7c5e836 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -496,7 +496,6 @@ jobs: terminus wp "${{ env.SITE_ID }}".dev -- rewrite flush terminus wp "${{ env.SITE_ID }}".dev -- cache flush terminus env:clear-cache "${{ env.SITE_ID }}".dev - terminus workflow:wait --max=10 -- "${{ env.SITE_ID }}".dev - name: Run Playwright tests on main site env: SITE_NAME: ${{ env.SITE_NAME }} diff --git a/devops/scripts/setup-playwright-tests.sh b/devops/scripts/setup-playwright-tests.sh index dfc91009..2faf1b79 100755 --- a/devops/scripts/setup-playwright-tests.sh +++ b/devops/scripts/setup-playwright-tests.sh @@ -102,7 +102,6 @@ setup_permalinks() { terminus wp "${site_id}".dev -- rewrite flush terminus wp "${site_id}".dev -- cache flush terminus env:clear-cache "${site_id}".dev - terminus workflow:wait --max=30 } status_check() { From f6925be75822e3bb9e54627cb57f65e23654fd0e Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 13:59:46 -0600 Subject: [PATCH 42/61] remove trap line to test --- .github/tests/2-rest-url-fix.bats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index 9c746ae9..d0c9e21d 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -121,7 +121,7 @@ teardown_test() { # Create temp file for body local BODY_FILE BODY_FILE=$(mktemp) - trap 'rm -f "$BODY_FILE"' EXIT # Ensure cleanup + # trap 'rm -f "$BODY_FILE"' EXIT # Ensure cleanup -- Removing trap for diagnostics # curl writes body to BODY_FILE, metadata to stdout (captured by 'run') run curl -s -L -o "$BODY_FILE" \ From 42d41b3281fe8a68cbfe5ac6e322a9102c6d086f Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 14:13:20 -0600 Subject: [PATCH 43/61] change assert_match to assert_success --- .github/tests/2-rest-url-fix.bats | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index d0c9e21d..936e37b1 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -121,7 +121,6 @@ teardown_test() { # Create temp file for body local BODY_FILE BODY_FILE=$(mktemp) - # trap 'rm -f "$BODY_FILE"' EXIT # Ensure cleanup -- Removing trap for diagnostics # curl writes body to BODY_FILE, metadata to stdout (captured by 'run') run curl -s -L -o "$BODY_FILE" \ @@ -135,7 +134,9 @@ teardown_test() { CONTENT_TYPE=$(echo "$output" | grep "CONTENT_TYPE:" | cut -d: -f2-) assert_equal "$HTTP_STATUS" "200" "HTTP status was '$HTTP_STATUS', expected '200'. Full metadata: $output" - assert_match "$CONTENT_TYPE" "application/json" "Content-Type was '$ CONTENT_TYPE', expected to contain 'application/json'. Full metadata: $output" + + echo "$CONTENT_TYPE" | grep -q "application/json" + assert_success "Content-Type was '$CONTENT_TYPE', expected to contain 'application/json'. Full metadata: $output" JSON_BODY=$(cat "$BODY_FILE") From 0a3665ee4b01b4459b9d55380b4847a5082e1649 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 14:28:38 -0600 Subject: [PATCH 44/61] click the continue button if the continue button exists --- .github/tests/wpcm.spec.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/tests/wpcm.spec.ts b/.github/tests/wpcm.spec.ts index daa4d933..332f8aec 100644 --- a/.github/tests/wpcm.spec.ts +++ b/.github/tests/wpcm.spec.ts @@ -1,10 +1,27 @@ -import { test, expect } from "@playwright/test"; +import { test, expect, type Browser } from "@playwright/test"; const exampleArticle = "Hello world!"; const siteTitle = process.env.SITE_NAME || "WPCM Playwright Tests"; const siteUrl = process.env.SITE_URL || "https://dev-wpcm-playwright-tests.pantheonsite.io"; let graphqlEndpoint = process.env.GRAPHQL_ENDPOINT || `${siteUrl}/wp/graphql`; +test.beforeAll(async ({ browser }: { browser: Browser }) => { + const page = await browser.newPage(); + await page.goto(siteUrl); + const continueButtonLocator = page.locator('button.pds-button:has-text("Continue")'); + try { + // Wait for up to 10 seconds for the button to be visible + await continueButtonLocator.waitFor({ state: 'visible', timeout: 10000 }); + await continueButtonLocator.click(); + // Wait for navigation to complete after click, if necessary + await page.waitForLoadState('domcontentloaded', { timeout: 5000 }); + } catch (e) { + // Button not visible within the timeout, or other error, assume not present and continue + console.log("Interstitial 'Continue' button not found or not clicked during beforeAll, proceeding with tests."); + } + await page.close(); +}); + test("homepage loads and contains example content", async ({ page }) => { await page.goto(siteUrl); await expect(page).toHaveTitle(siteTitle); From e78fe30fd3e54890cb8647d40182a75265deb9ad Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 14:43:01 -0600 Subject: [PATCH 45/61] try a different means of clicking the button --- .github/tests/wpcm.spec.ts | 48 ++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/.github/tests/wpcm.spec.ts b/.github/tests/wpcm.spec.ts index 332f8aec..967e9fac 100644 --- a/.github/tests/wpcm.spec.ts +++ b/.github/tests/wpcm.spec.ts @@ -1,29 +1,28 @@ -import { test, expect, type Browser } from "@playwright/test"; +import { test, expect } from "@playwright/test"; const exampleArticle = "Hello world!"; const siteTitle = process.env.SITE_NAME || "WPCM Playwright Tests"; const siteUrl = process.env.SITE_URL || "https://dev-wpcm-playwright-tests.pantheonsite.io"; let graphqlEndpoint = process.env.GRAPHQL_ENDPOINT || `${siteUrl}/wp/graphql`; +let interstitialClicked = false; -test.beforeAll(async ({ browser }: { browser: Browser }) => { - const page = await browser.newPage(); +test("homepage loads and contains example content", async ({ page }) => { await page.goto(siteUrl); - const continueButtonLocator = page.locator('button.pds-button:has-text("Continue")'); - try { - // Wait for up to 10 seconds for the button to be visible - await continueButtonLocator.waitFor({ state: 'visible', timeout: 10000 }); - await continueButtonLocator.click(); - // Wait for navigation to complete after click, if necessary - await page.waitForLoadState('domcontentloaded', { timeout: 5000 }); - } catch (e) { - // Button not visible within the timeout, or other error, assume not present and continue - console.log("Interstitial 'Continue' button not found or not clicked during beforeAll, proceeding with tests."); + + if (!interstitialClicked) { + try { + const continueButtonLocator = page.locator('button.pds-button:has-text("Continue")'); + // Wait for a short period for the button to be visible + await continueButtonLocator.waitFor({ state: 'visible', timeout: 7000 }); + await continueButtonLocator.click(); + // Wait for potential navigation after click + await page.waitForNavigation({ waitUntil: 'domcontentloaded', timeout: 10000 }); + } catch (e) { + // Button not visible, or other error, assume not present or already handled. + } + interstitialClicked = true; // Mark as attempted/handled for this suite execution. } - await page.close(); -}); -test("homepage loads and contains example content", async ({ page }) => { - await page.goto(siteUrl); await expect(page).toHaveTitle(siteTitle); await expect(page.getByText(exampleArticle)).toHaveText(exampleArticle); }); @@ -39,6 +38,21 @@ test("WP REST API is accessible", async ({ request }) => { test("Hello World post is accessible", async ({ page }) => { await page.goto(`${siteUrl}/hello-world/'`); + + if (!interstitialClicked) { + try { + const continueButtonLocator = page.locator('button.pds-button:has-text("Continue")'); + // Wait for a short period for the button to be visible + await continueButtonLocator.waitFor({ state: 'visible', timeout: 7000 }); + await continueButtonLocator.click(); + // Wait for potential navigation after click + await page.waitForNavigation({ waitUntil: 'domcontentloaded', timeout: 10000 }); + } catch (e) { + // Button not visible, or other error, assume not present or already handled. + } + interstitialClicked = true; // Mark as attempted/handled for this suite execution. + } + await expect(page).toHaveTitle(`${exampleArticle} – ${siteTitle}`); // Locate the element containing the desired text const welcomeText = page.locator('text=Welcome to WordPress'); From f086bd2ecf1e84846a8992396fc56a69e858f615 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 15:14:32 -0600 Subject: [PATCH 46/61] adjust tests for more robust single/multisite handling --- .github/tests/2-rest-url-fix.bats | 72 ++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 16 deletions(-) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index 936e37b1..048ae129 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -19,6 +19,17 @@ get_home_url_path() { _wp eval 'echo rtrim(parse_url(home_url(), PHP_URL_PATH) ?: "", "/");' } +# Helper function to check if it's a multisite installation +_is_multisite() { + # This command exits with 0 if it's a network (multisite) installation, + # and 1 otherwise. We suppress output as we only care about the exit code. + if _wp core is-installed --network > /dev/null 2>&1; then + return 0 # true, it is multisite + else + return 1 # false, it is not multisite + fi +} + set_permalinks_to_pretty() { _wp option update permalink_structure '/%postname%/' --quiet } @@ -40,11 +51,18 @@ teardown_test() { flush_rewrites # Call your helper } -@test "Check REST URL with default (pretty) permalinks (after setup script flush)" { +@test "Check REST URL with default (pretty) permalinks" { set_permalinks_to_pretty flush_rewrites - SITE_URL="https://dev-${SITE_ID}.pantheonsite.io/wp/wp-json/" + local rest_api_base_path + if _is_multisite; then + rest_api_base_path="/wp/wp-json/" + else + rest_api_base_path="/wp-json/" + fi + SITE_URL="https://dev-${SITE_ID}.pantheonsite.io${rest_api_base_path}" + run curl -s -o /dev/null -w '%{http_code}:%{content_type}' -L "${SITE_URL}" assert_success "curl command failed to access ${SITE_URL}" # Assert that the final HTTP status code is 200 (OK) and application/json @@ -75,11 +93,20 @@ teardown_test() { # DO NOT FLUSH HERE # Check home_url path to confirm /wp setup - run get_home_url_path - assert_success - assert_output --partial "/wp" + if _is_multisite; then + run get_home_url_path + assert_success + assert_output --partial "/wp" + fi + + local rest_api_base_path + if _is_multisite; then + rest_api_base_path="/wp/wp-json/" + else + rest_api_base_path="/wp-json/" + fi + SITE_URL="https://dev-${SITE_ID}.pantheonsite.io${rest_api_base_path}" - SITE_URL="https://dev-${SITE_ID}.pantheonsite.io/wp/wp-json/" run curl -s -o /dev/null -w '%{http_code}:%{content_type}' -L "${SITE_URL}" assert_success "curl command failed to access ${SITE_URL} (before flush)" # Assert that the final HTTP status code is 200 (OK) and application/json @@ -93,17 +120,24 @@ teardown_test() { unset_pretty_permalinks flush_rewrites - # Get the full home URL to construct the test URL - SITE_URL=$( _wp option get home ) - # Construct the pretty-style REST API URL - # Note: home_url() includes /wp, so we append /wp-json/... directly - TEST_URL="${SITE_URL}/wp-json/wp/v2/posts" + # Construct the pretty-style REST API URL for a specific endpoint + local base_domain="https://dev-${SITE_ID}.pantheonsite.io" + local rest_endpoint_full_path + if _is_multisite; then + # For multisite in /wp/, the REST API base is /wp/wp-json/ + rest_endpoint_full_path="/wp/wp-json/wp/v2/posts" + else + # For single site, the REST API base is /wp-json/ + rest_endpoint_full_path="/wp-json/wp/v2/posts" + fi + TEST_URL="${base_domain}${rest_endpoint_full_path}" # Make a curl request to the pretty URL run curl -s -o /dev/null -w '%{http_code}:%{content_type}' -L "${TEST_URL}" - assert_success + assert_success "curl command failed for ${TEST_URL}. Output: $output" # Assert that the final HTTP status code is 200 (OK) and application/json - assert_output --partial "200:application/json" + assert_output --partial "200:" "Expected HTTP 200 for ${TEST_URL}. Output: $output" + assert_output --partial ":application/json" "Expected Content-Type application/json for ${TEST_URL}. Output: $output" # Restore pretty permalinks for subsequent tests set_permalinks_to_pretty @@ -112,10 +146,16 @@ teardown_test() { @test "Validate REST API JSON output for 'hello-world' post (with plain permalinks)" { unset_pretty_permalinks - # Hardcode known post ID and construct URL from SITE_ID env var - # This avoids issues with noisy output from _wp option get home / _wp post list + # Hardcode known post ID local POST_ID=1 - local BASE_URL="https://dev-${SITE_ID}.pantheonsite.io/wp/wp-json/" + local base_domain="https://dev-${SITE_ID}.pantheonsite.io" + local rest_api_base_path + if _is_multisite; then + rest_api_base_path="/wp/wp-json/" + else + rest_api_base_path="/wp-json/" + fi + local BASE_URL="${base_domain}${rest_api_base_path}" local HELLO_WORLD_API_URL="${BASE_URL}wp/v2/posts/${POST_ID}" # Create temp file for body From 53bfdb72a28a89249ea1bc78586be4581a4cee4a Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 15:27:05 -0600 Subject: [PATCH 47/61] update service-level to plan --- devops/scripts/setup-playwright-tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devops/scripts/setup-playwright-tests.sh b/devops/scripts/setup-playwright-tests.sh index 2faf1b79..40fa3cf7 100755 --- a/devops/scripts/setup-playwright-tests.sh +++ b/devops/scripts/setup-playwright-tests.sh @@ -40,7 +40,7 @@ create_site() { else terminus site:create "${site_id}" "${site_name}" "${UPSTREAM_NAME}" --org=5ae1fa30-8cc4-4894-8ca9-d50628dcba17 echo "Site created. Setting site plan to 'pro'" - terminus service-level:set "${site_id}" pro + terminus plan:set "${site_id}" pro fi terminus connection:set "${site_id}".dev git -y } From 010059744e13981c232de98a6e4bca7f9cdbb674 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 15:46:49 -0600 Subject: [PATCH 48/61] set the plan to a real plan --- devops/scripts/setup-playwright-tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devops/scripts/setup-playwright-tests.sh b/devops/scripts/setup-playwright-tests.sh index 40fa3cf7..7d6f8f79 100755 --- a/devops/scripts/setup-playwright-tests.sh +++ b/devops/scripts/setup-playwright-tests.sh @@ -40,7 +40,7 @@ create_site() { else terminus site:create "${site_id}" "${site_name}" "${UPSTREAM_NAME}" --org=5ae1fa30-8cc4-4894-8ca9-d50628dcba17 echo "Site created. Setting site plan to 'pro'" - terminus plan:set "${site_id}" pro + terminus plan:set "${site_id}" plan-performance_small-contract-annual-1 fi terminus connection:set "${site_id}".dev git -y } From 57acb565e04478ec8d31d1a6b1274a245f72f02d Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 15:46:57 -0600 Subject: [PATCH 49/61] set the plan to free before delete --- .github/workflows/test.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d7c5e836..61cca013 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -171,7 +171,9 @@ jobs: - name: Delete Site if: success() shell: bash - run: terminus site:delete wpcm-playwright-tests -y + run: | + terminus plan:set wpcm-playwright-tests plan-free-preferred-monthly-1 + terminus site:delete wpcm-playwright-tests -y test-subdir: name: Subdirectory multisite @@ -323,7 +325,9 @@ jobs: - name: Delete Site if: success() shell: bash - run: terminus site:delete wpcm-subdir-playwright-tests -y + run: | + terminus plan:set wpcm-subdir-playwright-tests plan-free-preferred-monthly-1 + terminus site:delete wpcm-subdir-playwright-tests -y test-subdom: name: Subdomain multisite From 0b46728d39983d01eeb85ce2fbc2b04543abdf57 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 16:23:42 -0600 Subject: [PATCH 50/61] update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a8888d2..fbbee9f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### v1.33.0 (2025-05-08) +* Fixes an issue where we were running `maybe-add-symlinks` but the script didn't exist. ([#183](https://github.com/pantheon-systems/wordpress-composer-managed/pull/183)) +* Fixes an issue where WP REST API urls would break on new sites before "pretty permalink" structure was set. ([#181](https://github.com/pantheon-systems/wordpress-composer-managed/pull/181)) + ### v1.32.5 (2025-02-10) * Adds the `maybe-install-symlinks` Composer script to `post-update-cmd` hook. This ensures that symlinks are created (and the `web/index.php` file is re-created) after a `composer update`. ([#175](https://github.com/pantheon-systems/wordpress-composer-managed/pull/175)) From 844687ef08d19cc3a15808628473a9217a730e03 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 16:28:13 -0600 Subject: [PATCH 51/61] dont run tests when delete test sites workflow is updated --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 61cca013..647fe160 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,6 +7,7 @@ on: - '.github/workflows/phpcbf.yml' - '.github/workflows/sage-test.yml' - '.github/workflows/sync-default.yml' + - '.github/workflows/manual-delete-test-sites.yml' - '.github/tests/*.bats' - 'private/scripts/**' - 'devops/**' From bb98ef9c71dac2f90b6e8f3e5de75374c589d745 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Wed, 7 May 2025 16:28:23 -0600 Subject: [PATCH 52/61] downgrade sites before delete --- .github/workflows/manual-delete-test-sites.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/manual-delete-test-sites.yml b/.github/workflows/manual-delete-test-sites.yml index f498305d..f3b66e1c 100644 --- a/.github/workflows/manual-delete-test-sites.yml +++ b/.github/workflows/manual-delete-test-sites.yml @@ -52,6 +52,7 @@ jobs: for site in "${sites_to_delete[@]}"; do echo "Deleting site: $site" + terminus plan:set $site plan-free-preferred-monthly-1 terminus site:delete $site -y done echo "All done! 🧹" From 5ee4a1cd204e30b39066608dc6391e747f0d0db6 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Thu, 8 May 2025 08:09:35 -0600 Subject: [PATCH 53/61] bypass interstertial page via http headers --- .github/tests/custom-test.ts | 16 ++++++++++++++++ .github/tests/wpcm.spec.ts | 31 +------------------------------ 2 files changed, 17 insertions(+), 30 deletions(-) create mode 100644 .github/tests/custom-test.ts diff --git a/.github/tests/custom-test.ts b/.github/tests/custom-test.ts new file mode 100644 index 00000000..e49e8f83 --- /dev/null +++ b/.github/tests/custom-test.ts @@ -0,0 +1,16 @@ +import { test as baseTest, expect as baseExpect } from '@playwright/test'; + +// Extend base test by providing a custom 'page' fixture. +export const test = baseTest.extend({ + page: async ({ page }, use) => { + // Set custom headers for all page navigations/requests initiated by the page. + await page.setExtraHTTPHeaders({ + 'Deterrence-Bypass': 'true', + }); + // Continue with the test, providing the modified page fixture. + await use(page); + }, +}); + +// Re-export expect so you can import it from this file as well. +export { baseExpect as expect }; diff --git a/.github/tests/wpcm.spec.ts b/.github/tests/wpcm.spec.ts index 967e9fac..232836f8 100644 --- a/.github/tests/wpcm.spec.ts +++ b/.github/tests/wpcm.spec.ts @@ -1,28 +1,13 @@ -import { test, expect } from "@playwright/test"; +import { test, expect } from "./custom-test"; const exampleArticle = "Hello world!"; const siteTitle = process.env.SITE_NAME || "WPCM Playwright Tests"; const siteUrl = process.env.SITE_URL || "https://dev-wpcm-playwright-tests.pantheonsite.io"; let graphqlEndpoint = process.env.GRAPHQL_ENDPOINT || `${siteUrl}/wp/graphql`; -let interstitialClicked = false; test("homepage loads and contains example content", async ({ page }) => { await page.goto(siteUrl); - if (!interstitialClicked) { - try { - const continueButtonLocator = page.locator('button.pds-button:has-text("Continue")'); - // Wait for a short period for the button to be visible - await continueButtonLocator.waitFor({ state: 'visible', timeout: 7000 }); - await continueButtonLocator.click(); - // Wait for potential navigation after click - await page.waitForNavigation({ waitUntil: 'domcontentloaded', timeout: 10000 }); - } catch (e) { - // Button not visible, or other error, assume not present or already handled. - } - interstitialClicked = true; // Mark as attempted/handled for this suite execution. - } - await expect(page).toHaveTitle(siteTitle); await expect(page.getByText(exampleArticle)).toHaveText(exampleArticle); }); @@ -39,20 +24,6 @@ test("WP REST API is accessible", async ({ request }) => { test("Hello World post is accessible", async ({ page }) => { await page.goto(`${siteUrl}/hello-world/'`); - if (!interstitialClicked) { - try { - const continueButtonLocator = page.locator('button.pds-button:has-text("Continue")'); - // Wait for a short period for the button to be visible - await continueButtonLocator.waitFor({ state: 'visible', timeout: 7000 }); - await continueButtonLocator.click(); - // Wait for potential navigation after click - await page.waitForNavigation({ waitUntil: 'domcontentloaded', timeout: 10000 }); - } catch (e) { - // Button not visible, or other error, assume not present or already handled. - } - interstitialClicked = true; // Mark as attempted/handled for this suite execution. - } - await expect(page).toHaveTitle(`${exampleArticle} – ${siteTitle}`); // Locate the element containing the desired text const welcomeText = page.locator('text=Welcome to WordPress'); From 8da04ff93ad83ab753cd7e73da058d7d63446977 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Thu, 8 May 2025 08:18:49 -0600 Subject: [PATCH 54/61] add || true to plan:set command if it fails, it shouldn't prevent site deletion --- .github/workflows/manual-delete-test-sites.yml | 3 ++- .github/workflows/test.yml | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/manual-delete-test-sites.yml b/.github/workflows/manual-delete-test-sites.yml index f3b66e1c..19b9210c 100644 --- a/.github/workflows/manual-delete-test-sites.yml +++ b/.github/workflows/manual-delete-test-sites.yml @@ -52,7 +52,8 @@ jobs: for site in "${sites_to_delete[@]}"; do echo "Deleting site: $site" - terminus plan:set $site plan-free-preferred-monthly-1 + # Downgrade the site to a free plan, if applicable. + terminus plan:set $site plan-free-preferred-monthly-1 || true terminus site:delete $site -y done echo "All done! 🧹" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 647fe160..df91249a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -173,7 +173,8 @@ jobs: if: success() shell: bash run: | - terminus plan:set wpcm-playwright-tests plan-free-preferred-monthly-1 + # Downgrade the site to a free plan, if applicable. + terminus plan:set wpcm-playwright-tests plan-free-preferred-monthly-1 || true terminus site:delete wpcm-playwright-tests -y test-subdir: @@ -327,7 +328,8 @@ jobs: if: success() shell: bash run: | - terminus plan:set wpcm-subdir-playwright-tests plan-free-preferred-monthly-1 + # Downgrade the site to a free plan, if applicable. + terminus plan:set wpcm-subdir-playwright-tests plan-free-preferred-monthly-1 || true terminus site:delete wpcm-subdir-playwright-tests -y test-subdom: From 2904fc3cdb8fbf3b0e67eb470d6d65c175f88779 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Thu, 8 May 2025 08:36:58 -0600 Subject: [PATCH 55/61] test php 8.4 too --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3b7dcf1a..3c0c6f00 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-version: ['8.1', '8.2', '8.3'] + php-version: ['8.1', '8.2', '8.3', '8.4'] steps: - uses: actions/checkout@v4 From d52081bbef6595232bf5fcd05b83ff1399dd0c96 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Thu, 8 May 2025 08:37:08 -0600 Subject: [PATCH 56/61] better handling of check commits step --- .github/workflows/ci.yml | 80 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3c0c6f00..298b1c66 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,21 +39,87 @@ jobs: restore-keys: ${{ runner.os }}-composer- - name: Check Commits + id: commit_check # Add an ID to access outputs run: | # Don't warn about detached head. git config advice.detachedHead false - git fetch --all - git checkout -b default origin/default + git fetch --all --prune # Fetch all refs and remove stale remote-tracking branches + + # Ensure the local 'default' branch reflects 'origin/default' + git checkout -B default origin/default + + # Checkout the actual head of the pull request branch git checkout ${{ github.event.pull_request.head.sha }} - echo "This script does preliminary checks to make sure that the commits in a PR made in this repository are ready for the deploy-public-upstream script. i.e. any given commit modifies either 'normal' or 'non-release' files, never mixed." - bash ${{ github.workspace }}/devops/scripts/check-commits.sh || echo "commit_check_failed=1" >> $GITHUB_ENV - - name: Comment on PR if commit check failed - if: env.commit_check_failed == '1' + echo "Running check-commits.sh to analyze PR commits..." + + script_exit_code=0 + # Execute the script, capturing all its stderr. + script_stderr_output=$(bash ${{ github.workspace }}/devops/scripts/check-commits.sh 2>&1) || script_exit_code=$? + + echo "--- Script Standard Error Output (and stdout if any) ---" + echo "${script_stderr_output}" + echo "--- End Script Output ---" + echo "Script exit code: $script_exit_code" + + # Script itself failed (e.g., mixed files in a single commit, forbidden files) + commit_script_failed_output="false" + if [ "$script_exit_code" -ne 0 ]; then + commit_script_failed_output="true" + fi + echo "commit_script_failed=${commit_script_failed_output}" >> $GITHUB_OUTPUT + + # Mixture of 'normal' and 'non-release' commit types across the PR + has_normal_commits=$(echo "${script_stderr_output}" | grep -c "is a normal commit" || true) + has_nonrelease_commits=$(echo "${script_stderr_output}" | grep -c "is a non-release commit" || true) + + echo "Normal commits found: $has_normal_commits" + echo "Non-release commits found: $has_nonrelease_commits" + + mixed_commit_types_in_pr_output="false" + if [ "$has_normal_commits" -gt 0 ] && [ "$has_nonrelease_commits" -gt 0 ]; then + mixed_commit_types_in_pr_output="true" + fi + echo "mixed_commit_types_in_pr=${mixed_commit_types_in_pr_output}" >> $GITHUB_OUTPUT + + # Prepare overall error summary for the comment + final_error_summary="" + if [ "${commit_script_failed_output}" == "true" ]; then + # Extract lines that look like errors from the script + script_reported_errors=$(echo "${script_stderr_output}" | grep -E "contains both release and nonrelease changes|contains forbidden files" || true) + if [ -n "${script_reported_errors}" ]; then + final_error_summary+="Script reported the following issues with specific commits:\n${script_reported_errors}\n\n" + else + # If script failed but no specific errors were grepped, include the full output for context + final_error_summary+="The check-commits.sh script failed (exit code $script_exit_code). Full script output for context:\n${script_stderr_output}\n\n" + fi + fi + + if [ "${mixed_commit_types_in_pr_output}" == "true" ]; then + final_error_summary+="This PR contains a mixture of 'normal' (release) commits and 'non-release' (internal) commits. This requires careful merging (e.g., rebase and merge) to ensure only 'normal' commits are deployed to public upstream if that's the intent." + fi + + echo "final_error_summary<> $GITHUB_OUTPUT + echo -e "${final_error_summary}" >> $GITHUB_OUTPUT + echo "ERROR_SUMMARY_EOF" >> $GITHUB_OUTPUT + + - name: Comment on PR if commit check failed or types are mixed + # Trigger if the script failed OR if there's a mix of commit types + if: steps.commit_check.outputs.commit_script_failed == 'true' || steps.commit_check.outputs.mixed_commit_types_in_pr == 'true' env: GH_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ github.event.pull_request.number }} + ERROR_DETAILS: ${{ steps.commit_check.outputs.final_error_summary }} run: | - gh pr comment ${{ github.event.pull_request.number }} -b "Hi from your friendly robot! :robot: It looks like there might be commits to both release and non-release files in this PR. Please review and make sure to use **rebase and merge** rather than **squash** when you merge." + COMMENT_BODY="Hi from your friendly robot! :robot: + Please review the commit checks for this PR: + \`\`\` + ${ERROR_DETAILS} + \`\`\` + If issues are present, please ensure commits modify either 'normal' or 'non-release' files (not a mix within a single commit) and do not contain forbidden files. + If this PR intentionally mixes 'normal' and 'non-release' commit types, remember to use **rebase and merge** rather than **squash** when merging to preserve individual commit integrity for the deploy process." + + gh pr comment $PR_NUMBER -b "$COMMENT_BODY" - name: Check Composer lock file is up to date run: composer validate --no-check-all From e49c94ce8a3d7e5a2510b4382dd5c012eb184256 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Thu, 8 May 2025 08:38:41 -0600 Subject: [PATCH 57/61] style --- .github/tests/2-rest-url-fix.bats | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index 048ae129..58a72874 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -44,7 +44,10 @@ flush_rewrites() { setup_suite() { # Ensure WP is installed and we are in the right directory - _wp core is-installed || (echo "WordPress not installed. Run setup script first." && exit 1) + if ! _wp core is-installed; then + echo "WordPress not installed. Run setup script first." + exit 1 + fi } teardown_test() { From 01be302ff0dfcdc7effb6df7bbd9c2fbf23512bf Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Thu, 8 May 2025 08:40:27 -0600 Subject: [PATCH 58/61] ensure the comment only appears once --- .github/workflows/ci.yml | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 298b1c66..77e3f11c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -111,15 +111,25 @@ jobs: PR_NUMBER: ${{ github.event.pull_request.number }} ERROR_DETAILS: ${{ steps.commit_check.outputs.final_error_summary }} run: | - COMMENT_BODY="Hi from your friendly robot! :robot: - Please review the commit checks for this PR: - \`\`\` - ${ERROR_DETAILS} - \`\`\` - If issues are present, please ensure commits modify either 'normal' or 'non-release' files (not a mix within a single commit) and do not contain forbidden files. - If this PR intentionally mixes 'normal' and 'non-release' commit types, remember to use **rebase and merge** rather than **squash** when merging to preserve individual commit integrity for the deploy process." - - gh pr comment $PR_NUMBER -b "$COMMENT_BODY" + COMMENT_MARKER="" + + # Check for existing comment with the marker + EXISTING_COMMENT_ID=$(gh pr view $PR_NUMBER --json comments -q ".comments[] | select(.body | contains(\"${COMMENT_MARKER}\")) | .id" || echo "") + + if [ -n "$EXISTING_COMMENT_ID" ]; then + echo "Commit check comment already exists (ID: $EXISTING_COMMENT_ID). Skipping new comment." + else + echo "No existing commit check comment found. Posting a new one." + COMMENT_BODY="Hi from your friendly robot! :robot: ${COMMENT_MARKER} + Please review the commit checks for this PR: + \`\`\` + ${ERROR_DETAILS} + \`\`\` + If issues are present, please ensure commits modify either 'normal' or 'non-release' files (not a mix within a single commit) and do not contain forbidden files. + If this PR intentionally mixes 'normal' and 'non-release' commit types, remember to use **rebase and merge** rather than **squash** when merging to preserve individual commit integrity for the deploy process." + + gh pr comment $PR_NUMBER -b "$COMMENT_BODY" + fi - name: Check Composer lock file is up to date run: composer validate --no-check-all From a86c2501cb773d7383848f19b9a1e125c30d556d Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Thu, 8 May 2025 08:41:18 -0600 Subject: [PATCH 59/61] remove backticks this shouldn't be a code comment --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 77e3f11c..8ad5962b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -122,9 +122,9 @@ jobs: echo "No existing commit check comment found. Posting a new one." COMMENT_BODY="Hi from your friendly robot! :robot: ${COMMENT_MARKER} Please review the commit checks for this PR: - \`\`\` + ${ERROR_DETAILS} - \`\`\` + If issues are present, please ensure commits modify either 'normal' or 'non-release' files (not a mix within a single commit) and do not contain forbidden files. If this PR intentionally mixes 'normal' and 'non-release' commit types, remember to use **rebase and merge** rather than **squash** when merging to preserve individual commit integrity for the deploy process." From 4e672ee4265f0900a151f120a87981d09f4d27a6 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Thu, 8 May 2025 08:41:31 -0600 Subject: [PATCH 60/61] but maybe it should be a blockquote --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ad5962b..296d7939 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -123,7 +123,7 @@ jobs: COMMENT_BODY="Hi from your friendly robot! :robot: ${COMMENT_MARKER} Please review the commit checks for this PR: - ${ERROR_DETAILS} + > ${ERROR_DETAILS} If issues are present, please ensure commits modify either 'normal' or 'non-release' files (not a mix within a single commit) and do not contain forbidden files. If this PR intentionally mixes 'normal' and 'non-release' commit types, remember to use **rebase and merge** rather than **squash** when merging to preserve individual commit integrity for the deploy process." From ae9419827b2c8a3ef6da6ae8324b9283a65bdcc4 Mon Sep 17 00:00:00 2001 From: Chris Reynolds Date: Thu, 8 May 2025 08:42:44 -0600 Subject: [PATCH 61/61] combine multisite checks --- .github/tests/2-rest-url-fix.bats | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/tests/2-rest-url-fix.bats b/.github/tests/2-rest-url-fix.bats index 58a72874..8c78c0a6 100644 --- a/.github/tests/2-rest-url-fix.bats +++ b/.github/tests/2-rest-url-fix.bats @@ -95,19 +95,18 @@ teardown_test() { set_permalinks_to_pretty # DO NOT FLUSH HERE + local rest_api_base_path + # Check home_url path to confirm /wp setup if _is_multisite; then run get_home_url_path assert_success assert_output --partial "/wp" - fi - - local rest_api_base_path - if _is_multisite; then rest_api_base_path="/wp/wp-json/" else rest_api_base_path="/wp-json/" fi + SITE_URL="https://dev-${SITE_ID}.pantheonsite.io${rest_api_base_path}" run curl -s -o /dev/null -w '%{http_code}:%{content_type}' -L "${SITE_URL}"