Skip to content

Commit c71bb2c

Browse files
authored
[#3] Fix DND for macos-12 (#4)
* fix: try adjustment to applescript * ci: record during ci * chore: bump package version
1 parent f0b4c46 commit c71bb2c

File tree

7 files changed

+127
-60
lines changed

7 files changed

+127
-60
lines changed

.github/workflows/test.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,9 @@ jobs:
2121
node-version: 16
2222
- run: yarn install --frozen-lockfile
2323
- run: yarn ci
24+
- uses: actions/upload-artifact@v2
25+
if: always()
26+
with:
27+
name: artifacts
28+
path: |
29+
**/recordings/**/*

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ If you are using GitHub Actions, check out the dedicated [`guidepup/setup-action
3737
uses: guidepup/[email protected]
3838
```
3939
40+
## Debugging
41+
42+
If you are encountering errors in CI you can pass a `--record` flag to the command which will output a screen-recording of the setup to a `./recordings/` directory.
43+
4044
## See Also 🐶
4145

4246
Check out some of the other Guidepup modules:

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@guidepup/setup",
3-
"version": "0.4.1",
3+
"version": "0.5.0",
44
"description": "Setup your environment for screen-reader automation.",
55
"main": "lib/index.js",
66
"typings": "lib/index.d.ts",
@@ -25,7 +25,7 @@
2525
],
2626
"scripts": {
2727
"build": "yarn clean && yarn compile",
28-
"ci": "yarn clean && yarn lint && yarn build && yarn start --ci",
28+
"ci": "yarn clean && yarn lint && yarn build && yarn start --ci --record",
2929
"clean": "rimraf lib",
3030
"compile": "tsc",
3131
"dev": "ts-node ./src/index.ts",

src/macOS/enableDoNotDisturb.ts

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,36 +12,29 @@ killall usernoted && killall ControlCenter
1212

1313
// source https://www.reddit.com/r/applescript/comments/r9nnil/enable_do_not_disturb_on_monterey/
1414
const enableFocusModeAppleScript = `
15-
tell application "System Preferences"
16-
activate
17-
end tell
15+
set timeoutSeconds to 5.0
1816
19-
tell application "System Events"
20-
tell process "System Preferences"
21-
repeat 25 times
22-
if (exists window "System Preferences") then
23-
click button "Notifications\n& Focus" of scroll area 1 of window "System Preferences"
24-
repeat 25 times
25-
if (exists window "Notifications & Focus") then
26-
click radio button "Focus" of tab group 1 of window "Notifications & Focus"
27-
set focusSwitch to checkbox 1 of group 1 of tab group 1 of window "Notifications & Focus"
28-
tell focusSwitch
29-
if not (its value as boolean) then click focusSwitch
30-
end tell
31-
tell application "System Preferences"
32-
quit
33-
end tell
34-
return
35-
end if
36-
delay 0.2
37-
end repeat
38-
error number 1
39-
end if
40-
delay 0.2
41-
end repeat
42-
error number 1
43-
end tell
44-
end tell
17+
set openSystemPreferences to "tell application \\"System Preferences\\" to activate"
18+
19+
set clickNotificationAndFocusButton to "click UI Element \\"Notifications
20+
& Focus\\" of scroll area 1 of window \\"System Preferences\\" of application process \\"System Preferences\\""
21+
22+
set clickFocusTab to "click radio button \\"Focus\\" of tab group 1 of window \\"Notifications & Focus\\" of application process \\"System Preferences\\""
23+
24+
set enableDoNotDisturb to "
25+
set doNotDisturbToggle to checkbox 1 of group 1 of tab group 1 of window \\"Notifications & Focus\\" of application process \\"System Preferences\\"
26+
27+
tell doNotDisturbToggle
28+
if not (its value as boolean) then click doNotDisturbToggle
29+
end tell"
30+
31+
set closeSystemPreferences to "tell application \\"System Preferences\\" to quit"
32+
33+
my withTimeout(openSystemPreferences, timeoutSeconds)
34+
my withTimeout(clickNotificationAndFocusButton, timeoutSeconds)
35+
my withTimeout(clickFocusTab, timeoutSeconds)
36+
my withTimeout(enableDoNotDisturb, timeoutSeconds)
37+
my withTimeout(closeSystemPreferences, timeoutSeconds)
4538
`;
4639

4740
export async function enableDoNotDisturb() {
@@ -57,7 +50,7 @@ export async function enableDoNotDisturb() {
5750
// From MacOS 12 Monterey (Darwin 21) there is no known way to enable DND via system defaults
5851
try {
5952
await runAppleScript(enableFocusModeAppleScript);
60-
} catch (_) {
53+
} catch (e) {
6154
throw new Error(ERR_MACOS_FAILED_TO_ENABLE_DO_NOT_DISTURB);
6255
}
6356
}

src/macOS/record.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { execSync, spawn } from "child_process";
2+
import { dirname } from "path";
3+
import { unlinkSync } from "fs";
4+
5+
/**
6+
* Start a screen recording.
7+
*
8+
* @param {string} filepath The file path to save the screen recording to.
9+
* @returns {Function} A function to stop the screen recording.
10+
*/
11+
export function record(filepath: string): () => void {
12+
execSync(`mkdir -p ${dirname(filepath)}`);
13+
14+
try {
15+
unlinkSync(filepath);
16+
} catch (_) {
17+
// file doesn't exist.
18+
}
19+
20+
const screencapture = spawn("/usr/sbin/screencapture", [
21+
"-v",
22+
"-C",
23+
"-k",
24+
"-T0",
25+
"-g",
26+
filepath,
27+
]);
28+
29+
return () => {
30+
screencapture.stdin.write("q");
31+
};
32+
}

src/macOS/runAppleScript.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,29 @@ export async function runAppleScript<T = string | void>(
77
script: string,
88
{ timeout = DEFAULT_TIMEOUT } = { timeout: DEFAULT_TIMEOUT }
99
): Promise<T> {
10-
const scriptWithTimeout = `with timeout of ${timeout} seconds\n${script}\nend timeout`;
10+
const scriptWithTimeout = `
11+
with timeout of ${timeout} seconds
12+
${script}
13+
end timeout
14+
15+
on withTimeout(uiScript, timeoutSeconds)
16+
set endDate to (current date) + timeoutSeconds
17+
repeat
18+
try
19+
run script "tell application \\"System Events\\"
20+
" & uiScript & "
21+
end tell"
22+
exit repeat
23+
on error errorMessage
24+
if ((current date) > endDate) then
25+
error errorMessage & "\n\nFor script: " & uiScript
26+
end if
27+
end try
28+
29+
delay 0.2
30+
end repeat
31+
end doWithTimeout
32+
`;
1133

1234
return (await new Promise<string | void>((resolve, reject) => {
1335
const child = execFile(

src/macOS/setup.ts

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,46 +11,56 @@ import { setVoiceOverEnabledViaUi } from "./setVoiceOverEnabledViaUi";
1111
import { logInfo } from "../logging";
1212
import { ERR_MACOS_REQUIRES_MANUAL_USER_INTERACTION } from "../errors";
1313
import { enableDoNotDisturb } from "./enableDoNotDisturb";
14+
import { record } from "./record";
1415

1516
const isCi = process.argv.includes("--ci");
17+
const isRecorded = process.argv.includes("--record");
1618

1719
export async function setup(): Promise<void> {
18-
checkVersion();
19-
enableAppleScriptControlSystemDefaults();
20-
disableSplashScreenSystemDefaults();
21-
disableDictationInputAutoEnable();
20+
const stopRecording = isRecorded
21+
? record(`./recordings/macos-setup-${+new Date()}.mov`)
22+
: () => null;
2223

2324
try {
24-
updateTccDb();
25-
} catch (e) {
26-
if (isCi) {
27-
throw e;
25+
checkVersion();
26+
enableAppleScriptControlSystemDefaults();
27+
disableSplashScreenSystemDefaults();
28+
disableDictationInputAutoEnable();
29+
30+
try {
31+
updateTccDb();
32+
} catch (e) {
33+
if (isCi) {
34+
throw e;
35+
}
2836
}
29-
}
3037

31-
if (isCi) {
32-
await enableDoNotDisturb();
33-
}
38+
if (isCi) {
39+
await enableDoNotDisturb();
40+
}
3441

35-
if (!isSipEnabled()) {
36-
writeDatabaseFile();
42+
if (!isSipEnabled()) {
43+
writeDatabaseFile();
3744

38-
return;
39-
}
45+
return;
46+
}
4047

41-
if (await isAppleScriptControlEnabled()) {
42-
return;
43-
}
48+
if (await isAppleScriptControlEnabled()) {
49+
return;
50+
}
4451

45-
if (isCi) {
46-
throw new Error(ERR_MACOS_REQUIRES_MANUAL_USER_INTERACTION);
47-
}
52+
if (isCi) {
53+
throw new Error(ERR_MACOS_REQUIRES_MANUAL_USER_INTERACTION);
54+
}
4855

49-
const credentials = await askUserToControlUi();
56+
const credentials = await askUserToControlUi();
5057

51-
logInfo("");
52-
logInfo("Starting UI control...");
53-
logInfo("Please refrain from interaction until the script has completed");
58+
logInfo("");
59+
logInfo("Starting UI control...");
60+
logInfo("Please refrain from interaction until the script has completed");
5461

55-
await setVoiceOverEnabledViaUi(credentials);
62+
await setVoiceOverEnabledViaUi(credentials);
63+
} finally {
64+
stopRecording();
65+
}
5666
}

0 commit comments

Comments
 (0)