brave-real-launcher 1.2.0 → 1.2.2
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/.github/workflows/main.yml +164 -36
- package/dist/brave-finder.mjs +341 -0
- package/dist/brave-launcher.mjs +426 -0
- package/dist/chrome-launcher.mjs +407 -0
- package/dist/flags.mjs +93 -0
- package/dist/index.mjs +12 -0
- package/dist/random-port.mjs +22 -0
- package/dist/utils.mjs +231 -0
- package/dist/xvfb-support.mjs +187 -0
- package/package.json +13 -2
- package/scripts/rename-esm.cjs +82 -0
- package/test-dual-modules.cjs +68 -0
|
@@ -72,8 +72,37 @@ jobs:
|
|
|
72
72
|
run: |
|
|
73
73
|
echo "🔍 Checking for chrome-launcher updates..."
|
|
74
74
|
|
|
75
|
-
# Chrome-launcher की latest version fetch करें
|
|
76
|
-
|
|
75
|
+
# Chrome-launcher की latest version fetch करें with error handling
|
|
76
|
+
echo "🔍 Fetching chrome-launcher latest release..."
|
|
77
|
+
|
|
78
|
+
# Fetch chrome-launcher version with multiple methods
|
|
79
|
+
CHROME_VERSION=""
|
|
80
|
+
|
|
81
|
+
# Method 1: Try GitHub API
|
|
82
|
+
if command -v jq >/dev/null 2>&1; then
|
|
83
|
+
CHROME_API_RESPONSE=$(curl -s "https://api.github.com/repos/GoogleChrome/chrome-launcher/releases/latest")
|
|
84
|
+
if echo "$CHROME_API_RESPONSE" | jq -e '.tag_name' >/dev/null 2>&1; then
|
|
85
|
+
CHROME_VERSION=$(echo "$CHROME_API_RESPONSE" | jq -r .tag_name | sed 's/v//')
|
|
86
|
+
echo "✅ Found chrome-launcher version via GitHub API: $CHROME_VERSION"
|
|
87
|
+
fi
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
# Method 2: Fallback to NPM registry
|
|
91
|
+
if [ -z "$CHROME_VERSION" ] || [ "$CHROME_VERSION" = "null" ]; then
|
|
92
|
+
echo "⚠️ GitHub API failed, trying NPM registry..."
|
|
93
|
+
CHROME_VERSION=$(curl -s "https://registry.npmjs.org/chrome-launcher/latest" | jq -r .version 2>/dev/null || echo "")
|
|
94
|
+
if [ -n "$CHROME_VERSION" ] && [ "$CHROME_VERSION" != "null" ]; then
|
|
95
|
+
echo "✅ Found chrome-launcher version via NPM: $CHROME_VERSION"
|
|
96
|
+
fi
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
# Method 3: Final fallback - use current version
|
|
100
|
+
if [ -z "$CHROME_VERSION" ] || [ "$CHROME_VERSION" = "null" ]; then
|
|
101
|
+
echo "⚠️ All methods failed, using current version as fallback"
|
|
102
|
+
CHROME_VERSION=$(node -e "console.log(require('./package.json').version)")
|
|
103
|
+
echo "📦 Fallback to current version: $CHROME_VERSION"
|
|
104
|
+
fi
|
|
105
|
+
|
|
77
106
|
CURRENT_VERSION=$(node -e "console.log(require('./package.json').version)")
|
|
78
107
|
|
|
79
108
|
echo "chrome_version=$CHROME_VERSION" >> $GITHUB_OUTPUT
|
|
@@ -102,10 +131,27 @@ jobs:
|
|
|
102
131
|
- name: 🔄 Sync with Chrome-Launcher
|
|
103
132
|
if: steps.sync-check.outputs.should_sync == 'true'
|
|
104
133
|
run: |
|
|
105
|
-
echo "
|
|
134
|
+
echo "🔢 Syncing brave-real-launcher with chrome-launcher v${{ steps.sync-check.outputs.chrome_version }}"
|
|
135
|
+
|
|
136
|
+
# Validate chrome version before using it
|
|
137
|
+
CHROME_VER="${{ steps.sync-check.outputs.chrome_version }}"
|
|
138
|
+
if [ "$CHROME_VER" = "null" ] || [ -z "$CHROME_VER" ]; then
|
|
139
|
+
echo "❌ Invalid chrome version detected: $CHROME_VER"
|
|
140
|
+
echo "🔄 Skipping sync due to invalid version"
|
|
141
|
+
exit 0
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
# Validate version format (should be x.y.z)
|
|
145
|
+
if ! echo "$CHROME_VER" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$'; then
|
|
146
|
+
echo "❌ Invalid version format: $CHROME_VER"
|
|
147
|
+
echo "🔄 Skipping sync due to invalid version format"
|
|
148
|
+
exit 0
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
echo "✅ Valid chrome version: $CHROME_VER"
|
|
106
152
|
|
|
107
153
|
# Package.json version को update करें
|
|
108
|
-
npm version $
|
|
154
|
+
npm version "$CHROME_VER" --no-git-tag-version
|
|
109
155
|
|
|
110
156
|
# Git configuration
|
|
111
157
|
git config user.name "🤖 Brave Sync Bot"
|
|
@@ -198,55 +244,136 @@ jobs:
|
|
|
198
244
|
npm audit --audit-level high || echo "⚠️ Some security issues found but continuing..."
|
|
199
245
|
echo "✅ Security audit completed!"
|
|
200
246
|
|
|
201
|
-
# Step 10:
|
|
202
|
-
- name:
|
|
203
|
-
id:
|
|
247
|
+
# Step 10: Smart Version Management & Publish Strategy
|
|
248
|
+
- name: 🔢 Smart Version Management
|
|
249
|
+
id: version-manager
|
|
204
250
|
run: |
|
|
205
|
-
echo "
|
|
251
|
+
echo "🔢 Smart version management starting..."
|
|
206
252
|
|
|
253
|
+
# Get current status
|
|
207
254
|
PUBLISH_MODE="${{ github.event.inputs.publish_mode || 'auto' }}"
|
|
208
|
-
|
|
209
|
-
|
|
255
|
+
SYNC_PERFORMED="${{ steps.sync-check.outputs.should_sync }}"
|
|
256
|
+
CHROME_VERSION="${{ steps.sync-check.outputs.chrome_version }}"
|
|
210
257
|
|
|
211
|
-
# Current
|
|
258
|
+
# Current version from package.json
|
|
212
259
|
CURRENT_VERSION=$(node -e "console.log(require('./package.json').version)")
|
|
260
|
+
|
|
261
|
+
# NPM published version
|
|
262
|
+
echo "📎 Checking NPM registry..."
|
|
213
263
|
NPM_VERSION=$(npm view brave-real-launcher version 2>/dev/null || echo "0.0.0")
|
|
214
264
|
|
|
215
|
-
echo "
|
|
265
|
+
echo "=== Version Status ==="
|
|
266
|
+
echo "Local version: $CURRENT_VERSION"
|
|
216
267
|
echo "NPM version: $NPM_VERSION"
|
|
268
|
+
echo "Chrome-launcher version: $CHROME_VERSION"
|
|
269
|
+
echo "Sync performed: $SYNC_PERFORMED"
|
|
270
|
+
echo "Publish mode: $PUBLISH_MODE"
|
|
271
|
+
|
|
272
|
+
# Smart version determination
|
|
273
|
+
NEW_VERSION=""
|
|
274
|
+
SHOULD_PUBLISH="false"
|
|
275
|
+
REASON=""
|
|
217
276
|
|
|
218
|
-
# Publish logic
|
|
219
277
|
if [ "$PUBLISH_MODE" = "skip" ]; then
|
|
278
|
+
SHOULD_PUBLISH="false"
|
|
220
279
|
REASON="Publish mode set to skip"
|
|
221
|
-
|
|
280
|
+
NEW_VERSION="$CURRENT_VERSION"
|
|
281
|
+
|
|
282
|
+
elif [ "$SYNC_PERFORMED" = "true" ]; then
|
|
283
|
+
# Chrome-launcher sync हुआ है - use chrome version
|
|
284
|
+
NEW_VERSION="$CHROME_VERSION"
|
|
222
285
|
SHOULD_PUBLISH="true"
|
|
223
|
-
REASON="
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
286
|
+
REASON="Synced with chrome-launcher v$CHROME_VERSION"
|
|
287
|
+
|
|
288
|
+
# Update package.json with chrome version
|
|
289
|
+
if [ "$NEW_VERSION" != "$CURRENT_VERSION" ]; then
|
|
290
|
+
echo "🔄 Updating package.json to chrome-launcher version: $NEW_VERSION"
|
|
291
|
+
npm version "$NEW_VERSION" --no-git-tag-version
|
|
292
|
+
fi
|
|
293
|
+
|
|
294
|
+
else
|
|
295
|
+
# Regular update - सिर्फ main/master branch पर publish करें
|
|
296
|
+
if [[ "${{ github.ref }}" == "refs/heads/main" || "${{ github.ref }}" == "refs/heads/master" ]]; then
|
|
297
|
+
|
|
298
|
+
if [ "$NPM_VERSION" = "0.0.0" ]; then
|
|
299
|
+
# First publish
|
|
300
|
+
NEW_VERSION="$CURRENT_VERSION"
|
|
228
301
|
SHOULD_PUBLISH="true"
|
|
229
|
-
REASON="
|
|
302
|
+
REASON="First publish"
|
|
230
303
|
else
|
|
231
|
-
|
|
304
|
+
# Auto-increment करें अगर NPM version >= current version
|
|
305
|
+
if [ "$(printf '%s\n' "$NPM_VERSION" "$CURRENT_VERSION" | sort -V | tail -n1)" = "$NPM_VERSION" ]; then
|
|
306
|
+
# NPM version is same or newer, increment
|
|
307
|
+
echo "🔢 Auto-incrementing patch version..."
|
|
308
|
+
|
|
309
|
+
# Parse NPM version and increment
|
|
310
|
+
IFS='.' read -r major minor patch <<< "$NPM_VERSION"
|
|
311
|
+
NEW_PATCH=$((patch + 1))
|
|
312
|
+
NEW_VERSION="${major}.${minor}.${NEW_PATCH}"
|
|
313
|
+
|
|
314
|
+
echo "📈 Version increment: $NPM_VERSION → $NEW_VERSION"
|
|
315
|
+
|
|
316
|
+
# Update package.json
|
|
317
|
+
npm version "$NEW_VERSION" --no-git-tag-version
|
|
318
|
+
|
|
319
|
+
SHOULD_PUBLISH="true"
|
|
320
|
+
REASON="Auto-incremented from $NPM_VERSION to $NEW_VERSION"
|
|
321
|
+
else
|
|
322
|
+
# Current version is newer
|
|
323
|
+
NEW_VERSION="$CURRENT_VERSION"
|
|
324
|
+
SHOULD_PUBLISH="true"
|
|
325
|
+
REASON="Local version newer than NPM: $CURRENT_VERSION > $NPM_VERSION"
|
|
326
|
+
fi
|
|
232
327
|
fi
|
|
328
|
+
|
|
233
329
|
else
|
|
234
|
-
|
|
330
|
+
# Not on main branch
|
|
331
|
+
NEW_VERSION="$CURRENT_VERSION"
|
|
332
|
+
SHOULD_PUBLISH="false"
|
|
333
|
+
REASON="Not on main/master branch"
|
|
334
|
+
fi
|
|
335
|
+
fi
|
|
336
|
+
|
|
337
|
+
# Force publish override
|
|
338
|
+
if [ "$PUBLISH_MODE" = "force" ]; then
|
|
339
|
+
if [ -z "$NEW_VERSION" ] || [ "$NEW_VERSION" = "$CURRENT_VERSION" ]; then
|
|
340
|
+
# Force के लिए increment करें
|
|
341
|
+
IFS='.' read -r major minor patch <<< "$([ "$NPM_VERSION" != "0.0.0" ] && echo "$NPM_VERSION" || echo "$CURRENT_VERSION")"
|
|
342
|
+
NEW_PATCH=$((patch + 1))
|
|
343
|
+
NEW_VERSION="${major}.${minor}.${NEW_PATCH}"
|
|
344
|
+
npm version "$NEW_VERSION" --no-git-tag-version
|
|
235
345
|
fi
|
|
346
|
+
SHOULD_PUBLISH="true"
|
|
347
|
+
REASON="Force publish requested - v$NEW_VERSION"
|
|
348
|
+
fi
|
|
349
|
+
|
|
350
|
+
# Commit version changes if made
|
|
351
|
+
if [ "$NEW_VERSION" != "$CURRENT_VERSION" ]; then
|
|
352
|
+
echo "📁 Committing version change..."
|
|
353
|
+
git config user.name "🤖 Brave Version Manager"
|
|
354
|
+
git config user.email "action@github.com"
|
|
355
|
+
git add package.json package-lock.json
|
|
356
|
+
git commit -m "🔢 Version update: $CURRENT_VERSION → $NEW_VERSION" || echo "No changes to commit"
|
|
357
|
+
git push || echo "Nothing to push"
|
|
236
358
|
fi
|
|
237
359
|
|
|
360
|
+
# Output results
|
|
238
361
|
echo "should_publish=$SHOULD_PUBLISH" >> $GITHUB_OUTPUT
|
|
239
362
|
echo "reason=$REASON" >> $GITHUB_OUTPUT
|
|
240
|
-
echo "
|
|
241
|
-
echo "
|
|
363
|
+
echo "final_version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
|
364
|
+
echo "version_changed=$([ "$NEW_VERSION" != "$CURRENT_VERSION" ] && echo "true" || echo "false")" >> $GITHUB_OUTPUT
|
|
242
365
|
|
|
243
|
-
echo "
|
|
366
|
+
echo "=== Final Decision ==="
|
|
367
|
+
echo "📋 Should publish: $SHOULD_PUBLISH"
|
|
368
|
+
echo "📋 Final version: $NEW_VERSION"
|
|
369
|
+
echo "📋 Reason: $REASON"
|
|
244
370
|
|
|
245
371
|
# Step 11: Publish to NPM
|
|
246
372
|
- name: 🚀 Publish to NPM
|
|
247
|
-
if: steps.
|
|
373
|
+
if: steps.version-manager.outputs.should_publish == 'true'
|
|
248
374
|
run: |
|
|
249
|
-
|
|
375
|
+
FINAL_VERSION="${{ steps.version-manager.outputs.final_version }}"
|
|
376
|
+
echo "🚀 Publishing brave-real-launcher v$FINAL_VERSION to NPM..."
|
|
250
377
|
|
|
251
378
|
# Verify we have NPM token
|
|
252
379
|
if [ -z "$NPM_TOKEN" ]; then
|
|
@@ -263,12 +390,12 @@ jobs:
|
|
|
263
390
|
git config user.name "🤖 Brave Publish Bot"
|
|
264
391
|
git config user.email "action@github.com"
|
|
265
392
|
|
|
266
|
-
TAG_NAME="v$
|
|
393
|
+
TAG_NAME="v$FINAL_VERSION"
|
|
267
394
|
git tag "$TAG_NAME" || echo "Tag might already exist"
|
|
268
395
|
git push origin "$TAG_NAME" || echo "Failed to push tag"
|
|
269
396
|
fi
|
|
270
397
|
|
|
271
|
-
echo "🎉 Successfully published brave-real-launcher v$
|
|
398
|
+
echo "🎉 Successfully published brave-real-launcher v$FINAL_VERSION!"
|
|
272
399
|
env:
|
|
273
400
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
274
401
|
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
@@ -280,19 +407,20 @@ jobs:
|
|
|
280
407
|
echo "=================================="
|
|
281
408
|
echo "📋 Execution Summary:"
|
|
282
409
|
echo "• Chrome-launcher version: ${{ steps.sync-check.outputs.chrome_version }}"
|
|
283
|
-
echo "•
|
|
410
|
+
echo "• Final version: ${{ steps.version-manager.outputs.final_version }}"
|
|
411
|
+
echo "• Version changed: ${{ steps.version-manager.outputs.version_changed }}"
|
|
284
412
|
echo "• Sync performed: ${{ steps.sync-check.outputs.should_sync }}"
|
|
285
413
|
echo "• Sync mode: ${{ steps.sync-check.outputs.sync_mode }}"
|
|
286
|
-
echo "• Publish performed: ${{ steps.
|
|
287
|
-
echo "• Publish reason: ${{ steps.
|
|
414
|
+
echo "• Publish performed: ${{ steps.version-manager.outputs.should_publish }}"
|
|
415
|
+
echo "• Publish reason: ${{ steps.version-manager.outputs.reason }}"
|
|
288
416
|
echo "• Build: ✅ Success"
|
|
289
417
|
echo "• Tests: ✅ Passed"
|
|
290
418
|
echo "• Security: ✅ Checked"
|
|
291
419
|
echo "=================================="
|
|
292
420
|
|
|
293
421
|
# Set workflow status
|
|
294
|
-
if [ "${{ steps.
|
|
295
|
-
echo "🚀 Package published successfully!"
|
|
422
|
+
if [ "${{ steps.version-manager.outputs.should_publish }}" = "true" ]; then
|
|
423
|
+
echo "🚀 Package published successfully as v${{ steps.version-manager.outputs.final_version }}!"
|
|
296
424
|
else
|
|
297
|
-
echo "📦 Package ready but not published (${{ steps.
|
|
298
|
-
fi
|
|
425
|
+
echo "📦 Package ready but not published (${{ steps.version-manager.outputs.reason }})"
|
|
426
|
+
fi
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright 2016 Google Inc. All Rights Reserved.
|
|
3
|
+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
4
|
+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
|
5
|
+
*/
|
|
6
|
+
'use strict';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { homedir } from 'os';
|
|
10
|
+
import { execSync } from 'child_process';
|
|
11
|
+
import escapeRegExp from 'escape-string-regexp';
|
|
12
|
+
import log from 'lighthouse-logger';
|
|
13
|
+
import which from 'which';
|
|
14
|
+
import { getWSLLocalAppDataPath, toWSLPath, BravePathNotSetError } from './utils.mjs.mjs';
|
|
15
|
+
const newLineRegex = /\r?\n/;
|
|
16
|
+
/**
|
|
17
|
+
* check for macOS default app paths first to avoid waiting for the slow lsregister command
|
|
18
|
+
*/
|
|
19
|
+
export function darwinFast() {
|
|
20
|
+
const priorityOptions = [
|
|
21
|
+
process.env.BRAVE_PATH,
|
|
22
|
+
process.env.LIGHTHOUSE_BRAVE_PATH,
|
|
23
|
+
// Brave Browser paths
|
|
24
|
+
'/Applications/Brave Browser.app/Contents/MacOS/Brave Browser',
|
|
25
|
+
'/Applications/Brave Browser Beta.app/Contents/MacOS/Brave Browser Beta',
|
|
26
|
+
'/Applications/Brave Browser Nightly.app/Contents/MacOS/Brave Browser Nightly',
|
|
27
|
+
'/Applications/Brave Browser Dev.app/Contents/MacOS/Brave Browser Dev',
|
|
28
|
+
];
|
|
29
|
+
for (const bravePath of priorityOptions) {
|
|
30
|
+
if (bravePath && canAccess(bravePath))
|
|
31
|
+
return bravePath;
|
|
32
|
+
}
|
|
33
|
+
return darwin()[0];
|
|
34
|
+
}
|
|
35
|
+
export function darwin() {
|
|
36
|
+
const suffixes = [
|
|
37
|
+
'/Contents/MacOS/Brave Browser',
|
|
38
|
+
'/Contents/MacOS/Brave Browser Beta',
|
|
39
|
+
'/Contents/MacOS/Brave Browser Nightly',
|
|
40
|
+
'/Contents/MacOS/Brave Browser Dev'
|
|
41
|
+
];
|
|
42
|
+
const LSREGISTER = '/System/Library/Frameworks/CoreServices.framework' +
|
|
43
|
+
'/Versions/A/Frameworks/LaunchServices.framework' +
|
|
44
|
+
'/Versions/A/Support/lsregister';
|
|
45
|
+
const installations = [];
|
|
46
|
+
const customBravePath = resolveBravePath();
|
|
47
|
+
if (customBravePath) {
|
|
48
|
+
installations.push(customBravePath);
|
|
49
|
+
}
|
|
50
|
+
// Look for Brave Browser installations
|
|
51
|
+
try {
|
|
52
|
+
execSync(`${LSREGISTER} -dump` +
|
|
53
|
+
' | grep -i \'brave browser\\( beta\\| nightly\\| dev\\)\\?\\.app\'' +
|
|
54
|
+
' | awk \'{$1=""; print $0}\'')
|
|
55
|
+
.toString()
|
|
56
|
+
.split(newLineRegex)
|
|
57
|
+
.forEach((inst) => {
|
|
58
|
+
suffixes.forEach(suffix => {
|
|
59
|
+
const execPath = path.join(inst.substring(0, inst.indexOf('.app') + 4).trim(), suffix);
|
|
60
|
+
if (canAccess(execPath) && installations.indexOf(execPath) === -1) {
|
|
61
|
+
installations.push(execPath);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
// lsregister might not be available or might fail
|
|
68
|
+
log.warn('BraveLauncher', 'lsregister command failed, checking default paths');
|
|
69
|
+
}
|
|
70
|
+
// Retains one per line to maintain readability.
|
|
71
|
+
// clang-format off
|
|
72
|
+
const home = escapeRegExp(process.env.HOME || homedir());
|
|
73
|
+
const priorities = [
|
|
74
|
+
{ regex: new RegExp(`^${home}/Applications/.*Brave Browser\\.app`), weight: 50 },
|
|
75
|
+
{ regex: new RegExp(`^${home}/Applications/.*Brave Browser Beta\\.app`), weight: 51 },
|
|
76
|
+
{ regex: new RegExp(`^${home}/Applications/.*Brave Browser Nightly\\.app`), weight: 52 },
|
|
77
|
+
{ regex: new RegExp(`^${home}/Applications/.*Brave Browser Dev\\.app`), weight: 53 },
|
|
78
|
+
{ regex: /^\/Applications\/.*Brave Browser\.app/, weight: 100 },
|
|
79
|
+
{ regex: /^\/Applications\/.*Brave Browser Beta\.app/, weight: 101 },
|
|
80
|
+
{ regex: /^\/Applications\/.*Brave Browser Nightly\.app/, weight: 102 },
|
|
81
|
+
{ regex: /^\/Applications\/.*Brave Browser Dev\.app/, weight: 103 },
|
|
82
|
+
{ regex: /^\/Volumes\/.*Brave Browser\.app/, weight: -2 },
|
|
83
|
+
{ regex: /^\/Volumes\/.*Brave Browser Beta\.app/, weight: -1 },
|
|
84
|
+
];
|
|
85
|
+
if (process.env.LIGHTHOUSE_BRAVE_PATH) {
|
|
86
|
+
priorities.unshift({ regex: new RegExp(escapeRegExp(process.env.LIGHTHOUSE_BRAVE_PATH)), weight: 150 });
|
|
87
|
+
}
|
|
88
|
+
if (process.env.BRAVE_PATH) {
|
|
89
|
+
priorities.unshift({ regex: new RegExp(escapeRegExp(process.env.BRAVE_PATH)), weight: 151 });
|
|
90
|
+
}
|
|
91
|
+
// clang-format on
|
|
92
|
+
return sort(installations, priorities);
|
|
93
|
+
}
|
|
94
|
+
function resolveBravePath() {
|
|
95
|
+
if (canAccess(process.env.BRAVE_PATH)) {
|
|
96
|
+
return process.env.BRAVE_PATH;
|
|
97
|
+
}
|
|
98
|
+
if (canAccess(process.env.LIGHTHOUSE_BRAVE_PATH)) {
|
|
99
|
+
log.warn('BraveLauncher', 'LIGHTHOUSE_BRAVE_PATH is deprecated, use BRAVE_PATH env variable instead.');
|
|
100
|
+
return process.env.LIGHTHOUSE_BRAVE_PATH;
|
|
101
|
+
}
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Look for linux executables in 3 ways
|
|
106
|
+
* 1. Look into BRAVE_PATH env variable
|
|
107
|
+
* 2. Look into the directories where .desktop are saved on gnome based distro's
|
|
108
|
+
* 3. Look for brave-browser & brave executables by using the which command
|
|
109
|
+
* Support for both x64 and ARM64 architectures
|
|
110
|
+
*/
|
|
111
|
+
export function linux() {
|
|
112
|
+
let installations = [];
|
|
113
|
+
// 1. Look into BRAVE_PATH env variable
|
|
114
|
+
const customBravePath = resolveBravePath();
|
|
115
|
+
if (customBravePath) {
|
|
116
|
+
installations.push(customBravePath);
|
|
117
|
+
}
|
|
118
|
+
// 2. Look into the directories where .desktop are saved on gnome based distro's
|
|
119
|
+
const desktopInstallationFolders = [
|
|
120
|
+
path.join(homedir(), '.local/share/applications/'),
|
|
121
|
+
'/usr/share/applications/',
|
|
122
|
+
];
|
|
123
|
+
desktopInstallationFolders.forEach(folder => {
|
|
124
|
+
installations = installations.concat(findBraveExecutables(folder));
|
|
125
|
+
});
|
|
126
|
+
// 3. Look for brave-browser, brave, and other brave executables by using the which command
|
|
127
|
+
const executables = [
|
|
128
|
+
'brave-browser',
|
|
129
|
+
'brave-browser-stable',
|
|
130
|
+
'brave-browser-beta',
|
|
131
|
+
'brave-browser-nightly',
|
|
132
|
+
'brave-browser-dev',
|
|
133
|
+
'brave',
|
|
134
|
+
'brave-stable',
|
|
135
|
+
'brave-beta',
|
|
136
|
+
'brave-nightly',
|
|
137
|
+
'brave-dev',
|
|
138
|
+
// Flatpak versions
|
|
139
|
+
'flatpak run com.brave.Browser',
|
|
140
|
+
// Snap versions
|
|
141
|
+
'brave',
|
|
142
|
+
];
|
|
143
|
+
// Check standard installation paths for different architectures
|
|
144
|
+
const standardPaths = [
|
|
145
|
+
'/usr/bin/brave-browser',
|
|
146
|
+
'/usr/bin/brave-browser-stable',
|
|
147
|
+
'/usr/bin/brave-browser-beta',
|
|
148
|
+
'/usr/bin/brave-browser-nightly',
|
|
149
|
+
'/usr/bin/brave',
|
|
150
|
+
'/opt/brave.com/brave/brave',
|
|
151
|
+
'/opt/brave.com/brave-beta/brave-beta',
|
|
152
|
+
'/opt/brave.com/brave-nightly/brave-nightly',
|
|
153
|
+
'/opt/brave.com/brave-dev/brave-dev',
|
|
154
|
+
'/snap/bin/brave',
|
|
155
|
+
// ARM64 specific paths
|
|
156
|
+
'/usr/lib/aarch64-linux-gnu/brave/brave',
|
|
157
|
+
'/opt/brave-browser/brave',
|
|
158
|
+
// Additional paths for different distros
|
|
159
|
+
'/usr/local/bin/brave',
|
|
160
|
+
'/usr/local/bin/brave-browser',
|
|
161
|
+
];
|
|
162
|
+
standardPaths.forEach(bravePath => {
|
|
163
|
+
if (canAccess(bravePath)) {
|
|
164
|
+
installations.push(bravePath);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
executables.forEach((executable) => {
|
|
168
|
+
try {
|
|
169
|
+
const bravePath = which.sync(executable, { nothrow: true });
|
|
170
|
+
if (bravePath && canAccess(bravePath)) {
|
|
171
|
+
installations.push(bravePath);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch (e) {
|
|
175
|
+
// Not installed.
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
if (!installations.length) {
|
|
179
|
+
throw new BravePathNotSetError();
|
|
180
|
+
}
|
|
181
|
+
const priorities = [
|
|
182
|
+
{ regex: /brave-wrapper$/, weight: 51 },
|
|
183
|
+
{ regex: /brave-browser-stable$/, weight: 50 },
|
|
184
|
+
{ regex: /brave-browser$/, weight: 49 },
|
|
185
|
+
{ regex: /brave-stable$/, weight: 48 },
|
|
186
|
+
{ regex: /brave$/, weight: 47 },
|
|
187
|
+
{ regex: /brave-browser-beta$/, weight: 46 },
|
|
188
|
+
{ regex: /brave-beta$/, weight: 45 },
|
|
189
|
+
{ regex: /brave-browser-nightly$/, weight: 44 },
|
|
190
|
+
{ regex: /brave-nightly$/, weight: 43 },
|
|
191
|
+
{ regex: /brave-browser-dev$/, weight: 42 },
|
|
192
|
+
{ regex: /brave-dev$/, weight: 41 },
|
|
193
|
+
];
|
|
194
|
+
if (process.env.LIGHTHOUSE_BRAVE_PATH) {
|
|
195
|
+
priorities.unshift({ regex: new RegExp(escapeRegExp(process.env.LIGHTHOUSE_BRAVE_PATH)), weight: 100 });
|
|
196
|
+
}
|
|
197
|
+
if (process.env.BRAVE_PATH) {
|
|
198
|
+
priorities.unshift({ regex: new RegExp(escapeRegExp(process.env.BRAVE_PATH)), weight: 101 });
|
|
199
|
+
}
|
|
200
|
+
return sort(uniq(installations.filter(Boolean)), priorities);
|
|
201
|
+
}
|
|
202
|
+
export function wsl() {
|
|
203
|
+
// Manually populate the environment variables assuming it's the default config
|
|
204
|
+
process.env.LOCALAPPDATA = getWSLLocalAppDataPath(`${process.env.PATH}`);
|
|
205
|
+
process.env.PROGRAMFILES = toWSLPath('C:/Program Files', '/mnt/c/Program Files');
|
|
206
|
+
process.env['PROGRAMFILES(X86)'] =
|
|
207
|
+
toWSLPath('C:/Program Files (x86)', '/mnt/c/Program Files (x86)');
|
|
208
|
+
return win32();
|
|
209
|
+
}
|
|
210
|
+
export function win32() {
|
|
211
|
+
const installations = [];
|
|
212
|
+
// Brave Browser installation paths for different architectures
|
|
213
|
+
const suffixes = [
|
|
214
|
+
// Standard x64 installations
|
|
215
|
+
`${path.sep}BraveSoftware${path.sep}Brave-Browser${path.sep}Application${path.sep}brave.exe`,
|
|
216
|
+
`${path.sep}BraveSoftware${path.sep}Brave-Browser-Beta${path.sep}Application${path.sep}brave.exe`,
|
|
217
|
+
`${path.sep}BraveSoftware${path.sep}Brave-Browser-Nightly${path.sep}Application${path.sep}brave.exe`,
|
|
218
|
+
`${path.sep}BraveSoftware${path.sep}Brave-Browser-Dev${path.sep}Application${path.sep}brave.exe`,
|
|
219
|
+
// Alternative paths
|
|
220
|
+
`${path.sep}Brave${path.sep}Application${path.sep}brave.exe`,
|
|
221
|
+
`${path.sep}Brave-Browser${path.sep}Application${path.sep}brave.exe`,
|
|
222
|
+
];
|
|
223
|
+
const prefixes = [
|
|
224
|
+
process.env.LOCALAPPDATA,
|
|
225
|
+
process.env.PROGRAMFILES,
|
|
226
|
+
process.env['PROGRAMFILES(X86)'],
|
|
227
|
+
// Additional paths for ARM64 and other architectures
|
|
228
|
+
process.env.PROGRAMW6432,
|
|
229
|
+
].filter(Boolean);
|
|
230
|
+
const customBravePath = resolveBravePath();
|
|
231
|
+
if (customBravePath) {
|
|
232
|
+
installations.push(customBravePath);
|
|
233
|
+
}
|
|
234
|
+
// Check standard installation paths
|
|
235
|
+
prefixes.forEach(prefix => suffixes.forEach(suffix => {
|
|
236
|
+
const bravePath = path.join(prefix, suffix);
|
|
237
|
+
if (canAccess(bravePath)) {
|
|
238
|
+
installations.push(bravePath);
|
|
239
|
+
}
|
|
240
|
+
}));
|
|
241
|
+
// Check Windows Registry for Brave installations
|
|
242
|
+
try {
|
|
243
|
+
const registryPaths = getWindowsRegistryBravePaths();
|
|
244
|
+
registryPaths.forEach(regPath => {
|
|
245
|
+
if (canAccess(regPath)) {
|
|
246
|
+
installations.push(regPath);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
catch (e) {
|
|
251
|
+
log.warn('BraveLauncher', 'Failed to read Windows registry for Brave paths');
|
|
252
|
+
}
|
|
253
|
+
return installations;
|
|
254
|
+
}
|
|
255
|
+
function getWindowsRegistryBravePaths() {
|
|
256
|
+
const paths = [];
|
|
257
|
+
// Registry keys where Brave might be registered
|
|
258
|
+
const registryKeys = [
|
|
259
|
+
'HKEY_LOCAL_MACHINE\\SOFTWARE\\BraveSoftware\\Brave',
|
|
260
|
+
'HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\BraveSoftware\\Brave',
|
|
261
|
+
'HKEY_CURRENT_USER\\SOFTWARE\\BraveSoftware\\Brave',
|
|
262
|
+
'HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\brave.exe',
|
|
263
|
+
];
|
|
264
|
+
registryKeys.forEach(key => {
|
|
265
|
+
try {
|
|
266
|
+
const result = execSync(`reg query "${key}" /ve 2>nul`, { encoding: 'utf-8' });
|
|
267
|
+
const match = result.match(/REG_SZ\s+(.+)/);
|
|
268
|
+
if (match && match[1]) {
|
|
269
|
+
const bravePath = match[1].trim();
|
|
270
|
+
if (bravePath && canAccess(bravePath)) {
|
|
271
|
+
paths.push(bravePath);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
catch (e) {
|
|
276
|
+
// Registry key doesn't exist or access denied
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
return paths;
|
|
280
|
+
}
|
|
281
|
+
function sort(installations, priorities) {
|
|
282
|
+
const defaultPriority = 10;
|
|
283
|
+
return installations
|
|
284
|
+
// assign priorities
|
|
285
|
+
.map((inst) => {
|
|
286
|
+
for (const pair of priorities) {
|
|
287
|
+
if (pair.regex.test(inst)) {
|
|
288
|
+
return { path: inst, weight: pair.weight };
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return { path: inst, weight: defaultPriority };
|
|
292
|
+
})
|
|
293
|
+
// sort based on priorities
|
|
294
|
+
.sort((a, b) => (b.weight - a.weight))
|
|
295
|
+
// remove priority flag
|
|
296
|
+
.map(pair => pair.path);
|
|
297
|
+
}
|
|
298
|
+
function canAccess(file) {
|
|
299
|
+
if (!file) {
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
try {
|
|
303
|
+
fs.accessSync(file);
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
catch (e) {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
function uniq(arr) {
|
|
311
|
+
return Array.from(new Set(arr));
|
|
312
|
+
}
|
|
313
|
+
function findBraveExecutables(folder) {
|
|
314
|
+
const argumentsRegex = /(^[^ ]+).*/; // Take everything up to the first space
|
|
315
|
+
const braveExecRegex = '^Exec=\\/.*\\/(brave|brave-browser|brave-browser-stable|brave-browser-beta|brave-browser-nightly)-.*';
|
|
316
|
+
let installations = [];
|
|
317
|
+
if (canAccess(folder)) {
|
|
318
|
+
// Output of the grep & print looks like:
|
|
319
|
+
// /opt/brave.com/brave/brave --profile-directory
|
|
320
|
+
// /home/user/Downloads/brave-linux/brave-wrapper %U
|
|
321
|
+
let execPaths;
|
|
322
|
+
// Some systems do not support grep -R so fallback to -r.
|
|
323
|
+
try {
|
|
324
|
+
execPaths = execSync(`grep -ER "${braveExecRegex}" ${folder} | awk -F '=' '{print $2}'`, { stdio: 'pipe' });
|
|
325
|
+
}
|
|
326
|
+
catch (e) {
|
|
327
|
+
try {
|
|
328
|
+
execPaths = execSync(`grep -Er "${braveExecRegex}" ${folder} | awk -F '=' '{print $2}'`, { stdio: 'pipe' });
|
|
329
|
+
}
|
|
330
|
+
catch (e2) {
|
|
331
|
+
return installations;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
execPaths = execPaths.toString()
|
|
335
|
+
.split(newLineRegex)
|
|
336
|
+
.map((execPath) => execPath.replace(argumentsRegex, '$1'));
|
|
337
|
+
execPaths.forEach((execPath) => canAccess(execPath) && installations.push(execPath));
|
|
338
|
+
}
|
|
339
|
+
return installations;
|
|
340
|
+
}
|
|
341
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnJhdmUtZmluZGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2JyYXZlLWZpbmRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7OztHQUlHO0FBQ0gsWUFBWSxDQUFDO0FBRWIsT0FBTyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3BCLE9BQU8sSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUN4QixPQUFPLEVBQUMsT0FBTyxFQUFDLE1BQU0sSUFBSSxDQUFDO0FBQzNCLE9BQU8sRUFBQyxRQUFRLEVBQUMsTUFBTSxlQUFlLENBQUM7QUFDdkMsT0FBTyxZQUFZLE1BQU0sc0JBQXNCLENBQUM7QUFDaEQsT0FBTyxHQUFHLE1BQU0sbUJBQW1CLENBQUM7QUFDcEMsT0FBTyxLQUFLLE1BQU0sT0FBTyxDQUFDO0FBRTFCLE9BQU8sRUFBQyxzQkFBc0IsRUFBRSxTQUFTLEVBQUUsb0JBQW9CLEVBQUMsTUFBTSxZQUFZLENBQUM7QUFFbkYsTUFBTSxZQUFZLEdBQUcsT0FBTyxDQUFDO0FBSTdCOztHQUVHO0FBQ0gsTUFBTSxVQUFVLFVBQVU7SUFDeEIsTUFBTSxlQUFlLEdBQTRCO1FBQy9DLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVTtRQUN0QixPQUFPLENBQUMsR0FBRyxDQUFDLHFCQUFxQjtRQUNqQyxzQkFBc0I7UUFDdEIsOERBQThEO1FBQzlELHdFQUF3RTtRQUN4RSw4RUFBOEU7UUFDOUUsc0VBQXNFO0tBQ3ZFLENBQUM7SUFFRixLQUFLLE1BQU0sU0FBUyxJQUFJLGVBQWUsRUFBRTtRQUN2QyxJQUFJLFNBQVMsSUFBSSxTQUFTLENBQUMsU0FBUyxDQUFDO1lBQUUsT0FBTyxTQUFTLENBQUM7S0FDekQ7SUFFRCxPQUFPLE1BQU0sRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFBO0FBQ3BCLENBQUM7QUFFRCxNQUFNLFVBQVUsTUFBTTtJQUNwQixNQUFNLFFBQVEsR0FBRztRQUNmLCtCQUErQjtRQUMvQixvQ0FBb0M7UUFDcEMsdUNB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
|