browser-commander 0.25.0 → 0.26.1
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/.jscpd-baseline.json +6 -4
- package/CHANGELOG.md +32 -0
- package/package.json +2 -2
- package/scripts/check-duplication.mjs +176 -0
- package/scripts/create-manual-changeset.mjs +3 -1
- package/scripts/format-release-notes.mjs +8 -3
- package/scripts/merge-changesets.mjs +4 -1
- package/scripts/validate-changeset.mjs +23 -10
- package/scripts/version-and-commit.mjs +8 -10
- package/src/browser/browser-sources.json +10 -2
- package/src/browser/launcher.js +14 -0
- package/src/browser/real-browser.js +5 -0
- package/src/browser/safari-launch.js +42 -0
- package/src/browser/safari-support.js +98 -0
- package/src/browser/storage-state.js +28 -8
- package/src/browser/webdriver.js +132 -21
- package/src/cli/sessions.js +5 -0
- package/src/core/webdriver-page.js +62 -0
- package/src/elements/selectors.js +31 -14
- package/src/exports.js +6 -0
- package/src/parity/harness.js +4 -1
- package/src/tests/index.js +12 -1
- package/src/traces/recorder.js +5 -0
- package/tests/e2e/engine-matrix.e2e.test.js +2 -1
- package/tests/e2e/safari.e2e.test.js +105 -0
- package/tests/e2e/webdriver.e2e.test.js +2 -1
- package/tests/helpers/fixture-server.js +18 -1
- package/tests/unit/browser/real-browser.test.js +1 -1
- package/tests/unit/browser/safari.test.js +192 -0
- package/tests/unit/browser/webdriver.test.js +148 -3
- package/tests/unit/cli/dispatcher.test.js +1 -1
- package/tests/unit/elements/selectors.test.js +75 -0
- package/tests/unit/helpers/fixture-server.test.js +60 -0
- package/tests/unit/playwright-protocol-coverage.test.js +9 -6
- package/tests/unit/puppeteer-bindings-coverage.test.js +28 -7
- package/tests/unit/scripts/bot-commit-attribution.test.js +70 -0
- package/tests/unit/scripts/check-duplication.test.js +139 -0
- package/tests/unit/scripts/check-pipeline-status.test.js +264 -52
- package/tests/unit/scripts/check-version-modification.test.js +107 -1
- package/tests/unit/scripts/ci-timeout-budgets.test.js +15 -2
- package/tests/unit/scripts/ci-workflow-policy.test.js +91 -7
- package/tests/unit/scripts/command-stream-errexit.test.js +83 -41
- package/tests/unit/scripts/e2e-suite-coverage.test.js +174 -0
- package/tests/unit/scripts/github-actions-log.test.js +99 -0
- package/tests/unit/scripts/links-workflow.test.js +42 -0
- package/tests/unit/scripts/preflight-credentials.test.js +527 -0
- package/tests/unit/scripts/run-with-budget-warning.test.js +212 -6
- package/tests/unit/scripts/use-module-integration.test.js +2 -9
- package/tests/unit/scripts/use-module.test.js +20 -0
- package/tests/unit/scripts/validate-changeset.test.js +127 -0
- package/tests/unit/scripts/zizmor-version.test.js +82 -0
- package/tests/unit/tests/index.test.js +43 -0
package/.jscpd-baseline.json
CHANGED
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
"fingerprints": {
|
|
4
4
|
"00d381dd611ae8ac": 1,
|
|
5
5
|
"029a62ef1f48e856": 1,
|
|
6
|
-
"03241c1f33f40d14": 1,
|
|
7
6
|
"038170e72e0af1f5": 1,
|
|
7
|
+
"0413f601c8911a92": 1,
|
|
8
8
|
"04b28d01d0bb47dc": 1,
|
|
9
9
|
"04f605be138bc350": 1,
|
|
10
10
|
"0796d358ad2562c3": 1,
|
|
11
|
+
"080e47b73f48a044": 1,
|
|
11
12
|
"08e28c7706cdddd6": 1,
|
|
12
13
|
"090a69d9528e4cdb": 1,
|
|
13
14
|
"09694dc8fb9d683c": 1,
|
|
@@ -30,6 +31,7 @@
|
|
|
30
31
|
"17c26c0db2d35ad1": 1,
|
|
31
32
|
"193b46c0e7f72053": 1,
|
|
32
33
|
"19ff70fc80fad524": 1,
|
|
34
|
+
"1a9f0f0f68ed445c": 1,
|
|
33
35
|
"1ad4dc7e19a77f68": 1,
|
|
34
36
|
"1c1baf1bc8ea0eae": 1,
|
|
35
37
|
"1d61f5c49f12d2dc": 1,
|
|
@@ -79,7 +81,6 @@
|
|
|
79
81
|
"4715a17d690865ee": 1,
|
|
80
82
|
"4b0d3cc5c27227e7": 1,
|
|
81
83
|
"4c3b94caa28045d0": 1,
|
|
82
|
-
"4e090d6bb30e6551": 1,
|
|
83
84
|
"4f3c541b3f3e3f23": 1,
|
|
84
85
|
"4f93b074bc2ac9e8": 1,
|
|
85
86
|
"5085765e1f103f3e": 1,
|
|
@@ -114,6 +115,7 @@
|
|
|
114
115
|
"6880d620e0e67229": 1,
|
|
115
116
|
"69d50e6f6a5f2ee9": 1,
|
|
116
117
|
"6a7ed9ad3fc00a39": 1,
|
|
118
|
+
"6a9854bcdc9f71d7": 1,
|
|
117
119
|
"6af3c307939ef75e": 1,
|
|
118
120
|
"6b35b48512756192": 1,
|
|
119
121
|
"6cd00d6b6a039022": 1,
|
|
@@ -182,7 +184,7 @@
|
|
|
182
184
|
"acc95d694eb2f581": 1,
|
|
183
185
|
"ad4cfa76daa1b6fc": 1,
|
|
184
186
|
"ad50b48938af8aef": 1,
|
|
185
|
-
"
|
|
187
|
+
"add45ff3644fbeeb": 1,
|
|
186
188
|
"aee1e5c65f783848": 1,
|
|
187
189
|
"aee45e8b58c2d87f": 1,
|
|
188
190
|
"af702b20ebd45ef2": 1,
|
|
@@ -246,13 +248,13 @@
|
|
|
246
248
|
"e5feedc0c24e103e": 1,
|
|
247
249
|
"e67726a913675120": 1,
|
|
248
250
|
"e686288f2e9b8a6d": 1,
|
|
251
|
+
"e7d7bb7e163d2327": 1,
|
|
249
252
|
"ea1195048ded896c": 1,
|
|
250
253
|
"ea88711c5deb1fe5": 1,
|
|
251
254
|
"eb9a70c2a21094ff": 1,
|
|
252
255
|
"ebe03c5f8c2f5147": 1,
|
|
253
256
|
"ee19c8b9b332ee98": 1,
|
|
254
257
|
"eeca0ccf8d821f21": 1,
|
|
255
|
-
"f022b763ff342b2d": 1,
|
|
256
258
|
"f07f979f8c2706e8": 1,
|
|
257
259
|
"f186f73c118b8e8a": 1,
|
|
258
260
|
"f38f72095a67c95e": 1,
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.26.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- f2988a3: Close the parity probe server only after it stops listening, so a connection
|
|
8
|
+
that arrives during shutdown can no longer keep `close()` waiting. Test fixture
|
|
9
|
+
servers now drop idle browser preconnections too, which stopped the Safari
|
|
10
|
+
smoke test from timing out during cleanup on Node 24.
|
|
11
|
+
|
|
12
|
+
Parse Playwright `:has-text("…")` and `:text-is("…")` selectors with string
|
|
13
|
+
operations instead of a backtracking regular expression. A long selector that
|
|
14
|
+
did not match took quadratic time (16 seconds for 550,000 characters); it is
|
|
15
|
+
now rejected in linear time. CodeQL reported it as `js/polynomial-redos`.
|
|
16
|
+
|
|
17
|
+
Failure artifact names are trimmed of leading and trailing dashes by index, so
|
|
18
|
+
a test name with a long run of dashes no longer takes quadratic time.
|
|
19
|
+
|
|
20
|
+
A Safari launch right after a previous Safari session closed starts a fresh
|
|
21
|
+
`safaridriver`, up to three attempts, when the driver exits before it is ready
|
|
22
|
+
or refuses the connection. safaridriver serves one automation session at a
|
|
23
|
+
time, and CI saw both failures on back-to-back launches. A refused connection
|
|
24
|
+
now also names the driver server and whether it had exited. Authorization
|
|
25
|
+
errors are still reported at once. Set `VERBOSE=1` to log each retry.
|
|
26
|
+
|
|
27
|
+
## 0.26.0
|
|
28
|
+
|
|
29
|
+
### Minor Changes
|
|
30
|
+
|
|
31
|
+
- 8f9ef1c: Add native Safari and Technology Preview W3C WebDriver control through real/common launchers and CLI, with isolated cookie seeding, setup diagnostics and typed unsupported feature errors.
|
|
32
|
+
|
|
33
|
+
Update the transitive shell-quote lock entry to fix GHSA-pqg4-j6r4-53mv and unblock npm audit.
|
|
34
|
+
|
|
3
35
|
## 0.25.0
|
|
4
36
|
|
|
5
37
|
### Minor Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "browser-commander",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.1",
|
|
4
4
|
"description": "Universal browser automation library that supports Playwright, Puppeteer, and Selenium with a unified API",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"format": "prettier --write .",
|
|
33
33
|
"format:check": "prettier --check .",
|
|
34
34
|
"docs:api": "jsdoc -c jsdoc.json",
|
|
35
|
-
"check:duplication": "
|
|
35
|
+
"check:duplication": "node scripts/check-duplication.mjs",
|
|
36
36
|
"check": "npm run lint && npm run format:check && npm run check:duplication",
|
|
37
37
|
"changeset": "changeset",
|
|
38
38
|
"changeset:version": "node scripts/changeset-version.mjs",
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Duplication gate that prints the clones it fails on, and only those.
|
|
5
|
+
*
|
|
6
|
+
* With the console reporter, `jscpd --baseline … --fail-on-new-clones` lists
|
|
7
|
+
* every clone it finds, accepted or not. A passing run printed 272 "Clone
|
|
8
|
+
* found" blocks, about 840 lines, for clones the baseline already accepts. The
|
|
9
|
+
* one clone that failed a run was marked only by a `[NEW]` tag somewhere in
|
|
10
|
+
* that list (issue #128). This runs jscpd with the JSON reporter instead. It
|
|
11
|
+
* prints the new clones from the report (as error annotations in GitHub
|
|
12
|
+
* Actions) and a one-line total.
|
|
13
|
+
*
|
|
14
|
+
* `npx jscpd .` still prints every clone, and `.jscpd.json` keeps the console
|
|
15
|
+
* reporter for that.
|
|
16
|
+
*
|
|
17
|
+
* Usage (from js/): node scripts/check-duplication.mjs
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { spawnSync } from 'node:child_process';
|
|
21
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
22
|
+
import { createRequire } from 'node:module';
|
|
23
|
+
import { tmpdir } from 'node:os';
|
|
24
|
+
import path from 'node:path';
|
|
25
|
+
import { fileURLToPath } from 'node:url';
|
|
26
|
+
|
|
27
|
+
const BASELINE = '.jscpd-baseline.json';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Path to jscpd's CLI entry point, or null when it is not installed. Running
|
|
31
|
+
* it through `process.execPath` avoids the `.bin` shims, which Windows cannot
|
|
32
|
+
* execute directly.
|
|
33
|
+
*/
|
|
34
|
+
export function resolveJscpdCli() {
|
|
35
|
+
const require = createRequire(import.meta.url);
|
|
36
|
+
let manifestPath;
|
|
37
|
+
try {
|
|
38
|
+
manifestPath = require.resolve('jscpd/package.json');
|
|
39
|
+
} catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const { bin } = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
43
|
+
const entry = typeof bin === 'string' ? bin : bin?.jscpd;
|
|
44
|
+
const cli = entry && path.join(path.dirname(manifestPath), entry);
|
|
45
|
+
return cli && existsSync(cli) ? cli : null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function escapeProperty(value) {
|
|
49
|
+
return String(value)
|
|
50
|
+
.replaceAll('%', '%25')
|
|
51
|
+
.replaceAll('\r', '%0D')
|
|
52
|
+
.replaceAll('\n', '%0A')
|
|
53
|
+
.replaceAll(':', '%3A')
|
|
54
|
+
.replaceAll(',', '%2C');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function escapeMessage(value) {
|
|
58
|
+
return String(value)
|
|
59
|
+
.replaceAll('%', '%25')
|
|
60
|
+
.replaceAll('\r', '%0D')
|
|
61
|
+
.replaceAll('\n', '%0A');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The new clones in a jscpd JSON report, one line each.
|
|
66
|
+
*
|
|
67
|
+
* jscpd names code embedded in Markdown `README.md:javascript`, so the
|
|
68
|
+
* language suffix is dropped before the name is used as a path. In GitHub
|
|
69
|
+
* Actions each clone becomes an `::error` annotation on the
|
|
70
|
+
* repository-relative path. `cwd` is where jscpd ran, and `workspace` is the
|
|
71
|
+
* checkout root.
|
|
72
|
+
*
|
|
73
|
+
* @param {{duplicates: Array<object>}} report
|
|
74
|
+
* @param {{githubActions?: boolean, cwd?: string, workspace?: string}} [options]
|
|
75
|
+
*/
|
|
76
|
+
export function formatNewClones(report, options = {}) {
|
|
77
|
+
const {
|
|
78
|
+
githubActions = false,
|
|
79
|
+
cwd = process.cwd(),
|
|
80
|
+
workspace = cwd,
|
|
81
|
+
} = options;
|
|
82
|
+
const fileOf = (side) => {
|
|
83
|
+
const name = side.name.replace(/:[a-z]+$/, '');
|
|
84
|
+
return githubActions
|
|
85
|
+
? path
|
|
86
|
+
.relative(workspace, path.resolve(cwd, name))
|
|
87
|
+
.split(path.sep)
|
|
88
|
+
.join('/')
|
|
89
|
+
: name;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return report.duplicates
|
|
93
|
+
.filter((duplicate) => duplicate.isNew)
|
|
94
|
+
.map(({ firstFile, secondFile, lines }) => {
|
|
95
|
+
const first = fileOf(firstFile);
|
|
96
|
+
const second = `${fileOf(secondFile)}:${secondFile.start}-${secondFile.end}`;
|
|
97
|
+
if (!githubActions) {
|
|
98
|
+
return `New clone: ${first}:${firstFile.start}-${firstFile.end} ~ ${second} (${lines} lines)\n`;
|
|
99
|
+
}
|
|
100
|
+
const properties = [
|
|
101
|
+
`file=${escapeProperty(first)}`,
|
|
102
|
+
`line=${firstFile.start}`,
|
|
103
|
+
`endLine=${firstFile.end}`,
|
|
104
|
+
'title=New duplicated code',
|
|
105
|
+
].join(',');
|
|
106
|
+
const message =
|
|
107
|
+
`${lines} lines duplicate ${second}. Extract the shared code, or ` +
|
|
108
|
+
'accept the clone with `npm run check:duplication:update`.';
|
|
109
|
+
return `::error ${properties}::${escapeMessage(message)}\n`;
|
|
110
|
+
})
|
|
111
|
+
.join('');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function main() {
|
|
115
|
+
const cli = resolveJscpdCli();
|
|
116
|
+
if (!cli) {
|
|
117
|
+
console.error('jscpd is not installed; run npm ci in js/.');
|
|
118
|
+
return 2;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const output = mkdtempSync(path.join(tmpdir(), 'jscpd-report-'));
|
|
122
|
+
try {
|
|
123
|
+
const jscpd = spawnSync(
|
|
124
|
+
process.execPath,
|
|
125
|
+
[
|
|
126
|
+
cli,
|
|
127
|
+
'.',
|
|
128
|
+
'--baseline',
|
|
129
|
+
BASELINE,
|
|
130
|
+
'--fail-on-new-clones',
|
|
131
|
+
'--reporters',
|
|
132
|
+
'json',
|
|
133
|
+
'--output',
|
|
134
|
+
output,
|
|
135
|
+
'--no-colors',
|
|
136
|
+
],
|
|
137
|
+
{ encoding: 'utf8' }
|
|
138
|
+
);
|
|
139
|
+
const reportPath = path.join(output, 'jscpd-report.json');
|
|
140
|
+
if (jscpd.error || !existsSync(reportPath)) {
|
|
141
|
+
// Fail closed: a run that produced no report proved nothing.
|
|
142
|
+
process.stdout.write(jscpd.stdout ?? '');
|
|
143
|
+
process.stderr.write(jscpd.stderr ?? '');
|
|
144
|
+
console.error(
|
|
145
|
+
`jscpd wrote no report (exit ${jscpd.status}): ${jscpd.error?.message ?? reportPath}`
|
|
146
|
+
);
|
|
147
|
+
return jscpd.status || 2;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const report = JSON.parse(readFileSync(reportPath, 'utf8'));
|
|
151
|
+
const newCount = report.duplicates.filter((d) => d.isNew).length;
|
|
152
|
+
process.stdout.write(
|
|
153
|
+
formatNewClones(report, {
|
|
154
|
+
githubActions: process.env.GITHUB_ACTIONS === 'true',
|
|
155
|
+
workspace: process.env.GITHUB_WORKSPACE || process.cwd(),
|
|
156
|
+
})
|
|
157
|
+
);
|
|
158
|
+
const total = report.duplicates.length;
|
|
159
|
+
console.log(
|
|
160
|
+
`jscpd: ${total} clone${total === 1 ? '' : 's'}, ${newCount} new ` +
|
|
161
|
+
`(the rest are accepted in ${BASELINE}).`
|
|
162
|
+
);
|
|
163
|
+
if (jscpd.status !== 0 && newCount === 0) {
|
|
164
|
+
// jscpd failed for a reason other than new clones; show what it said.
|
|
165
|
+
process.stdout.write(jscpd.stdout);
|
|
166
|
+
process.stderr.write(jscpd.stderr);
|
|
167
|
+
}
|
|
168
|
+
return jscpd.status ?? 2;
|
|
169
|
+
} finally {
|
|
170
|
+
rmSync(output, { recursive: true, force: true });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
175
|
+
process.exitCode = main();
|
|
176
|
+
}
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
import { writeFileSync } from 'fs';
|
|
14
14
|
import { randomBytes } from 'crypto';
|
|
15
15
|
|
|
16
|
+
import { printUntrusted } from '../../scripts/github-actions-log.mjs';
|
|
17
|
+
|
|
16
18
|
const PACKAGE_NAME = 'browser-commander';
|
|
17
19
|
|
|
18
20
|
import {
|
|
@@ -69,7 +71,7 @@ ${description}
|
|
|
69
71
|
|
|
70
72
|
console.log(`Created changeset: ${changesetFile}`);
|
|
71
73
|
console.log('Content:');
|
|
72
|
-
|
|
74
|
+
printUntrusted(content);
|
|
73
75
|
|
|
74
76
|
// Format with Prettier
|
|
75
77
|
console.log('\nFormatting with Prettier...');
|
|
@@ -70,6 +70,9 @@ if (!releaseId || !version || !repository) {
|
|
|
70
70
|
process.exit(1);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/** Start of the npm badge appended below, which marks a formatted body. */
|
|
74
|
+
const NPM_BADGE_PREFIX = '[. The
|
|
86
|
+
// badge's alt text marks it: matching the image host would also skip a
|
|
87
|
+
// body whose changelog text mentions that host.
|
|
88
|
+
if (currentBody.includes(NPM_BADGE_PREFIX)) {
|
|
84
89
|
console.log('ℹ️ Release notes already formatted');
|
|
85
90
|
process.exit(0);
|
|
86
91
|
}
|
|
@@ -186,7 +191,7 @@ try {
|
|
|
186
191
|
|
|
187
192
|
// Build formatted release notes
|
|
188
193
|
const versionWithoutV = version.replace(/^v/, '');
|
|
189
|
-
const npmBadge =
|
|
194
|
+
const npmBadge = `${NPM_BADGE_PREFIX}https://img.shields.io/badge/npm-${versionWithoutV}-blue.svg)](https://www.npmjs.com/package/${PACKAGE_NAME}/v/${versionWithoutV})`;
|
|
190
195
|
|
|
191
196
|
let formattedBody = `${cleanDescription}`;
|
|
192
197
|
|
|
@@ -23,6 +23,8 @@ import {
|
|
|
23
23
|
} from 'fs';
|
|
24
24
|
import { join } from 'path';
|
|
25
25
|
|
|
26
|
+
import { printUntrusted } from '../../scripts/github-actions-log.mjs';
|
|
27
|
+
|
|
26
28
|
const PACKAGE_NAME = 'browser-commander';
|
|
27
29
|
const CHANGESET_DIR = '.changeset';
|
|
28
30
|
|
|
@@ -254,7 +256,8 @@ function main() {
|
|
|
254
256
|
}
|
|
255
257
|
|
|
256
258
|
console.log('\nChangeset merge completed successfully');
|
|
257
|
-
console.log(
|
|
259
|
+
console.log('\nMerged changeset content:');
|
|
260
|
+
printUntrusted(mergedContent);
|
|
258
261
|
}
|
|
259
262
|
|
|
260
263
|
main();
|
|
@@ -7,13 +7,17 @@
|
|
|
7
7
|
* - Only checks changeset files ADDED by the current PR (not pre-existing ones)
|
|
8
8
|
* - Uses git diff to compare PR head against base branch
|
|
9
9
|
* - Validates that the PR adds exactly one changeset with proper format
|
|
10
|
-
* -
|
|
10
|
+
* - Fails when a pull request's diff cannot be computed (issue #128): checking
|
|
11
|
+
* every changeset on disk instead would let a stale one satisfy the check
|
|
12
|
+
* - Outside a pull request (local runs), checks the changesets on disk
|
|
11
13
|
*/
|
|
12
14
|
|
|
13
15
|
import { execSync } from 'child_process';
|
|
14
16
|
import { readFileSync, readdirSync, existsSync } from 'fs';
|
|
15
17
|
import { join } from 'path';
|
|
16
18
|
|
|
19
|
+
import { printUntrusted } from '../../scripts/github-actions-log.mjs';
|
|
20
|
+
|
|
17
21
|
const PACKAGE_NAME = 'browser-commander';
|
|
18
22
|
// When running from js/ directory, the changeset dir is local
|
|
19
23
|
const CHANGESET_DIR = '.changeset';
|
|
@@ -113,13 +117,11 @@ function tryBaseBranchComparison(prBase) {
|
|
|
113
117
|
}
|
|
114
118
|
|
|
115
119
|
/**
|
|
116
|
-
*
|
|
120
|
+
* Outside a pull request: get all changesets in directory
|
|
117
121
|
* @returns {string[]} Array of all changeset file names
|
|
118
122
|
*/
|
|
119
123
|
function getAllChangesets() {
|
|
120
|
-
console.log(
|
|
121
|
-
'Warning: Could not determine PR diff, checking all changesets in directory'
|
|
122
|
-
);
|
|
124
|
+
console.log('No pull request to compare; checking all changesets on disk');
|
|
123
125
|
if (!existsSync(CHANGESET_DIR)) {
|
|
124
126
|
return [];
|
|
125
127
|
}
|
|
@@ -136,6 +138,8 @@ function getAddedChangesetFiles() {
|
|
|
136
138
|
const baseSha = process.env.GITHUB_BASE_SHA || process.env.BASE_SHA;
|
|
137
139
|
const headSha = process.env.GITHUB_HEAD_SHA || process.env.HEAD_SHA;
|
|
138
140
|
|
|
141
|
+
const prBase = process.env.GITHUB_BASE_REF;
|
|
142
|
+
|
|
139
143
|
// Try explicit SHAs first
|
|
140
144
|
if (baseSha && headSha) {
|
|
141
145
|
const result = tryExplicitShaComparison(baseSha, headSha);
|
|
@@ -145,7 +149,6 @@ function getAddedChangesetFiles() {
|
|
|
145
149
|
}
|
|
146
150
|
|
|
147
151
|
// Try base branch comparison
|
|
148
|
-
const prBase = process.env.GITHUB_BASE_REF;
|
|
149
152
|
if (prBase) {
|
|
150
153
|
const result = tryBaseBranchComparison(prBase);
|
|
151
154
|
if (result !== null) {
|
|
@@ -153,7 +156,14 @@ function getAddedChangesetFiles() {
|
|
|
153
156
|
}
|
|
154
157
|
}
|
|
155
158
|
|
|
156
|
-
//
|
|
159
|
+
// In a pull request, every changeset on disk includes ones already on the
|
|
160
|
+
// base branch, so falling back to them would pass a PR that adds none.
|
|
161
|
+
if (prBase || baseSha || headSha) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
'Could not determine which changesets this pull request adds, because git could not diff it against its base'
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
157
167
|
return getAllChangesets();
|
|
158
168
|
}
|
|
159
169
|
|
|
@@ -252,7 +262,9 @@ try {
|
|
|
252
262
|
console.error(`::error::${validation.error}`);
|
|
253
263
|
console.error(`\nFile content of ${changesetFile}:`);
|
|
254
264
|
try {
|
|
255
|
-
|
|
265
|
+
printUntrusted(readFileSync(changesetFile, 'utf-8'), {
|
|
266
|
+
stream: process.stderr,
|
|
267
|
+
});
|
|
256
268
|
} catch {
|
|
257
269
|
console.error('(could not read file)');
|
|
258
270
|
}
|
|
@@ -261,9 +273,10 @@ try {
|
|
|
261
273
|
|
|
262
274
|
console.log('Changeset validation passed');
|
|
263
275
|
console.log(` Type: ${validation.type}`);
|
|
264
|
-
console.log(
|
|
276
|
+
console.log(' Description:');
|
|
277
|
+
printUntrusted(validation.description);
|
|
265
278
|
} catch (error) {
|
|
266
|
-
console.error(
|
|
279
|
+
console.error(`::error::${error.message}`);
|
|
267
280
|
if (process.env.DEBUG) {
|
|
268
281
|
console.error('Stack trace:', error.stack);
|
|
269
282
|
}
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
loadCommandStream,
|
|
25
25
|
loadLinoArguments,
|
|
26
26
|
} from '../../scripts/use-module.mjs';
|
|
27
|
+
import { printUntrusted } from '../../scripts/github-actions-log.mjs';
|
|
27
28
|
|
|
28
29
|
const { $ } = await loadCommandStream();
|
|
29
30
|
const { makeConfig } = await loadLinoArguments();
|
|
@@ -53,11 +54,9 @@ const config = makeConfig({
|
|
|
53
54
|
const { mode, bumpType, description } = config;
|
|
54
55
|
|
|
55
56
|
// Debug: Log parsed configuration
|
|
56
|
-
console.log('Parsed configuration:', {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
description: description || '(none)',
|
|
60
|
-
});
|
|
57
|
+
console.log('Parsed configuration:', { mode, bumpType });
|
|
58
|
+
console.log('Description:');
|
|
59
|
+
printUntrusted(description || '(none)');
|
|
61
60
|
|
|
62
61
|
// Detect if positional arguments were used (common mistake)
|
|
63
62
|
const args = process.argv.slice(2);
|
|
@@ -145,7 +144,7 @@ async function main() {
|
|
|
145
144
|
try {
|
|
146
145
|
// Configure git
|
|
147
146
|
await $`git config user.name "github-actions[bot]"`;
|
|
148
|
-
await $`git config user.email "github-actions[bot]@users.noreply.github.com"`;
|
|
147
|
+
await $`git config user.email "41898282+github-actions[bot]@users.noreply.github.com"`;
|
|
149
148
|
|
|
150
149
|
// Check if remote main has advanced (handles re-runs after partial success)
|
|
151
150
|
console.log('Checking for remote changes...');
|
|
@@ -231,10 +230,9 @@ async function main() {
|
|
|
231
230
|
// Stage all changes (package.json, package-lock.json, CHANGELOG.md, deleted changesets)
|
|
232
231
|
await $`git add -A`;
|
|
233
232
|
|
|
234
|
-
// Commit with version number as message
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
await $`git commit -m "${escapedMessage}"`;
|
|
233
|
+
// Commit with version number as message. command-stream quotes the
|
|
234
|
+
// interpolated value itself; escaping it here would add backslashes.
|
|
235
|
+
await $`git commit -m ${newVersion}`;
|
|
238
236
|
|
|
239
237
|
// Push directly to main.
|
|
240
238
|
//
|
|
@@ -766,7 +766,9 @@
|
|
|
766
766
|
},
|
|
767
767
|
"default": {
|
|
768
768
|
"darwin": ["com.apple.safari"]
|
|
769
|
-
}
|
|
769
|
+
},
|
|
770
|
+
"executables": { "darwin": ["/usr/bin/safaridriver"] },
|
|
771
|
+
"controlProtocol": "webdriver"
|
|
770
772
|
},
|
|
771
773
|
{
|
|
772
774
|
"id": "safari-technology-preview",
|
|
@@ -781,7 +783,13 @@
|
|
|
781
783
|
},
|
|
782
784
|
"default": {
|
|
783
785
|
"darwin": ["com.apple.safaritechnologypreview"]
|
|
784
|
-
}
|
|
786
|
+
},
|
|
787
|
+
"executables": {
|
|
788
|
+
"darwin": [
|
|
789
|
+
"/Applications/Safari Technology Preview.app/Contents/MacOS/safaridriver"
|
|
790
|
+
]
|
|
791
|
+
},
|
|
792
|
+
"controlProtocol": "webdriver"
|
|
785
793
|
},
|
|
786
794
|
{
|
|
787
795
|
"id": "whale",
|
package/src/browser/launcher.js
CHANGED
|
@@ -17,6 +17,8 @@ import { resolveSystemBrowserExecutable } from './system-browser.js';
|
|
|
17
17
|
import { applyFingerprint } from '../fingerprint/apply.js';
|
|
18
18
|
import { attachDownloads } from '../downloads/attach.js';
|
|
19
19
|
import { launchWebDriver } from './webdriver.js';
|
|
20
|
+
import { launchSafari } from './safari-launch.js';
|
|
21
|
+
import { isSafariChannel } from './safari-support.js';
|
|
20
22
|
import {
|
|
21
23
|
loadStorageState,
|
|
22
24
|
restorePlaywrightStorageState,
|
|
@@ -300,6 +302,18 @@ export async function launchBrowserWithDependencies(
|
|
|
300
302
|
options = {},
|
|
301
303
|
dependencies = {}
|
|
302
304
|
) {
|
|
305
|
+
if (isSafariChannel(options.channel ?? options.browser)) {
|
|
306
|
+
validateLaunchMode({
|
|
307
|
+
engine: options.engine ?? 'playwright',
|
|
308
|
+
launch: options.launch ?? 'real',
|
|
309
|
+
attach: options.attach,
|
|
310
|
+
});
|
|
311
|
+
return await launchSafari(options, dependencies);
|
|
312
|
+
}
|
|
313
|
+
return await launchNonSafariBrowser(options, dependencies);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async function launchNonSafariBrowser(options, dependencies) {
|
|
303
317
|
const {
|
|
304
318
|
engine = 'playwright',
|
|
305
319
|
launch = 'real',
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { findBrowserSource } from './browser-sources.js';
|
|
4
|
+
import { launchSafari } from './safari-launch.js';
|
|
5
|
+
import { isSafariChannel } from './safari-support.js';
|
|
4
6
|
|
|
5
7
|
import { startProcess } from '../utilities/subprocess.js';
|
|
6
8
|
import {
|
|
@@ -563,6 +565,9 @@ export async function launchAndConnectRealBrowserWithDependencies(
|
|
|
563
565
|
options = {},
|
|
564
566
|
dependencies = {}
|
|
565
567
|
) {
|
|
568
|
+
if (isSafariChannel(options.channel ?? options.browser)) {
|
|
569
|
+
return launchSafari(options, dependencies);
|
|
570
|
+
}
|
|
566
571
|
const {
|
|
567
572
|
engine = 'playwright',
|
|
568
573
|
channel = 'chrome',
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { launchWebDriver } from './webdriver.js';
|
|
2
|
+
import { findBrowserSource } from './browser-sources.js';
|
|
3
|
+
import { validateSafariOptions } from './safari-support.js';
|
|
4
|
+
import { restoreWebDriverStorageState } from './storage-state.js';
|
|
5
|
+
|
|
6
|
+
/** Launch installed Safari through its native W3C WebDriver server. */
|
|
7
|
+
export async function launchSafari(options = {}, dependencies = {}) {
|
|
8
|
+
validateSafariOptions(options);
|
|
9
|
+
const channel = findBrowserSource(
|
|
10
|
+
options.channel ?? options.browser ?? 'safari'
|
|
11
|
+
).id;
|
|
12
|
+
const launched = await (dependencies.launchWebDriver ?? launchWebDriver)(
|
|
13
|
+
{
|
|
14
|
+
...options,
|
|
15
|
+
browser: channel,
|
|
16
|
+
driverPath: options.driverPath ?? options.executablePath,
|
|
17
|
+
executablePath: undefined,
|
|
18
|
+
},
|
|
19
|
+
dependencies
|
|
20
|
+
);
|
|
21
|
+
try {
|
|
22
|
+
const state = options.storageState;
|
|
23
|
+
await restoreWebDriverStorageState({
|
|
24
|
+
page: launched.page,
|
|
25
|
+
storageState: state,
|
|
26
|
+
seedCookies: options.seedCookies,
|
|
27
|
+
});
|
|
28
|
+
} catch (error) {
|
|
29
|
+
await launched.close();
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
...launched,
|
|
34
|
+
browser: launched.driver,
|
|
35
|
+
engine: 'selenium',
|
|
36
|
+
controlProtocol: 'webdriver',
|
|
37
|
+
executablePath: launched.driverPath,
|
|
38
|
+
browserProcess: launched.driverProcess,
|
|
39
|
+
downloads: null,
|
|
40
|
+
launch: options.launch ?? 'real',
|
|
41
|
+
};
|
|
42
|
+
}
|