Skip to content

Commit d40df9e

Browse files
committed
feat: added another QOL change to prevent sleep while codify apply or destroy is running
1 parent 8619ec6 commit d40df9e

8 files changed

Lines changed: 96 additions & 14 deletions

File tree

src/commands/apply.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ For more information, visit: https://codifycli.com/docs/commands/apply
3434
char: 'v',
3535
description: 'Print plugin output (stdout/stderr) to the terminal.',
3636
}),
37+
'allow-sleep': Flags.boolean({
38+
description: 'Allow the system to sleep during apply operations.',
39+
default: false,
40+
}),
3741
}
3842

3943
static args = {
@@ -58,6 +62,7 @@ For more information, visit: https://codifycli.com/docs/commands/apply
5862
path: flags.path ?? args.pathArgs,
5963
verbosityLevel: flags.debug || flags.verbose ? 3 : 0,
6064
autoApprove: flags.yes,
65+
allowSleep: flags['allow-sleep'],
6166
// secure: flags.secure,
6267
}, this.reporter);
6368

src/commands/destroy.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ For more information, visit: https://codifycli.com/docs/commands/destory`
4242
char: 'v',
4343
description: 'Print plugin output (stdout/stderr) to the terminal.',
4444
}),
45+
'allow-sleep': Flags.boolean({
46+
description: 'Allow the system to sleep during destroy operations.',
47+
default: false,
48+
}),
4549
}
4650

4751
public async run(): Promise<void> {
@@ -56,6 +60,7 @@ For more information, visit: https://codifycli.com/docs/commands/destory`
5660
typeIds: args,
5761
path: flags.path,
5862
autoApprove: flags.yes,
63+
allowSleep: flags['allow-sleep'],
5964
}, this.reporter)
6065

6166
process.exit(0);

src/orchestrators/apply.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ProcessName, ctx } from '../events/context.js';
22
import { DefaultReporter } from '../ui/reporters/default-reporter.js';
33
import { Reporter } from '../ui/reporters/reporter.js';
4+
import { SleepInhibitor } from '../utils/sleep-inhibitor.js';
45
import { VerbosityLevel } from '../utils/verbosity-level.js';
56
import { PlanOrchestrator } from './plan.js';
67

@@ -10,6 +11,7 @@ export interface ApplyArgs {
1011
verbosityLevel?: number;
1112
noProgress?: boolean;
1213
autoApprove?: boolean;
14+
allowSleep?: boolean;
1315
}
1416

1517
export const ApplyOrchestrator = {
@@ -40,17 +42,27 @@ export const ApplyOrchestrator = {
4042
});
4143
}
4244

43-
if (!args.noProgress) ctx.processStarted(ProcessName.APPLY);
44-
if (!args.noProgress) await reporter.displayProgress();
45+
const inhibitor = args.allowSleep ? null : SleepInhibitor.start();
46+
if (inhibitor && reporter instanceof DefaultReporter) {
47+
reporter.setSleepPrevented(true);
48+
}
49+
50+
try {
51+
if (!args.noProgress) ctx.processStarted(ProcessName.APPLY);
52+
if (!args.noProgress) await reporter.displayProgress();
4553

46-
const applyResult = await pluginManager.apply(project, filteredPlan);
54+
const applyResult = await pluginManager.apply(project, filteredPlan);
4755

48-
if (!args.noProgress) ctx.processFinished(ProcessName.APPLY);
56+
if (!args.noProgress) ctx.processFinished(ProcessName.APPLY);
4957

50-
await reporter.displayApplyComplete(applyResult);
58+
await reporter.displayApplyComplete(applyResult);
5159

52-
if (applyResult.isPartialFailure()) {
53-
process.exit(1);
60+
if (applyResult.isPartialFailure()) {
61+
process.exit(1);
62+
}
63+
} finally {
64+
inhibitor?.stop();
5465
}
66+
5567
},
5668
};

src/orchestrators/destroy.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { ProcessName, SubProcessName, ctx } from '../events/context.js';
77
import { PluginManager, ResourceDefinitionMap } from '../plugins/plugin-manager.js';
88
import { DefaultReporter } from '../ui/reporters/default-reporter.js';
99
import { PromptType, Reporter } from '../ui/reporters/reporter.js';
10+
import { SleepInhibitor } from '../utils/sleep-inhibitor.js';
1011
import { wildCardMatch } from '../utils/wild-card-match.js';
1112

1213
export interface DestroyArgs {
@@ -15,6 +16,7 @@ export interface DestroyArgs {
1516
secureMode?: boolean;
1617
verbosityLevel?: number;
1718
autoApprove?: boolean;
19+
allowSleep?: boolean;
1820
}
1921

2022
export class DestroyOrchestrator {
@@ -69,15 +71,24 @@ export class DestroyOrchestrator {
6971
});
7072
}
7173

72-
await reporter.displayProgress();
73-
const applyResult = await ctx.process(ProcessName.DESTROY, () =>
74-
pluginManager.apply(destroyProject, filteredPlan)
75-
)
74+
const inhibitor = args.allowSleep ? null : SleepInhibitor.start();
75+
if (inhibitor && reporter instanceof DefaultReporter) {
76+
reporter.setSleepPrevented(true);
77+
}
7678

77-
await reporter.displayApplyComplete(applyResult);
79+
try {
80+
await reporter.displayProgress();
81+
const applyResult = await ctx.process(ProcessName.DESTROY, () =>
82+
pluginManager.apply(destroyProject, filteredPlan)
83+
)
7884

79-
if (applyResult.isPartialFailure()) {
80-
process.exit(1);
85+
await reporter.displayApplyComplete(applyResult);
86+
87+
if (applyResult.isPartialFailure()) {
88+
process.exit(1);
89+
}
90+
} finally {
91+
inhibitor?.stop();
8192
}
8293
}
8394

src/ui/components/progress/progress-display.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export function ProgressDisplay(props: { emitter: EventEmitter }) {
3030
const [progress] = useAtom(store.progressState);
3131
const [isVerbose, setIsVerbose] = useState(() => VerbosityLevel.get() > 0);
3232
const [passwordSaved] = useAtom(store.isSudoPasswordCached);
33+
const [sleepPrevented] = useAtom(store.isSleepPrevented);
3334

3435
const isApplyOrDestroy = progress?.name === ProcessName.APPLY || progress?.name === ProcessName.DESTROY;
3536

@@ -66,6 +67,7 @@ export function ProgressDisplay(props: { emitter: EventEmitter }) {
6667
? <Text color="green">✓ sudo password</Text>
6768
: <Text dimColor>[p] Enter sudo password</Text>
6869
}
70+
{sleepPrevented && <Text color="green">✓ sleep prevented</Text>}
6971
</Box>
7072
)}
7173
</Box>

src/ui/reporters/default-reporter.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ export class DefaultReporter implements Reporter {
8585
store.set(store.isSudoPasswordCached, true);
8686
}
8787

88+
setSleepPrevented(value: boolean): void {
89+
store.set(store.isSleepPrevented, value);
90+
}
91+
8892
async promptPressKeyToContinue(message?: string): Promise<void> {
8993
const previousRenderState = this.getRenderState();
9094

src/ui/store/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export const store = new class {
3636

3737
progressState = atom(null as ProgressState | null)
3838
isSudoPasswordCached = atom(false)
39+
isSleepPrevented = atom(false)
3940

4041
get<Value>(atom: Atom<Value>): Value {
4142
return this.internal.get(atom);

src/utils/sleep-inhibitor.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { spawn, type ChildProcess } from 'node:child_process';
2+
3+
import { OsUtils } from './os-utils.js';
4+
5+
export class SleepInhibitor {
6+
private process: ChildProcess;
7+
8+
private constructor(process: ChildProcess) {
9+
this.process = process;
10+
}
11+
12+
static start(): SleepInhibitor | null {
13+
let child: ChildProcess;
14+
15+
if (OsUtils.isMacOS()) {
16+
child = spawn('caffeinate', ['-i'], { stdio: 'ignore', detached: false });
17+
} else if (OsUtils.isLinux()) {
18+
child = spawn(
19+
'systemd-inhibit',
20+
['--what=sleep', '--why=Codify apply in progress', '--mode=block', 'sleep', 'infinity'],
21+
{ stdio: 'ignore', detached: false }
22+
);
23+
} else {
24+
return null;
25+
}
26+
27+
child.unref();
28+
29+
const inhibitor = new SleepInhibitor(child);
30+
for (const sig of ['exit', 'SIGINT', 'SIGTERM', 'uncaughtException']) {
31+
process.on(sig, () => inhibitor.stop());
32+
}
33+
34+
return inhibitor;
35+
}
36+
37+
stop(): void {
38+
if (!this.process.killed) {
39+
this.process.kill();
40+
}
41+
}
42+
}

0 commit comments

Comments
 (0)