Skip to content

Commit

Permalink
#8343: handle 4 digit extension version numbers (#8346)
Browse files Browse the repository at this point in the history
* update semver helpers to handle 4 digit versions

* fix deployment check

* make 4th digit larger to reflect actual usage

* replace direct access with getExtensionVersion()

* fix broken tests

* fix manifest for connectPage

* add 4th digit for all builds

* manifest tests

* add release version test

* revert version change

* fix package-lock version

* properly fix package.json version

* rename validateSemVerString -> normalizeSemVerString

* add documentation for getExtensionVersion

* test bad values

* js docs for normalizeSemVerString
  • Loading branch information
grahamlangford authored Apr 25, 2024
1 parent 9d0a15e commit 1b2a1d9
Show file tree
Hide file tree
Showing 35 changed files with 415 additions and 130 deletions.
12 changes: 9 additions & 3 deletions scripts/__snapshots__/manifest.test.js.snap

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 11 additions & 13 deletions scripts/manifest.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ import Policy from "csp-parse";
import { normalizeManifestPermissions } from "webext-permissions";
import { excludeDuplicatePatterns } from "webext-patterns";

function getVersion(env, isBetaListing) {
function getVersion(env) {
const stageMap = {
alpha: 1000,
beta: 2000,
release: 3000,
};

// `manifest.json` only supports numbers in the version, so use the semver
Expand All @@ -32,19 +33,16 @@ function getVersion(env, isBetaListing) {
);
const { version, stage, stageNumber } = match.groups;

// Add 4th digit for alpha/beta release builds. Used to update the extension BETA listing in the Chrome Web Store.
if (isBetaListing) {
if (stage && stageNumber) {
// Ex: 1.8.13-alpha.1 -> 1.8.13.1001
// Ex: 1.8.13-beta.55 -> 1.8.13.2055
return `${version}.${stageMap[stage] + Number(stageNumber)}`;
}

// Ex: 1.8.13.3000 -- Ensures that the release build version number is greater than the alpha/beta build version numbers
return `${version}.3000`;
// Add 4th digit for differentiating alpha/beta/stable release builds.
// Used primarily to update the extension BETA listing in the Chrome Web Store.
if (stage && stageNumber) {
// Ex: 1.8.13-alpha.1 -> 1.8.13.1001
// Ex: 1.8.13-beta.55 -> 1.8.13.2055
return `${version}.${stageMap[stage] + Number(stageNumber)}`;
}

return version;
// Ex: 1.8.13.3000 -- Ensures that the release build version number is greater than the alpha/beta build version numbers
return `${version}.${stageMap.release}`;
}

function getVersionName(env, isProduction) {
Expand Down Expand Up @@ -146,7 +144,7 @@ function addInternalUrlsToContentScripts(manifest, internal) {
function customizeManifest(manifestV2, options = {}) {
const { isProduction, manifestVersion, env = {}, isBeta } = options;
const manifest = structuredClone(manifestV2);
manifest.version = getVersion(env, isBeta);
manifest.version = getVersion(env);
manifest.version_name = getVersionName(env, isProduction);

if (!isProduction) {
Expand Down
98 changes: 68 additions & 30 deletions scripts/manifest.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,45 +15,83 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/* eslint-disable @shopify/jest/no-snapshots -- We want to specifically commit the entire customized manifest as a snapshot */
/* eslint-disable no-restricted-imports -- Aliases don't work outside built files */

import { omit } from "lodash";
import manifest from "../src/manifest.json";
import { loadEnv } from "./env.mjs";
import customizeManifest from "./manifest.mjs";

loadEnv();

const cleanCustomize = (...args) =>
omit(customizeManifest(...args), ["version", "version_name", "key"]);
const cleanCustomize = (...args) => omit(customizeManifest(...args), ["key"]);

describe("customizeManifest", () => {
test("mv2", () => {
expect(
cleanCustomize(manifest, {
env: process.env,
isProduction: true,
}),
).toMatchSnapshot();
});
test("mv3", () => {
expect(
cleanCustomize(manifest, {
env: process.env,
isProduction: true,
manifestVersion: 3,
}),
).toMatchSnapshot();
describe("release builds", () => {
test("mv2", () => {
expect(
cleanCustomize(manifest, {
// eslint-disable-next-line camelcase -- auto-inserted
env: { ...process.env, npm_package_version: "1.8.13" },
isProduction: true,
}),
).toMatchSnapshot();
});

test("mv3", () => {
expect(
cleanCustomize(manifest, {
// eslint-disable-next-line camelcase -- auto-inserted
env: { ...process.env, npm_package_version: "1.8.13" },
isProduction: true,
manifestVersion: 3,
}),
).toMatchSnapshot();
});

test("beta", () => {
expect(
cleanCustomize(manifest, {
// eslint-disable-next-line camelcase -- auto-inserted
env: { ...process.env, npm_package_version: "1.8.13" },
isProduction: true,
manifestVersion: 3,
isBeta: true,
}),
).toMatchSnapshot();
});
});
test("beta", () => {
expect(
cleanCustomize(manifest, {
env: process.env,
isProduction: true,
manifestVersion: 3,
isBeta: true,
}),
).toMatchSnapshot();

describe("four digit versioning", () => {
test("alpha version", () => {
expect(
cleanCustomize(manifest, {
// eslint-disable-next-line camelcase -- auto-inserted
env: { ...process.env, npm_package_version: "1.8.13-alpha.123" },
isProduction: true,
manifestVersion: 3,
}),
).toContainEntry(["version", "1.8.13.1123"]);
});

test("beta version", () => {
expect(
cleanCustomize(manifest, {
// eslint-disable-next-line camelcase -- auto-inserted
env: { ...process.env, npm_package_version: "1.8.13-beta.123" },
isProduction: true,
manifestVersion: 3,
}),
).toContainEntry(["version", "1.8.13.2123"]);
});

test("release version", () => {
expect(
cleanCustomize(manifest, {
// eslint-disable-next-line camelcase -- auto-inserted
env: { ...process.env, npm_package_version: "1.8.13" },
isProduction: true,
manifestVersion: 3,
}),
).toContainEntry(["version", "1.8.13.3000"]);
});
});
});
7 changes: 2 additions & 5 deletions src/background/backgroundPlatform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import type { NetworkRequestConfig } from "@/types/networkTypes";
import type { RemoteResponse } from "@/types/contract";
import { performConfiguredRequest } from "@/background/requests";
import BackgroundLogger from "@/telemetry/BackgroundLogger";
import { validateSemVerString } from "@/types/helpers";
import { PlatformBase } from "@/platform/platformBase";
import { getExtensionVersion } from "@/utils/extensionUtils";

/**
* Background platform implementation. Currently, just makes API requests.
Expand All @@ -40,10 +40,7 @@ class BackgroundPlatform extends PlatformBase {
});

constructor() {
super(
"background",
validateSemVerString(browser.runtime.getManifest().version),
);
super("background", getExtensionVersion());
}

override get logger(): PlatformProtocol["logger"] {
Expand Down
8 changes: 4 additions & 4 deletions src/background/deploymentUpdater.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
getModComponentState,
saveModComponentState,
} from "@/store/extensionsStorage";
import { uuidv4, validateSemVerString } from "@/types/helpers";
import { uuidv4, normalizeSemVerString } from "@/types/helpers";
import { appApiMock } from "@/testUtils/appApiMock";
import { omit } from "lodash";
import { syncDeployments } from "@/background/deploymentUpdater";
Expand Down Expand Up @@ -68,8 +68,8 @@ jest.mock("@/store/settings/settingsStorage");
jest.mock("@/hooks/useRefreshRegistries");

jest.mock("@/utils/extensionUtils", () => ({
...jest.requireActual("@/utils/extensionUtils"),
forEachTab: jest.fn(),
getExtensionVersion: () => browser.runtime.getManifest().version,
}));

// Override manual mock to support `expect` assertions
Expand Down Expand Up @@ -380,7 +380,7 @@ describe("syncDeployments", () => {
_recipe: {
id: deployment.package.package_id,
name: deployment.package.name,
version: validateSemVerString("0.0.1"),
version: normalizeSemVerString("0.0.1"),
updated_at: deployment.updated_at,
sharing: sharingDefinitionFactory(),
},
Expand Down Expand Up @@ -438,7 +438,7 @@ describe("syncDeployments", () => {
_recipe: {
id: deployment.package.package_id,
name: deployment.package.name,
version: validateSemVerString("0.0.1"),
version: normalizeSemVerString("0.0.1"),
updated_at: deployment.updated_at,
sharing: sharingDefinitionFactory(),
},
Expand Down
2 changes: 1 addition & 1 deletion src/background/deploymentUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ async function activateDeploymentsInBackground({
);

// Version to report to the server.
const { version: extensionVersionString } = browser.runtime.getManifest();
const extensionVersionString = getExtensionVersion();
const extensionVersion = parseSemVer(extensionVersionString);

const deploymentsByActivationMethod = await Promise.all(
Expand Down
9 changes: 6 additions & 3 deletions src/background/installer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ import {
import { Events } from "@/telemetry/events";
import { DEFAULT_SERVICE_URL, UNINSTALL_URL } from "@/urlConstants";
import { CONTROL_ROOM_TOKEN_INTEGRATION_ID } from "@/integrations/constants";
import { getExtensionConsoleUrl } from "@/utils/extensionUtils";
import {
getExtensionConsoleUrl,
getExtensionVersion,
} from "@/utils/extensionUtils";
import { oncePerSession } from "@/mv3/SessionStorage";
import { resetFeatureFlagsCache } from "@/auth/featureFlagStorage";

Expand Down Expand Up @@ -223,7 +226,7 @@ export async function showInstallPage({
// https://developer.chrome.com/docs/extensions/reference/runtime/#event-onInstalled
// https://developer.chrome.com/docs/extensions/reference/runtime/#type-OnInstalledReason
console.debug("onInstalled", { reason, previousVersion });
const { version } = browser.runtime.getManifest();
const version = getExtensionVersion();

if (reason === "install") {
void recordEvent({
Expand Down Expand Up @@ -309,7 +312,7 @@ export function getAvailableVersion(): typeof _availableVersion {
*/
export function isUpdateAvailable(): boolean {
const available = getAvailableVersion();
const installed = browser.runtime.getManifest().version;
const installed = getExtensionVersion();
return (
Boolean(available) && installed !== available && gt(available, installed)
);
Expand Down
7 changes: 6 additions & 1 deletion src/background/messenger/external/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,14 @@
import { _liftBackground as liftExternal } from "@/background/externalProtocol";
import * as local from "@/background/messenger/external/_implementation";
import { readPartnerAuthData } from "@/auth/authStorage";
import { getExtensionVersion } from "@/utils/extensionUtils";

export const connectPage = liftExternal("CONNECT_PAGE", async () =>
browser.runtime.getManifest(),
// Ensure the version we send to the app is a valid semver.
({
...browser.runtime.getManifest(),
version: getExtensionVersion(),
}),
);

export const setExtensionAuth = liftExternal(
Expand Down
1 change: 1 addition & 0 deletions src/background/restrictUnauthenticatedUrlAccess.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jest.mock("@/auth/authStorage", () => ({
}));

jest.mock("@/utils/extensionUtils", () => ({
...jest.requireActual("@/utils/extensionUtils"),
forEachTab: jest.fn(),
}));

Expand Down
13 changes: 6 additions & 7 deletions src/background/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { count as registrySize } from "@/registry/packageRegistry";
import { count as logSize } from "@/telemetry/logging";
import { count as traceSize } from "@/telemetry/trace";
import { getUUID } from "@/telemetry/telemetryHelpers";
import { getTabsWithAccess } from "@/utils/extensionUtils";
import { getExtensionVersion, getTabsWithAccess } from "@/utils/extensionUtils";
import { type Event } from "@/telemetry/events";

const EVENT_BUFFER_DEBOUNCE_MS = 2000;
Expand Down Expand Up @@ -274,7 +274,8 @@ export async function TEST_flushAll(): Promise<void> {

async function collectUserSummary(): Promise<UserSummary> {
const { os } = await browser.runtime.getPlatformInfo();
const { version, version_name: versionName } = browser.runtime.getManifest();
const { version_name: versionName } = browser.runtime.getManifest();
const version = getExtensionVersion();
// Not supported on Chromium, and may require additional permissions
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/getBrowserInfo
// const {name: browserName} = await browser.runtime.getBrowserInfo();
Expand Down Expand Up @@ -336,11 +337,9 @@ export async function recordEvent({
data: UnknownObject | undefined;
}): Promise<void> {
if (await allowsTrack()) {
const {
version,
version_name: versionName,
manifest_version: manifestVersion,
} = browser.runtime.getManifest();
const { version_name: versionName, manifest_version: manifestVersion } =
browser.runtime.getManifest();
const version = getExtensionVersion();
const telemetryEvent = {
uid: await getUUID(),
event,
Expand Down
Loading

0 comments on commit 1b2a1d9

Please sign in to comment.