@applitools/core 4.72.2 → 4.73.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/dist/open-eyes.js +71 -4
- package/dist/utils/extract-git-info.js +5 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [4.73.0](https://github.com/Applitools-Dev/sdk/compare/js/core@4.72.2...js/core@4.73.0) (2026-10-18)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* read latestCommitInfo from git when cherry-pick lookup is on, log detection details | FLD-5129 ([#4293](https://github.com/Applitools-Dev/sdk/issues/4293)) ([0ec5dc8](https://github.com/Applitools-Dev/sdk/commit/0ec5dc86b4d78417cd2eb50cbaaf57d851556880))
|
|
9
|
+
|
|
3
10
|
## [4.72.2](https://github.com/Applitools-Dev/sdk/compare/js/core@4.72.1...js/core@4.72.2) (2026-10-18)
|
|
4
11
|
|
|
5
12
|
|
package/dist/open-eyes.js
CHANGED
|
@@ -40,6 +40,7 @@ const core_base_1 = require("@applitools/core-base");
|
|
|
40
40
|
const extract_git_info_1 = require("./utils/extract-git-info");
|
|
41
41
|
const types_1 = require("./types");
|
|
42
42
|
const reportedScmPreconditionFindings = new Map();
|
|
43
|
+
const reportedCherryPickDetections = new Set();
|
|
43
44
|
const scmPreconditionKey = (cwd, gitBranchName) => `${cwd} ${gitBranchName !== null && gitBranchName !== void 0 ? gitBranchName : ''}`;
|
|
44
45
|
const findingsHash = (findings) => findings
|
|
45
46
|
.map(finding => finding.code)
|
|
@@ -80,7 +81,10 @@ function makeOpenEyes({ type: defaultType = 'classic', clients, batch, removeDup
|
|
|
80
81
|
(_l = settings.parentBranchName) !== null && _l !== void 0 ? _l : (settings.parentBranchName = (_m = utils.general.getEnvValue('PARENT_BRANCH')) !== null && _m !== void 0 ? _m : utils.general.getEnvValue('PARENT_BRANCH_NAME'));
|
|
81
82
|
(_o = settings.baselineBranchName) !== null && _o !== void 0 ? _o : (settings.baselineBranchName = (_p = utils.general.getEnvValue('BASELINE_BRANCH')) !== null && _p !== void 0 ? _p : utils.general.getEnvValue('BASELINE_BRANCH_NAME'));
|
|
82
83
|
(_q = settings.gitBranchingTimestamp) !== null && _q !== void 0 ? _q : (settings.gitBranchingTimestamp = utils.general.getEnvValue('GIT_MERGE_BASE_TIMESTAMP'));
|
|
84
|
+
let latestCommitInfoSource = settings.latestCommitInfo ? 'settings' : undefined;
|
|
83
85
|
(_r = settings.latestCommitInfo) !== null && _r !== void 0 ? _r : (settings.latestCommitInfo = getLatestCommitInfoFromEnvVars(logger));
|
|
86
|
+
if (settings.latestCommitInfo)
|
|
87
|
+
latestCommitInfoSource !== null && latestCommitInfoSource !== void 0 ? latestCommitInfoSource : (latestCommitInfoSource = 'env vars');
|
|
84
88
|
(_s = settings.ufgServerUrl) !== null && _s !== void 0 ? _s : (settings.ufgServerUrl = utils.general.getEnvValue('UFG_SERVER_URL'));
|
|
85
89
|
(_t = settings.ignoreBaseline) !== null && _t !== void 0 ? _t : (settings.ignoreBaseline = false);
|
|
86
90
|
(_u = settings.compareWithParentBranch) !== null && _u !== void 0 ? _u : (settings.compareWithParentBranch = false);
|
|
@@ -90,6 +94,9 @@ function makeOpenEyes({ type: defaultType = 'classic', clients, batch, removeDup
|
|
|
90
94
|
logger.warn(`latestCommitInfo.timestamp is an invalid ISO date string: ${settings.latestCommitInfo.timestamp}`);
|
|
91
95
|
settings.latestCommitInfo = undefined;
|
|
92
96
|
}
|
|
97
|
+
if (settings.latestCommitInfo) {
|
|
98
|
+
logger.log(`Using latestCommitInfo from ${latestCommitInfoSource !== null && latestCommitInfoSource !== void 0 ? latestCommitInfoSource : 'git'}`, JSON.stringify(settings.latestCommitInfo));
|
|
99
|
+
}
|
|
93
100
|
// Bare git refs from the scm integration. Kept out of `settings` because that pair is the
|
|
94
101
|
// session's identity on the wire; these are only a `git merge-base` argument.
|
|
95
102
|
let scmGitRefs;
|
|
@@ -134,7 +141,7 @@ function makeOpenEyes({ type: defaultType = 'classic', clients, batch, removeDup
|
|
|
134
141
|
}
|
|
135
142
|
}
|
|
136
143
|
const account = await core.getAccountInfo({ settings: eyesServerSettings, logger });
|
|
137
|
-
await handleBranchFallbackList(account, settings, logger);
|
|
144
|
+
await handleBranchFallbackList(account, settings, latestCommitInfoSource, logger);
|
|
138
145
|
const componentConcurrency = account.serverConcurrency.componentConcurrency;
|
|
139
146
|
settings.sessionConcurrency = handleConcurrency({
|
|
140
147
|
userConcurrency: concurrency,
|
|
@@ -225,8 +232,9 @@ function makeOpenEyes({ type: defaultType = 'classic', clients, batch, removeDup
|
|
|
225
232
|
}
|
|
226
233
|
}
|
|
227
234
|
};
|
|
228
|
-
async function handleBranchFallbackList(account, settings, logger) {
|
|
229
|
-
var _a, _b, _c;
|
|
235
|
+
async function handleBranchFallbackList(account, settings, latestCommitInfoSource, logger) {
|
|
236
|
+
var _a, _b, _c, _d;
|
|
237
|
+
logger.log('SCM settings from the server', JSON.stringify(account.scmSettings));
|
|
230
238
|
const scm = getScmSettings(account.scmSettings);
|
|
231
239
|
const integrationActive = !settings.ignoreGitBranching && scm.lookupFallbackList;
|
|
232
240
|
// Extract gitBranchName (the actual git branch name)
|
|
@@ -242,7 +250,7 @@ function makeOpenEyes({ type: defaultType = 'classic', clients, batch, removeDup
|
|
|
242
250
|
// logger.log(`Using branch name: ${settings.branchName}`)
|
|
243
251
|
}
|
|
244
252
|
else {
|
|
245
|
-
logger.log(`Skipping extraction of git branch name
|
|
253
|
+
logger.log(`Skipping extraction of git branch name: ignoreGitBranching=${settings.ignoreGitBranching}, fallback list enabled=${scm.lookupFallbackList}`);
|
|
246
254
|
}
|
|
247
255
|
// No branch name means the extractor never runs, so probe the env here instead.
|
|
248
256
|
if (integrationActive && !settings.gitBranchName) {
|
|
@@ -266,7 +274,20 @@ function makeOpenEyes({ type: defaultType = 'classic', clients, batch, removeDup
|
|
|
266
274
|
settings.branchLookupFallbackList = outcome.list;
|
|
267
275
|
// Walks from the same tip as latestCommitInfo (the PR head on pull_request events): the server compares the
|
|
268
276
|
// pick positions with a cutoff taken from it.
|
|
277
|
+
let envLatestCommitSha;
|
|
269
278
|
if (((_c = outcome.list) === null || _c === void 0 ? void 0 : _c.length) && settings.latestCommitInfo) {
|
|
279
|
+
// The env vars are often set once per CI pipeline and can point below the picks; git knows the real tip.
|
|
280
|
+
if (account.scmSettings.isCherryPickEnabled && latestCommitInfoSource === 'env vars') {
|
|
281
|
+
const gitCommitInfo = await (0, extract_git_info_1.extractLatestCommitInfo)({ execOptions: { cwd }, logger });
|
|
282
|
+
if (gitCommitInfo) {
|
|
283
|
+
logger.log(`Cherry-pick lookup is enabled for this account, so APPLITOOLS_GIT_LATEST_COMMIT_SHA/TIMESTAMP (${settings.latestCommitInfo.sha}) are ignored and latestCommitInfo is read from git`, JSON.stringify(gitCommitInfo));
|
|
284
|
+
envLatestCommitSha = settings.latestCommitInfo.sha;
|
|
285
|
+
settings.latestCommitInfo = gitCommitInfo;
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
logger.log(`Cherry-pick lookup is enabled for this account, but git returned no commit, so latestCommitInfo stays on APPLITOOLS_GIT_LATEST_COMMIT_SHA (${settings.latestCommitInfo.sha})`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
270
291
|
const cherryPicks = await (0, extract_git_info_1.extractCherryPicks)({
|
|
271
292
|
headSha: settings.latestCommitInfo.sha,
|
|
272
293
|
fallbackBranchNames: outcome.list.map(branch => branch.branchName),
|
|
@@ -275,6 +296,31 @@ function makeOpenEyes({ type: defaultType = 'classic', clients, batch, removeDup
|
|
|
275
296
|
});
|
|
276
297
|
if (cherryPicks === null || cherryPicks === void 0 ? void 0 : cherryPicks.length)
|
|
277
298
|
settings.cherryPicks = cherryPicks;
|
|
299
|
+
if (account.scmSettings.isCherryPickEnabled) {
|
|
300
|
+
reportCherryPickDetection({
|
|
301
|
+
account,
|
|
302
|
+
settings,
|
|
303
|
+
event: {
|
|
304
|
+
status: cherryPicks ? 'detected' : 'failed',
|
|
305
|
+
latestCommitInfoSource: envLatestCommitSha ? 'git' : latestCommitInfoSource !== null && latestCommitInfoSource !== void 0 ? latestCommitInfoSource : 'git',
|
|
306
|
+
latestCommitSha: settings.latestCommitInfo.sha,
|
|
307
|
+
envLatestCommitSha,
|
|
308
|
+
cherryPicks: cherryPicks === null || cherryPicks === void 0 ? void 0 : cherryPicks.map(({ pickedSha, originalSha, originalBranch }) => ({
|
|
309
|
+
pickedSha,
|
|
310
|
+
originalSha,
|
|
311
|
+
originalBranch,
|
|
312
|
+
})),
|
|
313
|
+
},
|
|
314
|
+
logger,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
const reason = ((_d = outcome.list) === null || _d === void 0 ? void 0 : _d.length) ? 'no latestCommitInfo' : 'empty fallback list';
|
|
320
|
+
logger.log(`Cherry-pick detection did not run: ${reason}`);
|
|
321
|
+
if (account.scmSettings.isCherryPickEnabled) {
|
|
322
|
+
reportCherryPickDetection({ account, settings, event: { status: 'skipped', reason }, logger });
|
|
323
|
+
}
|
|
278
324
|
}
|
|
279
325
|
if (outcome.status === 'precondition-failure') {
|
|
280
326
|
reportScmPreconditionFailure({
|
|
@@ -295,6 +341,27 @@ function makeOpenEyes({ type: defaultType = 'classic', clients, batch, removeDup
|
|
|
295
341
|
reportedScmPreconditionFindings.set(key, hash);
|
|
296
342
|
emitScmPreconditionFailureEvent({ account, settings, findings, defaultBranch, logger });
|
|
297
343
|
}
|
|
344
|
+
function reportCherryPickDetection({ account, settings, event, logger, }) {
|
|
345
|
+
var _a, _b;
|
|
346
|
+
const key = `${cwd} ${settings.gitBranchName} ${(_a = settings.latestCommitInfo) === null || _a === void 0 ? void 0 : _a.sha}`;
|
|
347
|
+
if (reportedCherryPickDetections.has(key))
|
|
348
|
+
return;
|
|
349
|
+
reportedCherryPickDetections.add(key);
|
|
350
|
+
void core.logEvent({
|
|
351
|
+
settings: {
|
|
352
|
+
...account.eyesServer,
|
|
353
|
+
level: event.envLatestCommitSha ? 'Warn' : 'Notice',
|
|
354
|
+
event: {
|
|
355
|
+
type: 'scmCherryPickDetection',
|
|
356
|
+
gitBranchName: settings.gitBranchName,
|
|
357
|
+
...event,
|
|
358
|
+
batchId: (_b = settings.batch) === null || _b === void 0 ? void 0 : _b.id,
|
|
359
|
+
userTestId: settings.userTestId,
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
logger,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
298
365
|
function emitScmPreconditionFailureEvent({ account, settings, findings, defaultBranch, logger, }) {
|
|
299
366
|
var _a;
|
|
300
367
|
void core.logEvent({
|
|
@@ -1732,7 +1732,7 @@ function parsePickCandidate(record) {
|
|
|
1732
1732
|
const [pickedSha, pickedCommitTime, body = ''] = record.replace(/^\n/, '').split('\x1f');
|
|
1733
1733
|
// Hand-edited lines are not validated; the first line is trusted.
|
|
1734
1734
|
const originalSha = (_a = body.match(CHERRY_PICK_LINE)) === null || _a === void 0 ? void 0 : _a[1];
|
|
1735
|
-
return originalSha ? { pickedSha, pickedCommitTime, originalSha } : undefined;
|
|
1735
|
+
return originalSha ? { pickedSha, pickedCommitTime, originalSha, subject: body.split('\n')[0].trim() } : undefined;
|
|
1736
1736
|
}
|
|
1737
1737
|
async function collectPickCandidates(git, branches, headSha, logger) {
|
|
1738
1738
|
const maxCommits = cherryPickLimit('CHERRY_PICK_MAX_COMMITS', 1000);
|
|
@@ -1745,6 +1745,7 @@ async function collectPickCandidates(git, branches, headSha, logger) {
|
|
|
1745
1745
|
const records = log.stdout.split('\x1e').filter(record => record.trim());
|
|
1746
1746
|
if (records.length === maxCommits)
|
|
1747
1747
|
logger.log(`Cherry-pick detection read only the newest ${maxCommits} commits of ${range}`);
|
|
1748
|
+
logger.log(`Cherry-pick detection read ${records.length} commits of ${range}`);
|
|
1748
1749
|
return records;
|
|
1749
1750
|
});
|
|
1750
1751
|
const candidates = new Map();
|
|
@@ -1779,18 +1780,19 @@ async function findContainingBranch(git, sha, branches) {
|
|
|
1779
1780
|
async function resolvePick(git, candidate, branches, logger) {
|
|
1780
1781
|
const originalSha = await resolveCommit(git, candidate.originalSha);
|
|
1781
1782
|
if (!originalSha) {
|
|
1782
|
-
logger.log(`Cherry-pick ${candidate.pickedSha} dropped: original ${candidate.originalSha} is not in the local history`);
|
|
1783
|
+
logger.log(`Cherry-pick ${candidate.pickedSha} "${candidate.subject}" dropped: original ${candidate.originalSha} is not in the local history`);
|
|
1783
1784
|
return undefined;
|
|
1784
1785
|
}
|
|
1785
1786
|
const originalBranch = await findContainingBranch(git, originalSha, branches);
|
|
1786
1787
|
if (!originalBranch) {
|
|
1787
|
-
logger.log(`Cherry-pick ${candidate.pickedSha} dropped: original ${originalSha} is not on a fallback-list branch`);
|
|
1788
|
+
logger.log(`Cherry-pick ${candidate.pickedSha} "${candidate.subject}" dropped: original ${originalSha} is not on a fallback-list branch`);
|
|
1788
1789
|
return undefined;
|
|
1789
1790
|
}
|
|
1790
1791
|
const shown = await git(['show', '-s', '--format=%aI', originalSha]);
|
|
1791
1792
|
const originalCommitTime = shown.stdout.trim();
|
|
1792
1793
|
if (shown.code !== 0 || !isISODate(originalCommitTime))
|
|
1793
1794
|
throw new Error(`no commit time for ${originalSha}`);
|
|
1795
|
+
logger.log(`Cherry-pick ${candidate.pickedSha} "${candidate.subject}" is a pick of ${originalSha} on ${originalBranch}`);
|
|
1794
1796
|
return {
|
|
1795
1797
|
pickedSha: candidate.pickedSha,
|
|
1796
1798
|
pickedCommitTime: candidate.pickedCommitTime,
|