Commit a1592e1a522 for woocommerce

commit a1592e1a522b72bc2a3387cd6f39f8458cf8927f
Author: theAverageDev (Luca Tumedei) <luca.tumedei@automattic.com>
Date:   Fri Jun 19 11:04:35 2026 +0200

    Fix Blocks e2e sample product import failing in nightly runs (#65798)

    * Add on-demand CI inputs to test a branch against a release artifact

    * Fix Blocks e2e nightly to run under the canonical woocommerce folder

    * Fix template injection in on-demand checks input validation

    * Add fail-loud guard to wp-env plugin override script

    Exit 1 when no '.'/'../woocommerce' source entry is found to replace,
    instead of silently writing an override that omits the artifact and
    failing downstream. Report the count of removed entries, and document
    why the mapping is scoped to env.tests.

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0970acb1d65..e8ef418643a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -32,6 +32,10 @@ on:
         description: 'The release artifact name to download and run tests for.'
         required: false
         type: string
+      releaseTag:
+        description: 'Release tag to download the artifact from. Defaults to refName, then the current ref. Lets a branch run pull a published artifact (e.g. nightly) while keeping its own checkout.'
+        required: false
+        type: string

 concurrency:
   # Cancel concurrent jobs but not for push event. For push use the run_id to have a unique group.
@@ -179,7 +183,7 @@ jobs:
       - name: 'Update wp-env config'
         if: ${{ inputs.artifactName != '' }}
         env:
-          RELEASE_TAG: ${{ inputs.refName != '' && inputs.refName || github.ref_name }}
+          RELEASE_TAG: ${{ inputs.releaseTag != '' && inputs.releaseTag || ( inputs.refName != '' && inputs.refName || github.ref_name ) }}
           ARTIFACT_NAME: ${{ inputs.artifactName }}
           WP_ENV_CONFIG_PATH: ${{ github.workspace }}/${{ matrix.projectPath }}
         run: node .github/workflows/scripts/override-wp-env-plugins.js
@@ -214,7 +218,7 @@ jobs:
           CODEVITALS_PROJECT_TOKEN: ${{ secrets.CODEVITALS_PROJECT_TOKEN }} # required by Metrics tests
           LAST_FAILED_RUN: ${{ vars.LAST_FAILED_RUN }}
           GITHUB_BASE_SHA: ${{ github.event.pull_request.base.sha }}
-          PLUGIN_SLUG: ${{ inputs.refName == 'nightly' && 'woocommerce-trunk-nightly' || '' }} # required by test:plugincheck
+          PLUGIN_SLUG: '' # required by test:plugincheck; nightly installs under the canonical 'woocommerce' folder
           PLAYWRIGHT_JUNIT_SUITE_NAME: 'WooCommerce Core'
           PLAYWRIGHT_JUNIT_SUITE_ID: 'woocommerce-core'
         run: |
diff --git a/.github/workflows/scripts/override-wp-env-plugins.js b/.github/workflows/scripts/override-wp-env-plugins.js
index 33f05c9569b..9d87332b9cd 100644
--- a/.github/workflows/scripts/override-wp-env-plugins.js
+++ b/.github/workflows/scripts/override-wp-env-plugins.js
@@ -22,45 +22,73 @@ const artifactUrl = `https://github.com/woocommerce/woocommerce/releases/downloa

 const configPath = `${ WP_ENV_CONFIG_PATH }/.wp-env.json`;
 console.log( `Reading ${ configPath }` );
-const data = fs.readFileSync( configPath, 'utf8' );
-const wpEnvConfig = JSON.parse( data );
+const wpEnvConfig = JSON.parse( fs.readFileSync( configPath, 'utf8' ) );
+
+// wp-env names an installed plugin's folder after the source basename, so
+// installing WooCommerce straight from the release URL would create a
+// `woocommerce-trunk-nightly` folder - a name no real install produces and which
+// breaks the test setup's `wp-content/plugins/woocommerce/...` assumptions.
+// Instead, mount the release artifact at the canonical `woocommerce` folder via a
+// mapping (wp-env downloads and extracts it for us; the zip's top-level dir is
+// `woocommerce/`, the same one WordPress core unzips for a real user) and drop
+// the source entry from the plugin lists. Mapped plugins are not auto-activated,
+// so `tests/e2e-pw/bin/test-env-setup.sh` activates WooCommerce explicitly.
+const wooCommerceEntries = [ '.', '../woocommerce' ];
+
+let removed = 0;
+const withoutWooCommerce = ( plugins ) => {
+	if ( ! Array.isArray( plugins ) ) {
+		return plugins;
+	}
+	const filtered = plugins.filter(
+		( entry ) => ! wooCommerceEntries.includes( entry )
+	);
+	removed += plugins.length - filtered.length;
+	return filtered;
+};
+
+const wooCommerceMapping = {
+	'wp-content/plugins/woocommerce': artifactUrl,
+};

 const overrideConfig = {};

 if ( wpEnvConfig.plugins ) {
-	overrideConfig.plugins = wpEnvConfig.plugins;
+	overrideConfig.plugins = withoutWooCommerce( wpEnvConfig.plugins );
 }

 if ( wpEnvConfig.env?.tests?.plugins ) {
+	// Scope the mapping to env.tests: `wp-env start` provisions both the
+	// development and tests instances, but the Blocks e2e suite only runs
+	// against tests. Mapping here avoids extracting the artifact into the dev
+	// instance that is never exercised. Move this to a root-level `mappings` if
+	// WooCommerce is ever needed in the dev environment too.
 	overrideConfig.env = {
 		tests: {
-			plugins: wpEnvConfig.env.tests.plugins,
+			plugins: withoutWooCommerce( wpEnvConfig.env.tests.plugins ),
+			mappings: wooCommerceMapping,
 		},
 	};
+} else {
+	overrideConfig.mappings = wooCommerceMapping;
 }

-const entriesToReplace = [ '.', '../woocommerce' ];
-
-for ( const entry of entriesToReplace ) {
-	// Search and replace in root plugins
-	let found = overrideConfig.plugins.indexOf( entry );
-	if ( found >= 0 ) {
-		console.log(
-			`Replacing ${ entry } with ${ artifactUrl } in root plugins`
-		);
-		overrideConfig.plugins[ found ] = artifactUrl;
-	}
-
-	// Search and replace in test env plugins
-	found = overrideConfig.env?.tests?.plugins?.indexOf( entry );
-	if ( found >= 0 ) {
-		console.log(
-			`Replacing ${ entry } with ${ artifactUrl } in env.tests.plugins`
-		);
-		overrideConfig.env.tests.plugins[ found ] = artifactUrl;
-	}
+if ( removed === 0 ) {
+	console.error(
+		`No WooCommerce source entry (${ wooCommerceEntries.join(
+			' or '
+		) }) found in ${ configPath }. The artifact would not land at ` +
+			`wp-content/plugins/woocommerce - the plugin layout likely changed. Aborting.`
+	);
+	process.exit( 1 );
 }

+console.log(
+	`Removed ${ removed } WooCommerce source entr${
+		removed === 1 ? 'y' : 'ies'
+	}; mapping ${ artifactUrl } -> wp-content/plugins/woocommerce`
+);
+
 const overrideConfigPath = `${ WP_ENV_CONFIG_PATH }/.wp-env.override.json`;
 console.log( `Saving ${ overrideConfigPath }` );
 fs.writeFileSync(
diff --git a/.github/workflows/tests-on-demand.yml b/.github/workflows/tests-on-demand.yml
index 3a234a33e76..132d214975e 100644
--- a/.github/workflows/tests-on-demand.yml
+++ b/.github/workflows/tests-on-demand.yml
@@ -16,15 +16,35 @@ on:
         type: string
         description: 'Custom event name: In case the `Event name` choice is `custom`, this field is required.'
         required: false
+      artifact-name:
+        type: string
+        description: 'Release artifact to install instead of building from the branch (e.g. woocommerce-trunk-nightly.zip). Leave empty to build from the checked-out branch.'
+        required: false
+        default: ''
+      release-tag:
+        type: string
+        description: 'Release tag to download the artifact from (e.g. nightly). Required when artifact-name is set; the checkout still uses the dispatched branch.'
+        required: false
+        default: ''

 jobs:
   validate-input:
     runs-on: ubuntu-latest
     steps:
       - name: 'Validate input'
+        env:
+          INPUT_TRIGGER: ${{ inputs.trigger }}
+          INPUT_CUSTOM_TRIGGER: ${{ inputs.custom-trigger }}
+          INPUT_ARTIFACT_NAME: ${{ inputs.artifact-name }}
+          INPUT_RELEASE_TAG: ${{ inputs.release-tag }}
         run: |
-          if [ "${{ inputs.trigger }}" == "custom" ] && [ -z "${{ inputs.custom-trigger }}" ]; then
-            echo "Custom event name is required when event name choice `custom`."
+          if [ "$INPUT_TRIGGER" = "custom" ] && [ -z "$INPUT_CUSTOM_TRIGGER" ]; then
+            echo "Custom event name is required when event name choice \`custom\`."
+            exit 1
+          fi
+
+          if [ -n "$INPUT_ARTIFACT_NAME" ] && [ -z "$INPUT_RELEASE_TAG" ]; then
+            echo "Input \`release-tag\` is required when \`artifact-name\` is provided."
             exit 1
           fi

@@ -33,4 +53,6 @@ jobs:
     uses: ./.github/workflows/ci.yml
     with:
       trigger: ${{ inputs.trigger == 'custom' && inputs.custom-trigger || inputs.trigger }}
+      artifactName: ${{ inputs.artifact-name }}
+      releaseTag: ${{ inputs.release-tag }}
     secrets: inherit
diff --git a/plugins/woocommerce/.wp-env.json b/plugins/woocommerce/.wp-env.json
index 63f0990d80b..fd3d3f71030 100644
--- a/plugins/woocommerce/.wp-env.json
+++ b/plugins/woocommerce/.wp-env.json
@@ -44,7 +44,8 @@
 				"wp-content/themes/twentytwentyfour": "https://downloads.wordpress.org/theme/twentytwentyfour.latest-stable.zip",
 				"wp-content/plugins/woocommerce/blocks-bin": "./client/blocks/bin",
 				"wp-content/plugins/woocommerce/blocks-bin/playwright": "./tests/e2e-pw/bin/blocks",
-				"wp-content/plugins/woocommerce-blocks-test-plugins": "./tests/e2e-pw/test-plugins/blocks"
+				"wp-content/plugins/woocommerce-blocks-test-plugins": "./tests/e2e-pw/test-plugins/blocks",
+				"wp-content/plugins/woocommerce/i18n/languages": "./i18n/languages"
 			}
 		}
 	}
diff --git a/plugins/woocommerce/changelog/disable-nightly-blocks-e2e-wp-latest-1 b/plugins/woocommerce/changelog/disable-nightly-blocks-e2e-wp-latest-1
deleted file mode 100644
index 9a75352a06c..00000000000
--- a/plugins/woocommerce/changelog/disable-nightly-blocks-e2e-wp-latest-1
+++ /dev/null
@@ -1,5 +0,0 @@
-Significance: patch
-Type: dev
-Comment: Disable the Blocks e2e WP latest-1 job in nightly checks to avoid noise while that build is fixed. CI job config only; nothing shipped to merchants.
-
-
diff --git a/plugins/woocommerce/changelog/fix-blocks-e2e-nightly-sample-data-path b/plugins/woocommerce/changelog/fix-blocks-e2e-nightly-sample-data-path
new file mode 100644
index 00000000000..698b110ed3e
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-blocks-e2e-nightly-sample-data-path
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+
+Fix the Blocks e2e nightly suite: mount the released plugin at the canonical `woocommerce` folder via a wp-env mapping, instead of letting wp-env name the plugin folder after the release zip (e.g. `woocommerce-trunk-nightly`), which broke the test setup's plugin-path assumptions. The generated test translations are mapped into the plugin's languages directory the same way. Also resolve the active plugin path (WC_ABSPATH) for the sample product import.
diff --git a/plugins/woocommerce/package.json b/plugins/woocommerce/package.json
index 311861e4bc9..5727ab78212 100644
--- a/plugins/woocommerce/package.json
+++ b/plugins/woocommerce/package.json
@@ -894,6 +894,7 @@
 					},
 					"events": [
 						"pull_request",
+						"nightly-checks",
 						"release-checks",
 						"blocks-e2e-wp-latest-1"
 					],
diff --git a/plugins/woocommerce/tests/e2e-pw/bin/blocks/scripts/products.sh b/plugins/woocommerce/tests/e2e-pw/bin/blocks/scripts/products.sh
index 8ba81760616..d876cfeec32 100644
--- a/plugins/woocommerce/tests/e2e-pw/bin/blocks/scripts/products.sh
+++ b/plugins/woocommerce/tests/e2e-pw/bin/blocks/scripts/products.sh
@@ -3,7 +3,13 @@
 ###################################################################################################
 # Import sample products and regenerate product lookup tables
 ###################################################################################################
-wp import wp-content/plugins/woocommerce/sample-data/sample_products.xml --authors=skip
+# Resolve the active WooCommerce plugin directory instead of assuming a fixed
+# folder name. Locally and on PR CI the plugin is source-mapped as
+# `woocommerce`, but in nightly it is installed from the release zip as
+# `woocommerce-trunk-nightly`, so a hardcoded path would not exist there.
+wc_abspath=$(wp eval 'echo defined("WC_ABSPATH") ? WC_ABSPATH : "";')
+[ -n "$wc_abspath" ] || { echo "Could not resolve WC_ABSPATH; is WooCommerce active?" >&2; exit 1; }
+wp import "${wc_abspath}sample-data/sample_products.xml" --authors=skip
 wp wc tool run regenerate_product_lookup_tables --user=1

 # This is a hacky work around to fix product categories not having their parent category correctly assigned.
diff --git a/plugins/woocommerce/tests/e2e-pw/bin/test-env-setup.sh b/plugins/woocommerce/tests/e2e-pw/bin/test-env-setup.sh
index 4e918b27712..3783c654a11 100755
--- a/plugins/woocommerce/tests/e2e-pw/bin/test-env-setup.sh
+++ b/plugins/woocommerce/tests/e2e-pw/bin/test-env-setup.sh
@@ -12,6 +12,13 @@ if [ ! -z ${CI+y} ]; then
     exit $?
 fi

+# In nightly runs WooCommerce is mounted via a wp-env mapping so it installs
+# under the canonical `woocommerce` folder; mapped plugins are not
+# auto-activated, so activate it before any WC-dependent setup below (e.g. the
+# `customer` role user). Harmless when WC is already active (PR/source-mapped).
+echo -e 'Activate WooCommerce \n'
+wp-env run tests-cli wp plugin activate woocommerce
+
 echo -e 'Install twentytwenty, twentytwentytwo and storefront themes \n'
 wp-env run tests-cli wp theme install storefront twentytwenty twentytwentytwo &