Skip to content

Commit 7950938

Browse files
committed
Merge branch 'development' into releases/3.4.16
2 parents ba99738 + b7046a0 commit 7950938

26 files changed

+496
-543
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ on:
3838
APPLE_APPLICATION_CERT_PASSWORD:
3939

4040
env:
41-
NODE_VERSION: 20.17.0
41+
NODE_VERSION: 20.18.1
4242

4343
jobs:
4444
lint:

.node-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
20.17.0
1+
20.18.1

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v20.17.0
1+
v20.18.1

.tool-versions

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
python 3.9.5
2-
nodejs 20.17.0
2+
nodejs 20.18.1

app/.npmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
runtime = electron
22
disturl = https://electronjs.org/headers
3-
target = 32.1.2
3+
target = 34.0.1

app/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
"desktop-trampoline": "desktop/desktop-trampoline#v0.9.10",
3434
"dexie": "^3.2.2",
3535
"dompurify": "^2.5.4",
36-
"dugite": "3.0.0-rc6",
36+
"dugite": "3.0.0-rc10",
3737
"electron-window-state": "^5.0.3",
3838
"event-kit": "^2.0.0",
3939
"focus-trap-react": "^8.1.0",
@@ -72,7 +72,7 @@
7272
"winston": "^3.6.0"
7373
},
7474
"devDependencies": {
75-
"electron-devtools-installer": "^3.2.1",
75+
"electron-devtools-installer": "^4.0.0",
7676
"temp": "^0.8.3",
7777
"webpack-hot-middleware": "^2.10.0"
7878
}

app/src/cli/main.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,31 @@
11
import { join, resolve } from 'path'
22
import parse from 'minimist'
3-
import { execFile, ExecFileException } from 'child_process'
3+
import { execFile, spawn } from 'child_process'
44

55
const run = (...args: Array<string>) => {
6-
function cb(e: ExecFileException | null, stderr: string) {
6+
function cb(e: unknown | null, stderr?: string) {
77
if (e) {
88
console.error(`Error running command ${args}`)
9-
console.error(stderr)
10-
process.exit(e.code)
9+
console.error(stderr ?? `${e}`)
10+
process.exit(
11+
typeof e === 'object' && 'code' in e && typeof e.code === 'number'
12+
? e.code
13+
: 1
14+
)
1115
}
1216
}
1317

1418
if (process.platform === 'darwin') {
1519
execFile('open', ['-n', join(__dirname, '../../..'), '--args', ...args], cb)
1620
} else if (process.platform === 'win32') {
1721
const exeName = `GitHubDesktop${__DEV__ ? '-dev' : ''}.exe`
18-
execFile(join(__dirname, `../../${exeName}`), args, cb)
22+
spawn(join(__dirname, `../../${exeName}`), args, {
23+
detached: true,
24+
stdio: 'ignore',
25+
})
26+
.on('error', cb)
27+
.on('exit', code => (process.exitCode = code ?? process.exitCode))
28+
.unref()
1929
} else {
2030
throw new Error('Unsupported platform')
2131
}

app/src/lib/custom-integration.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import { ChildProcess, SpawnOptions, spawn } from 'child_process'
21
import { parseCommandLineArgv } from 'windows-argv-parser'
32
import stringArgv from 'string-argv'
43
import { promisify } from 'util'
5-
import { exec } from 'child_process'
4+
import { exec, spawn, SpawnOptions } from 'child_process'
65
import { access, lstat } from 'fs/promises'
76
import * as fs from 'fs'
87

@@ -118,7 +117,9 @@ export async function validateCustomIntegrationPath(
118117

119118
return { isValid: isExecutableFile || !!bundleID, bundleID }
120119
} catch (e) {
121-
log.error(`Failed to validate path: ${path}`, e)
120+
if (e.code !== 'ENOENT') {
121+
log.error(`Failed to validate path: ${path}`, e)
122+
}
122123
return { isValid: false }
123124
}
124125
}
@@ -183,15 +184,13 @@ export function migratedCustomIntegration(
183184
* on Windows, where we need to wrap the command and arguments in quotes when
184185
* the shell option is enabled.
185186
*
186-
* @param command Command to spawn
187+
* @param cmd Command to spawn
187188
* @param args Arguments to pass to the command
188189
* @param options Options to pass to spawn (optional)
189190
* @returns The ChildProcess object returned by spawn
190191
*/
191-
export function spawnCustomIntegration(
192-
command: string,
192+
export const spawnCustomIntegration = (
193+
cmd: string,
193194
args: readonly string[],
194-
options?: SpawnOptions
195-
): ChildProcess {
196-
return options ? spawn(command, args, options) : spawn(command, args)
197-
}
195+
opts?: SpawnOptions
196+
) => spawn(cmd, args, { stdio: 'ignore', detached: true, ...opts })

app/src/lib/editors/launch.ts

Lines changed: 52 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -5,116 +5,82 @@ import {
55
expandTargetPathArgument,
66
ICustomIntegration,
77
parseCustomIntegrationArguments,
8-
spawnCustomIntegration,
98
} from '../custom-integration'
109

11-
/**
12-
* Open a given file or folder in the desired external editor.
13-
*
14-
* @param fullPath A folder or file path to pass as an argument when launching the editor.
15-
* @param editor The external editor to launch.
16-
*/
17-
export async function launchExternalEditor(
18-
fullPath: string,
19-
editor: FoundEditor
20-
): Promise<void> {
21-
const editorPath = editor.path
10+
async function launchEditor(
11+
editorPath: string,
12+
args: readonly string[],
13+
editorName: string,
14+
spawnAsDarwinApp: boolean
15+
) {
2216
const exists = await pathExists(editorPath)
2317
const label = __DARWIN__ ? 'Settings' : 'Options'
2418
if (!exists) {
2519
throw new ExternalEditorError(
26-
`Could not find executable for '${editor.editor}' at path '${editor.path}'. Please open ${label} and select an available editor.`,
20+
`Could not find executable for ${editorName} at path '${editorPath}'. Please open ${label} and select an available editor.`,
2721
{ openPreferences: true }
2822
)
2923
}
3024

31-
const opts: SpawnOptions = {
32-
// Make sure the editor processes are detached from the Desktop app.
33-
// Otherwise, some editors (like Notepad++) will be killed when the
34-
// Desktop app is closed.
35-
detached: true,
36-
}
37-
38-
try {
39-
if (__DARWIN__) {
40-
// In macOS we can use `open`, which will open the right executable file
41-
// for us, we only need the path to the editor .app folder.
42-
spawn('open', ['-a', editorPath, fullPath], opts)
43-
} else {
44-
spawn(editorPath, [fullPath], opts)
25+
return new Promise<void>((resolve, reject) => {
26+
const opts: SpawnOptions = {
27+
// Make sure the editor processes are detached from the Desktop app.
28+
// Otherwise, some editors (like Notepad++) will be killed when the
29+
// Desktop app is closed.
30+
detached: true,
31+
stdio: 'ignore',
4532
}
46-
} catch (error) {
47-
log.error(`Error while launching ${editor.editor}`, error)
48-
if (error?.code === 'EACCES') {
49-
throw new ExternalEditorError(
50-
`GitHub Desktop doesn't have the proper permissions to start '${editor.editor}'. Please open ${label} and try another editor.`,
51-
{ openPreferences: true }
52-
)
53-
} else {
54-
throw new ExternalEditorError(
55-
`Something went wrong while trying to start '${editor.editor}'. Please open ${label} and try another editor.`,
56-
{ openPreferences: true }
57-
)
58-
}
59-
}
33+
34+
const child = spawnAsDarwinApp
35+
? spawn('open', ['-a', editorPath, ...args], opts)
36+
: spawn(editorPath, args, opts)
37+
38+
child.on('error', reject)
39+
child.on('spawn', resolve)
40+
child.unref() // Don't wait for editor to exit
41+
}).catch((e: unknown) => {
42+
log.error(
43+
`Error while launching ${editorName}`,
44+
e instanceof Error ? e : undefined
45+
)
46+
throw new ExternalEditorError(
47+
e && typeof e === 'object' && 'code' in e && e.code === 'EACCES'
48+
? `GitHub Desktop doesn't have the proper permissions to start ${editorName}. Please open ${label} and try another editor.`
49+
: `Something went wrong while trying to start ${editorName}. Please open ${label} and try another editor.`,
50+
{ openPreferences: true }
51+
)
52+
})
6053
}
6154

55+
/**
56+
* Open a given file or folder in the desired external editor.
57+
*
58+
* @param fullPath A folder or file path to pass as an argument when launching the editor.
59+
* @param editor The external editor to launch.
60+
*/
61+
export const launchExternalEditor = (fullPath: string, editor: FoundEditor) =>
62+
launchEditor(editor.path, [fullPath], `'${editor.editor}'`, __DARWIN__)
63+
6264
/**
6365
* Open a given file or folder in the desired custom external editor.
6466
*
6567
* @param fullPath A folder or file path to pass as an argument when launching the editor.
6668
* @param customEditor The external editor to launch.
6769
*/
68-
export async function launchCustomExternalEditor(
70+
export const launchCustomExternalEditor = (
6971
fullPath: string,
7072
customEditor: ICustomIntegration
71-
): Promise<void> {
72-
const editorPath = customEditor.path
73-
const exists = await pathExists(editorPath)
74-
const label = __DARWIN__ ? 'Settings' : 'Options'
75-
if (!exists) {
76-
throw new ExternalEditorError(
77-
`Could not find executable for custom editor at path '${customEditor.path}'. Please open ${label} and select an available editor.`,
78-
{ openPreferences: true }
79-
)
80-
}
81-
82-
const opts: SpawnOptions = {
83-
// Make sure the editor processes are detached from the Desktop app.
84-
// Otherwise, some editors (like Notepad++) will be killed when the
85-
// Desktop app is closed.
86-
detached: true,
87-
}
88-
73+
) => {
8974
const argv = parseCustomIntegrationArguments(customEditor.arguments)
9075

9176
// Replace instances of RepoPathArgument with fullPath in customEditor.arguments
9277
const args = expandTargetPathArgument(argv, fullPath)
9378

94-
try {
95-
if (__DARWIN__ && customEditor.bundleID) {
96-
// In macOS we can use `open` if it's an app (i.e. if we have a bundleID),
97-
// which will open the right executable file for us, we only need the path
98-
// to the editor .app folder.
99-
spawnCustomIntegration('open', ['-a', editorPath, ...args], opts)
100-
} else {
101-
spawnCustomIntegration(editorPath, args, opts)
102-
}
103-
} catch (error) {
104-
log.error(
105-
`Error while launching custom editor at path ${customEditor.path} with arguments ${args}`,
106-
error
107-
)
108-
if (error?.code === 'EACCES') {
109-
throw new ExternalEditorError(
110-
`GitHub Desktop doesn't have the proper permissions to start custom editor at path ${customEditor.path}. Please open ${label} and try another editor.`,
111-
{ openPreferences: true }
112-
)
113-
} else {
114-
throw new ExternalEditorError(
115-
`Something went wrong while trying to start custom editor at path ${customEditor.path}. Please open ${label} and try another editor.`,
116-
{ openPreferences: true }
117-
)
118-
}
119-
}
79+
// In macOS we can use `open` if it's an app (i.e. if we have a bundleID),
80+
// which will open the right executable file for us, we only need the path
81+
// to the editor .app folder.
82+
const spawnAsDarwinApp = __DARWIN__ && customEditor.bundleID !== undefined
83+
const editorName = `custom editor at path '${customEditor.path}'`
84+
85+
return launchEditor(customEditor.path, args, editorName, spawnAsDarwinApp)
12086
}

app/src/main-process/app-window.ts

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -151,29 +151,6 @@ export class AppWindow {
151151
autoUpdater.removeAllListeners()
152152
terminateDesktopNotifications()
153153
})
154-
155-
if (__WIN32__) {
156-
// workaround for known issue with fullscreen-ing the app and restoring
157-
// is that some Chromium API reports the incorrect bounds, so that it
158-
// will leave a small space at the top of the screen on every other
159-
// maximize
160-
//
161-
// adapted from https://github.com/electron/electron/issues/12971#issuecomment-403956396
162-
//
163-
// can be tidied up once https://github.com/electron/electron/issues/12971
164-
// has been confirmed as resolved
165-
this.window.once('ready-to-show', () => {
166-
this.window.on('unmaximize', () => {
167-
setTimeout(() => {
168-
const bounds = this.window.getBounds()
169-
bounds.width += 1
170-
this.window.setBounds(bounds)
171-
bounds.width -= 1
172-
this.window.setBounds(bounds)
173-
}, 5)
174-
})
175-
})
176-
}
177154
}
178155

179156
public load() {
@@ -226,7 +203,7 @@ export class AppWindow {
226203
registerWindowStateChangedEvents(this.window)
227204
this.window.loadURL(encodePathAsUrl(__dirname, 'index.html'))
228205

229-
nativeTheme.addListener('updated', (event: string, userInfo: any) => {
206+
nativeTheme.addListener('updated', () => {
230207
ipcWebContents.send(this.window.webContents, 'native-theme-updated')
231208
})
232209

0 commit comments

Comments
 (0)