Commit 7130151fc28 for woocommerce

commit 7130151fc285ca320e965f65f03359a0488824d8
Author: Chi-Hsuan Huang <chihsuan.tw@gmail.com>
Date:   Wed Sep 23 10:34:36 2026 +0800

    Send the analytics package version on every Tracks event (#68908)

    * Send the analytics package version as a common event prop

    Every woocommerceanalytics_* event now carries package_version so rejected
    or malformed events in Tracks can be attributed to the package release that
    emitted them.

    * Stamp the composer.json version into PACKAGE_VERSION when publishing

    The release tooling bumps composer.json but never the constant, which sat at
    0.16.3 through the 0.16.7 and 0.17.0 releases. build-package.sh now rewrites
    the constant in the published copy from composer.json and fails when the
    constant line is missing.

    * Add changelog entry for the analytics package_version prop

    * Anchor the version check in the analytics publish build

    * Allow prerelease analytics versions and note the refresh key in the docblock

diff --git a/packages/php/woocommerce-analytics/changelog/wooa7s-2110-package-version-prop b/packages/php/woocommerce-analytics/changelog/wooa7s-2110-package-version-prop
new file mode 100644
index 00000000000..8f0ba8b298c
--- /dev/null
+++ b/packages/php/woocommerce-analytics/changelog/wooa7s-2110-package-version-prop
@@ -0,0 +1,4 @@
+Significance: minor
+Type: added
+
+Send the package version on every event as `package_version`, so Tracks data can be attributed to the package release that emitted it. The published copy has `PACKAGE_VERSION` stamped from `composer.json` at build time.
diff --git a/packages/php/woocommerce-analytics/src/class-wc-analytics-tracking.php b/packages/php/woocommerce-analytics/src/class-wc-analytics-tracking.php
index 0cd35eb0eef..390dc29bb6c 100644
--- a/packages/php/woocommerce-analytics/src/class-wc-analytics-tracking.php
+++ b/packages/php/woocommerce-analytics/src/class-wc-analytics-tracking.php
@@ -12,6 +12,7 @@ namespace Automattic\Woocommerce_Analytics;

 use Automattic\Jetpack\Device_Detection;
 use Automattic\Jetpack\Device_Detection\User_Agent_Info;
+use Automattic\Woocommerce_Analytics;
 use WP_Error;

 /**
@@ -459,17 +460,18 @@ class WC_Analytics_Tracking {
 		$blog_details = self::get_blog_details();

 		return array(
-			'ui'             => $blog_user_id,
-			'blog_id'        => $blog_details['blog_id'] ?? null,
-			'store_id'       => $blog_details['store_id'] ?? null,
-			'url'            => $blog_details['url'] ?? null,
-			'woo_version'    => $blog_details['wc_version'] ?? null,
-			'wp_version'     => get_bloginfo( 'version' ),
-			'store_admin'    => count( array_intersect( array( 'administrator', 'shop_manager' ), wp_get_current_user()->roles ) ) > 0 ? 1 : 0,
-			'device'         => self::get_device_type(),
-			'store_currency' => $blog_details['store_currency'] ?? null,
-			'timezone'       => wp_timezone_string(),
-			'is_guest'       => ( $blog_user_id === null || $blog_user_id === 0 ) ? 1 : 0,
+			'ui'              => $blog_user_id,
+			'blog_id'         => $blog_details['blog_id'] ?? null,
+			'store_id'        => $blog_details['store_id'] ?? null,
+			'url'             => $blog_details['url'] ?? null,
+			'woo_version'     => $blog_details['wc_version'] ?? null,
+			'wp_version'      => get_bloginfo( 'version' ),
+			'store_admin'     => count( array_intersect( array( 'administrator', 'shop_manager' ), wp_get_current_user()->roles ) ) > 0 ? 1 : 0,
+			'device'          => self::get_device_type(),
+			'store_currency'  => $blog_details['store_currency'] ?? null,
+			'timezone'        => wp_timezone_string(),
+			'is_guest'        => ( $blog_user_id === null || $blog_user_id === 0 ) ? 1 : 0,
+			'package_version' => Woocommerce_Analytics::PACKAGE_VERSION,
 		);
 	}

diff --git a/packages/php/woocommerce-analytics/src/class-woocommerce-analytics.php b/packages/php/woocommerce-analytics/src/class-woocommerce-analytics.php
index 4a372b4ce26..931b325794b 100644
--- a/packages/php/woocommerce-analytics/src/class-woocommerce-analytics.php
+++ b/packages/php/woocommerce-analytics/src/class-woocommerce-analytics.php
@@ -20,7 +20,9 @@ use Composer\InstalledVersions;
  */
 class Woocommerce_Analytics {
 	/**
-	 * Package version.
+	 * Package version, also the key that triggers the proxy speed module refresh. Rewritten from
+	 * composer.json by tasks/build-package.sh when the package is published, so bumping it here
+	 * does not refresh published copies. Bump the version in composer.json instead.
 	 */
 	const PACKAGE_VERSION = '0.18.0';

diff --git a/packages/php/woocommerce-analytics/tasks/build-package.sh b/packages/php/woocommerce-analytics/tasks/build-package.sh
index 833d5b9f524..cd9ff916586 100755
--- a/packages/php/woocommerce-analytics/tasks/build-package.sh
+++ b/packages/php/woocommerce-analytics/tasks/build-package.sh
@@ -43,6 +43,22 @@ rsync -avhW --quiet \
     "$DIST_DIR/src/" \
     --exclude="client/"

+# Stamp the composer.json version into the shipped copy. The constant in the
+# repo is not bumped by the release tooling, so this is what keeps the version
+# a release reports about itself correct.
+echo "Stamping PACKAGE_VERSION..."
+PACKAGE_VERSION="$(node -p "require('$PACKAGE_DIR/composer.json').version")"
+if ! [[ "$PACKAGE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.]+)?$ ]]; then
+    echo "composer.json has no usable version (got '$PACKAGE_VERSION')" >&2
+    exit 1
+fi
+MAIN_CLASS_FILE="$DIST_DIR/src/class-woocommerce-analytics.php"
+perl -pi -e "s/const PACKAGE_VERSION = '[^']*';/const PACKAGE_VERSION = '$PACKAGE_VERSION';/" "$MAIN_CLASS_FILE"
+if ! grep -qF "const PACKAGE_VERSION = '$PACKAGE_VERSION';" "$MAIN_CLASS_FILE"; then
+    echo "Failed to stamp PACKAGE_VERSION $PACKAGE_VERSION into $MAIN_CLASS_FILE" >&2
+    exit 1
+fi
+
 # Copy built JS assets (main bundle, asset manifest, and all chunks)
 echo "Copying built JS assets..."
 mkdir -p "$DIST_DIR/build"
diff --git a/packages/php/woocommerce-analytics/tests/php/Universal_Page_Output_Test.php b/packages/php/woocommerce-analytics/tests/php/Universal_Page_Output_Test.php
index 298f2b71dc1..5e9278ea455 100644
--- a/packages/php/woocommerce-analytics/tests/php/Universal_Page_Output_Test.php
+++ b/packages/php/woocommerce-analytics/tests/php/Universal_Page_Output_Test.php
@@ -229,7 +229,7 @@ class Universal_Page_Output_Test extends BaseTestCase {
 	public function test_page_output_retains_store_properties(): void {
 		$output = $this->render_analytics_data();

-		foreach ( array( 'timezone', 'wp_version', 'store_currency' ) as $property ) {
+		foreach ( array( 'timezone', 'wp_version', 'store_currency', 'package_version' ) as $property ) {
 			$this->assertStringContainsString(
 				'"' . $property . '"',
 				$output,
diff --git a/packages/php/woocommerce-analytics/tests/php/WC_Analytics_Tracking_Reserved_Props_Test.php b/packages/php/woocommerce-analytics/tests/php/WC_Analytics_Tracking_Reserved_Props_Test.php
index 9889b638f5a..7a816c90166 100644
--- a/packages/php/woocommerce-analytics/tests/php/WC_Analytics_Tracking_Reserved_Props_Test.php
+++ b/packages/php/woocommerce-analytics/tests/php/WC_Analytics_Tracking_Reserved_Props_Test.php
@@ -94,6 +94,7 @@ class WC_Analytics_Tracking_Reserved_Props_Test extends BaseTestCase {
 			'store_currency',
 			'timezone',
 			'is_guest',
+			'package_version',
 			// get_server_details(), minus CLIENT_OVERRIDABLE_PROPERTIES.
 			'_via_ua',
 			'_via_ip',
diff --git a/packages/php/woocommerce-analytics/tests/php/Woocommerce_Analytics_Test.php b/packages/php/woocommerce-analytics/tests/php/Woocommerce_Analytics_Test.php
index ea2ebd5a41a..fa7a8c7d3d6 100644
--- a/packages/php/woocommerce-analytics/tests/php/Woocommerce_Analytics_Test.php
+++ b/packages/php/woocommerce-analytics/tests/php/Woocommerce_Analytics_Test.php
@@ -545,6 +545,18 @@ class Woocommerce_Analytics_Test extends BaseTestCase {
 		$this->assertMatchesRegularExpression( '/^\d+\.\d+\.\d+/', Woocommerce_Analytics::PACKAGE_VERSION );
 	}

+	/**
+	 * Every event, page-embedded or server-fired, carries the package version so
+	 * malformed events in Tracks can be attributed to the release that sent them.
+	 */
+	public function test_package_version_is_sent_as_common_property(): void {
+		$page_properties   = WC_Analytics_Tracking::get_page_common_properties();
+		$server_properties = WC_Analytics_Tracking::get_common_properties();
+
+		$this->assertSame( Woocommerce_Analytics::PACKAGE_VERSION, $page_properties['package_version'] ?? null );
+		$this->assertSame( Woocommerce_Analytics::PACKAGE_VERSION, $server_properties['package_version'] ?? null );
+	}
+
 	/**
 	 * Test version option constant is defined.
 	 */