Skip to content

Commit

Permalink
refactor: Allow getting manifest flags without setting
Browse files Browse the repository at this point in the history
The pieces of `set-manifest-flags.ts` related to _getting_ flags have
been moved to the new `get-manifest-flags.ts` module. This module will
be used in a later PR by a script run during a CI workflow.

The migrated steps were also made asynchronous so that the asynchronous
steps could be run in parallel rather than blocking the process.

Documentation has been added to the `ManifestFlags` type as well, in
preparation for adding a new property in the next PR.
  • Loading branch information
Gudahtt committed Dec 12, 2024
1 parent 4b48ee6 commit f3c0b67
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 84 deletions.
44 changes: 43 additions & 1 deletion app/scripts/lib/manifestFlags.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,59 @@
import browser from 'webextension-polyfill';

/**
* Flags that we use to control runtime behavior of the extension. Typically
* used for E2E tests.
*
* These flags are added to `manifest.json` for runtime querying.
*/
export type ManifestFlags = {
/**
* CircleCI metadata for the current run
*/
circleci?: {
/**
* Whether CircleCI manifest flags are enabled.
*/
enabled: boolean;
/**
* The name of the branch that triggered the current run on CircleCI
*/
branch?: string;
/**
* The current CircleCI build number
*/
buildNum?: number;
/**
* The name of the CircleCI job currently running
*/
job?: string;
/**
* For jobs with CircleCI parallelism enabled, this is the index of the current machine.
*/
nodeIndex?: number;
/**
* The pull request number of the pull request the current run was triggered by
*/
prNumber?: number;
};
/**
* Sentry flags
*/
sentry?: {
/**
* Override the performance trace sample rate
*/
tracesSampleRate?: number;
lazyLoadSubSampleRate?: number; // multiply by tracesSampleRate to get the actual probability
/**
* Sub-sample rate for lazy-loaded components.
*
* Multiply this rate by tracesSampleRate to get the actual probability of sampling the load
* time of for a lazy-loaded component.
*/
lazyLoadSubSampleRate?: number;
/**
* Force enable Sentry
*/
forceEnable?: boolean;
};
};
Expand Down
103 changes: 103 additions & 0 deletions development/lib/get-manifest-flag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import path from 'node:path';
import fs from 'node:fs/promises';
import { promisify } from 'node:util';
import { exec as callbackExec } from 'node:child_process';

import { hasProperty } from '@metamask/utils';
import { merge } from 'lodash';

import type { ManifestFlags } from '../../app/scripts/lib/manifestFlags';

const exec = promisify(callbackExec);
const PR_BODY_FILEPATH = path.resolve(
__dirname,
'..',
'..',
'changed-files',
'pr-body.txt',
);

/**
* Search a string for `flags = {...}` and return ManifestFlags if it exists
*
* @param str - The string to search
* @param errorType - The type of error to log if parsing fails
* @returns The ManifestFlags object if valid, otherwise undefined
*/
function regexSearchForFlags(str: string, errorType: string): ManifestFlags {
// Search str for `flags = {...}`
const flagsMatch = str.match(/flags\s*=\s*(\{.*\})/u);

if (flagsMatch) {
try {
// Get 1st capturing group from regex
return JSON.parse(flagsMatch[1]);
} catch (error) {
console.error(
`Error parsing flags from ${errorType}, ignoring flags\n`,
error,
);
}
}

return {};
}

/**
* Get flags from the GitHub PR body if they are set
*
* To use this feature, add a line to your PR body like:
* `flags = {"sentry": {"tracesSampleRate": 0.1}}`
* (must be valid JSON)
*
* @returns Any manifest flags found in the PR body
*/
async function getFlagsFromPrBody(): Promise<ManifestFlags> {
let body: string;
try {
body = await fs.readFile(PR_BODY_FILEPATH, 'utf8');
} catch (error) {
if (
error instanceof Error &&
hasProperty(error, 'code') &&
error.code === 'ENOENT'
) {
console.debug('No pr-body.txt, ignoring flags');
return {};
}
throw error;
}

return regexSearchForFlags(body, 'PR body');
}

/**
* Get flags from the Git message if they are set
*
* To use this feature, add a line to your commit message like:
* `flags = {"sentry": {"tracesSampleRate": 0.1}}`
* (must be valid JSON)
*
* @returns Any manifest flags found in the commit message
*/
async function getFlagsFromGitMessage(): Promise<ManifestFlags> {
const gitMessage = (
await exec(`git show --format='%B' --no-patch "HEAD"`)
).toString();

return regexSearchForFlags(gitMessage, 'git message');
}

/**
* Get any manifest flags found in the PR body and git message.
*
* @returns Any manifest flags found
*/
export async function getManifestFlags(): Promise<ManifestFlags> {
const [prBodyFlags, gitMessageFlags] = await Promise.all([
getFlagsFromPrBody(),
getFlagsFromGitMessage(),
]);

return merge(prBodyFlags, gitMessageFlags);
}
2 changes: 1 addition & 1 deletion test/e2e/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ async function withFixtures(options, testSuite) {
}
await mockServer.start(8000);

setManifestFlags(manifestFlags);
await setManifestFlags(manifestFlags);

driver = (await buildWebDriver(driverOptions)).driver;
webDriver = driver.driver;
Expand Down
86 changes: 4 additions & 82 deletions test/e2e/set-manifest-flags.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { execSync } from 'child_process';
import fs from 'fs';
import { merge } from 'lodash';
import { ManifestFlags } from '../../app/scripts/lib/manifestFlags';
import { getManifestFlags } from '../../development/lib/get-manifest-flag';

export const folder = `dist/${process.env.SELENIUM_BROWSER}`;

Expand All @@ -12,86 +12,8 @@ function parseIntOrUndefined(value: string | undefined): number | undefined {
return value ? parseInt(value, 10) : undefined;
}

/**
* Search a string for `flags = {...}` and return ManifestFlags if it exists
*
* @param str - The string to search
* @param errorType - The type of error to log if parsing fails
* @returns The ManifestFlags object if valid, otherwise undefined
*/
function regexSearchForFlags(
str: string,
errorType: string,
): ManifestFlags | undefined {
// Search str for `flags = {...}`
const flagsMatch = str.match(/flags\s*=\s*(\{.*\})/u);

if (flagsMatch) {
try {
// Get 1st capturing group from regex
return JSON.parse(flagsMatch[1]);
} catch (error) {
console.error(
`Error parsing flags from ${errorType}, ignoring flags\n`,
error,
);
}
}

return undefined;
}

/**
* Add flags from the GitHub PR body if they are set
*
* To use this feature, add a line to your PR body like:
* `flags = {"sentry": {"tracesSampleRate": 0.1}}`
* (must be valid JSON)
*
* @param flags - The flags object to add to
*/
function addFlagsFromPrBody(flags: ManifestFlags) {
let body;

try {
body = fs.readFileSync('changed-files/pr-body.txt', 'utf8');
} catch (error) {
console.debug('No pr-body.txt, ignoring flags');
return;
}

const newFlags = regexSearchForFlags(body, 'PR body');

if (newFlags) {
// Use lodash merge to do a deep merge (spread operator is shallow)
merge(flags, newFlags);
}
}

/**
* Add flags from the Git message if they are set
*
* To use this feature, add a line to your commit message like:
* `flags = {"sentry": {"tracesSampleRate": 0.1}}`
* (must be valid JSON)
*
* @param flags - The flags object to add to
*/
function addFlagsFromGitMessage(flags: ManifestFlags) {
const gitMessage = execSync(
`git show --format='%B' --no-patch "HEAD"`,
).toString();

const newFlags = regexSearchForFlags(gitMessage, 'git message');

if (newFlags) {
// Use lodash merge to do a deep merge (spread operator is shallow)
merge(flags, newFlags);
}
}

// Alter the manifest with CircleCI environment variables and custom flags
export function setManifestFlags(flags: ManifestFlags = {}) {
export async function setManifestFlags(flags: ManifestFlags = {}) {
if (process.env.CIRCLECI) {
flags.circleci = {
enabled: true,
Expand All @@ -104,8 +26,8 @@ export function setManifestFlags(flags: ManifestFlags = {}) {
),
};

addFlagsFromPrBody(flags);
addFlagsFromGitMessage(flags);
const additionalManifestFlags = await getManifestFlags();
merge(flags, additionalManifestFlags);

// Set `flags.sentry.forceEnable` to true by default
if (flags.sentry === undefined) {
Expand Down

0 comments on commit f3c0b67

Please sign in to comment.