Skip to content

Commit 298b977

Browse files
Merge branch 'main' into feat/5792-updates
2 parents cdc0dec + b7a496a commit 298b977

15 files changed

+511
-288
lines changed

.circleci/config.yml

+1-30
Original file line numberDiff line numberDiff line change
@@ -276,26 +276,6 @@ workflows:
276276
requires:
277277
- prep-deps
278278

279-
locales_only:
280-
when:
281-
matches:
282-
pattern: /^l10n_crowdin_action$/
283-
value: << pipeline.git.branch >>
284-
jobs:
285-
- prep-deps
286-
- get-changed-files-with-git-diff
287-
- validate-locales-only:
288-
requires:
289-
- prep-deps
290-
- get-changed-files-with-git-diff
291-
- test-lint:
292-
requires:
293-
- prep-deps
294-
- all-tests-pass:
295-
requires:
296-
- test-lint
297-
- validate-locales-only
298-
299279
jobs:
300280
create_release_pull_request:
301281
executor: node-browsers-medium
@@ -368,7 +348,7 @@ jobs:
368348
at: .
369349
- run:
370350
name: Get changed files with git diff
371-
command: yarn git-diff-default-branch
351+
command: yarn tsx .circleci/scripts/git-diff-default-branch.ts
372352
- persist_to_workspace:
373353
root: .
374354
paths:
@@ -377,15 +357,6 @@ jobs:
377357
path: changed-files
378358
destination: changed-files
379359

380-
validate-locales-only:
381-
executor: node-browsers-small
382-
steps:
383-
- run: *shallow-git-clone-and-enable-vnc
384-
- run: sudo corepack enable
385-
- attach_workspace:
386-
at: .
387-
- run: yarn tsx .circleci/scripts/validate-locales-only.ts
388-
389360
prep-build:
390361
executor: node-linux-medium
391362
steps:
+175
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import { exec as execCallback } from 'child_process';
2+
import fs from 'fs';
3+
import path from 'path';
4+
import { promisify } from 'util';
5+
import { context } from '@actions/github';
6+
import * as core from '@actions/core';
7+
8+
const exec = promisify(execCallback);
9+
10+
// Get PR number from GitHub Actions environment variables
11+
const PR_NUMBER = context.payload.pull_request?.number;
12+
13+
const GITHUB_DEFAULT_BRANCH = 'main';
14+
const SOURCE_BRANCH = PR_NUMBER ? `refs/pull/${PR_NUMBER}/head` : '';
15+
16+
const CHANGED_FILES_DIR = 'changed-files';
17+
18+
type PRInfo = {
19+
base: {
20+
ref: string;
21+
};
22+
body: string;
23+
labels: { name: string }[];
24+
};
25+
26+
/**
27+
* Get JSON info about the given pull request using Octokit
28+
*
29+
* @returns PR information from GitHub
30+
*/
31+
async function getPrInfo(): Promise<PRInfo | null> {
32+
if (!PR_NUMBER) {
33+
return null;
34+
}
35+
36+
const { owner, repo } = context.repo;
37+
38+
const response = await fetch(
39+
`https://api.github.com/repos/${owner}/${repo}/pulls/${PR_NUMBER}`,
40+
{
41+
headers: {
42+
Authorization: `token ${process.env.GITHUB_TOKEN}`,
43+
Accept: 'application/vnd.github.v3+json',
44+
},
45+
},
46+
);
47+
48+
return await response.json();
49+
}
50+
51+
/**
52+
* Fetches the git repository with a specified depth.
53+
*
54+
* @param depth - The depth to use for the fetch command.
55+
* @returns True if the fetch is successful, otherwise false.
56+
*/
57+
async function fetchWithDepth(depth: number): Promise<boolean> {
58+
try {
59+
await exec(`git fetch --depth ${depth} origin "${GITHUB_DEFAULT_BRANCH}"`);
60+
if (SOURCE_BRANCH) {
61+
await exec(
62+
`git fetch --depth ${depth} origin "${SOURCE_BRANCH}:${SOURCE_BRANCH}"`,
63+
);
64+
}
65+
return true;
66+
} catch (error) {
67+
core.warning(`Failed to fetch with depth ${depth}:`, error);
68+
return false;
69+
}
70+
}
71+
72+
/**
73+
* Attempts to fetch the necessary commits until the merge base is found.
74+
* It tries different fetch depths and performs a full fetch if needed.
75+
*
76+
* @throws If an unexpected error occurs during the execution of git commands.
77+
*/
78+
async function fetchUntilMergeBaseFound() {
79+
const depths = [1, 10, 100];
80+
for (const depth of depths) {
81+
core.info(`Attempting git diff with depth ${depth}...`);
82+
await fetchWithDepth(depth);
83+
84+
try {
85+
await exec(`git merge-base origin/${GITHUB_DEFAULT_BRANCH} HEAD`);
86+
return;
87+
} catch (error: unknown) {
88+
if (error instanceof Error && 'code' in error) {
89+
core.warning(
90+
`Error 'no merge base' encountered with depth ${depth}. Incrementing depth...`,
91+
);
92+
} else {
93+
throw error;
94+
}
95+
}
96+
}
97+
await exec(`git fetch --unshallow origin "${GITHUB_DEFAULT_BRANCH}"`);
98+
}
99+
100+
/**
101+
* Performs a git diff command to get the list of files changed between the current branch and the origin.
102+
* It first ensures that the necessary commits are fetched until the merge base is found.
103+
*
104+
* @returns The output of the git diff command, listing the file paths with status (A, M, D).
105+
* @throws If unable to get the diff after fetching the merge base or if an unexpected error occurs.
106+
*/
107+
async function gitDiff(): Promise<string> {
108+
await fetchUntilMergeBaseFound();
109+
const { stdout: diffResult } = await exec(
110+
`git diff --name-status "origin/${GITHUB_DEFAULT_BRANCH}...${
111+
SOURCE_BRANCH || 'HEAD'
112+
}"`,
113+
);
114+
if (!diffResult) {
115+
throw new Error('Unable to get diff after full checkout.');
116+
}
117+
return diffResult;
118+
}
119+
120+
function writePrBodyAndInfoToFile(prInfo: PRInfo) {
121+
const prBodyPath = path.resolve(CHANGED_FILES_DIR, 'pr-body.txt');
122+
const labels = prInfo.labels.map((label) => label.name).join(', ');
123+
const updatedPrBody = `PR labels: {${labels}}\nPR base: {${
124+
prInfo.base.ref
125+
}}\n${prInfo.body.trim()}`;
126+
fs.writeFileSync(prBodyPath, updatedPrBody);
127+
core.info(`PR body and info saved to ${prBodyPath}`);
128+
}
129+
130+
/**
131+
* Main run function, stores the output of git diff and the body of the matching PR to a file.
132+
*
133+
* @returns Returns a promise that resolves when the git diff output and PR body is successfully stored.
134+
*/
135+
async function storeGitDiffOutputAndPrBody() {
136+
try {
137+
// Create the directory
138+
fs.mkdirSync(CHANGED_FILES_DIR, { recursive: true });
139+
140+
core.info(`Determining whether to run git diff...`);
141+
if (!PR_NUMBER) {
142+
core.info('Not a PR, skipping git diff');
143+
return;
144+
}
145+
146+
const prInfo = await getPrInfo();
147+
148+
const baseRef = prInfo?.base.ref;
149+
if (!baseRef) {
150+
core.info('Not a PR, skipping git diff');
151+
return;
152+
}
153+
// We perform git diff even if the PR base is not main or skip-e2e-quality-gate label is applied
154+
// because we rely on the git diff results for other jobs
155+
core.info('Attempting to get git diff...');
156+
const diffOutput = await gitDiff();
157+
core.info(diffOutput);
158+
159+
// Store the output of git diff
160+
const outputPath = path.resolve(CHANGED_FILES_DIR, 'changed-files.txt');
161+
fs.writeFileSync(outputPath, diffOutput.trim());
162+
core.info(`Git diff results saved to ${outputPath}`);
163+
164+
writePrBodyAndInfoToFile(prInfo);
165+
166+
core.info('success');
167+
} catch (error: any) {
168+
core.setFailed(`Failed to process git diff: ${error.message}`);
169+
}
170+
}
171+
172+
// If main module (i.e. this is the TS file that was run directly)
173+
if (require.main === module) {
174+
storeGitDiffOutputAndPrBody();
175+
}

.github/workflows/create-release-pr.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ on:
1414
required: true
1515
jobs:
1616
create-release-pr:
17-
uses: MetaMask/github-tools/.github/workflows/create-release-pr.yml@4f594ca7d90378da66b7efce987eff379934524b
17+
uses: MetaMask/github-tools/.github/workflows/create-release-pr.yml@3e0b0204e41b576263b9060945de3b3b9b8c5448
1818
with:
1919
platform: extension
2020
base-branch: ${{ inputs.base-branch }}
@@ -23,6 +23,7 @@ jobs:
2323
secrets:
2424
# This token needs write permissions to metamask-extension & read permissions to metamask-planning
2525
github-token: ${{ secrets.PR_TOKEN }}
26+
google-application-creds-base64: ${{ secrets.GCP_RLS_SHEET_ACCOUNT_BASE64 }}
2627
permissions:
2728
contents: write
2829
pull-requests: write

.github/workflows/locales-only.yml

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Locales only
2+
3+
on:
4+
pull_request:
5+
types:
6+
- opened
7+
- reopened
8+
- synchronize
9+
10+
jobs:
11+
locales-only:
12+
# For the `pull_request` event, the branch is `github.head_ref``.
13+
if: ${{ github.head_ref == 'l10n_crowdin_action' }}
14+
runs-on: ubuntu-latest
15+
env:
16+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
17+
steps:
18+
- name: Checkout and setup environment
19+
uses: metamask/github-tools/.github/actions/checkout-and-setup@1299bb1de0c6974ae6d0a32c7e8897fe168239ac
20+
with:
21+
is-high-risk-environment: false
22+
23+
- name: Get changed files with git diff
24+
run: yarn tsx .github/scripts/git-diff-default-branch.ts
25+
26+
- name: Validate locales only
27+
run: yarn tsx .circleci/scripts/validate-locales-only.ts
28+
29+
- name: Run lint
30+
run: yarn lint && yarn verify-locales --quiet

.github/workflows/main.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ on:
1717
jobs:
1818
prep-deps:
1919
runs-on: ubuntu-latest
20+
if: ${{ github.head_ref != 'l10n_crowdin_action' }}
2021
steps:
2122
- name: Checkout and setup environment
2223
uses: metamask/github-tools/.github/actions/checkout-and-setup@1299bb1de0c6974ae6d0a32c7e8897fe168239ac
2324
with:
2425
is-high-risk-environment: false
25-
cache-node-modules: 'true'
26+
cache-node-modules: true
2627

2728
lint-workflows:
2829
name: Lint workflows

package.json

-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,6 @@
131131
"attributions:check": "./development/attributions-check.sh",
132132
"attributions:generate": "./development/generate-attributions.sh",
133133
"ci-rerun-from-failed": "tsx .circleci/scripts/rerun-ci-workflow-from-failed.ts",
134-
"git-diff-default-branch": "tsx .circleci/scripts/git-diff-default-branch.ts",
135134
"validate-e2e-page-object-usage": "tsx .github/scripts/validate-e2e-page-object-usage.ts"
136135
},
137136
"resolutions": {

test/e2e/flask/multichain-api/testHelpers.ts

+17
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,20 @@ export const passwordLockMetamaskExtension = async (
151151
*/
152152
export const escapeColon = (selector: string): string =>
153153
selector.replace(':', '\\:');
154+
155+
/**
156+
* Wraps a describe call in a skip call if the SELENIUM_BROWSER environment variable is not the specified browser.
157+
*
158+
* @param browser - The browser environment of the current test, against which to conditionally run or skip the test.
159+
* @param description - The description of the test suite.
160+
* @param callback - The callback function to execute the test suite.
161+
*/
162+
export const describeBrowserOnly = (
163+
browser: string,
164+
description: string,
165+
callback: () => void,
166+
) => {
167+
return process.env.SELENIUM_BROWSER === browser
168+
? describe(description, callback)
169+
: describe.skip(description, callback);
170+
};

test/e2e/flask/multichain-api/wallet_createSession.spec.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { strict as assert } from 'assert';
2-
import { By } from 'selenium-webdriver';
2+
import { Browser, By } from 'selenium-webdriver';
33
import { isObject } from 'lodash';
44
import {
55
largeDelayMs,
@@ -17,9 +17,10 @@ import {
1717
addAccountInWalletAndAuthorize,
1818
updateNetworkCheckboxes,
1919
type FixtureCallbackArgs,
20+
describeBrowserOnly,
2021
} from './testHelpers';
2122

22-
describe('Multichain API', function () {
23+
describeBrowserOnly(Browser.CHROME, 'Multichain API', function () {
2324
describe('Connect wallet to the multichain dapp via `externally_connectable`, call `wallet_createSession` with requested EVM scope that does NOT match one of the user’s enabled networks', function () {
2425
it("the specified EVM scopes that do not match the user's configured networks should be treated as if they were not requested", async function () {
2526
await withFixtures(

test/e2e/flask/multichain-api/wallet_getSession.spec.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import { strict as assert } from 'assert';
2+
import { Browser } from 'selenium-webdriver';
23
import { unlockWallet, withFixtures } from '../../helpers';
34
import FixtureBuilder from '../../fixture-builder';
45
import { DEFAULT_FIXTURE_ACCOUNT } from '../../constants';
56
import TestDappMultichain from '../../page-objects/pages/test-dapp-multichain';
67
import {
78
DEFAULT_MULTICHAIN_TEST_DAPP_FIXTURE_OPTIONS,
9+
describeBrowserOnly,
810
getExpectedSessionScope,
911
type FixtureCallbackArgs,
1012
} from './testHelpers';
1113

12-
describe('Multichain API', function () {
14+
describeBrowserOnly(Browser.CHROME, 'Multichain API', function () {
1315
describe('Connect wallet to the multichain dapp via `externally_connectable`, call `wallet_getSession` when there is no existing session', function () {
1416
it('should successfully receive empty session scopes', async function () {
1517
await withFixtures(

test/e2e/flask/multichain-api/wallet_invokeMethod.spec.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { strict as assert } from 'assert';
2+
import { Browser } from 'selenium-webdriver';
23
import {
34
ACCOUNT_1,
45
ACCOUNT_2,
@@ -14,11 +15,12 @@ import TestDappMultichain from '../../page-objects/pages/test-dapp-multichain';
1415
import {
1516
DEFAULT_MULTICHAIN_TEST_DAPP_FIXTURE_OPTIONS,
1617
addAccountInWalletAndAuthorize,
18+
describeBrowserOnly,
1719
escapeColon,
1820
type FixtureCallbackArgs,
1921
} from './testHelpers';
2022

21-
describe('Multichain API', function () {
23+
describeBrowserOnly(Browser.CHROME, 'Multichain API', function () {
2224
const GANACHE_SCOPES = ['eip155:1337', 'eip155:1338', 'eip155:1000'];
2325
const ACCOUNTS = [ACCOUNT_1, ACCOUNT_2];
2426
const DEFAULT_INITIAL_BALANCE_HEX = convertETHToHexGwei(

0 commit comments

Comments
 (0)