Skip to content

Commit f9d342c

Browse files
committed
feat: Added toggle for adding sudo password during apply
1 parent a0f241c commit f9d342c

4 files changed

Lines changed: 48 additions & 67 deletions

File tree

src/common/base-command.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ export abstract class BaseCommand extends Command {
5252
this.reporter.notifySudoPasswordPreSupplied();
5353
}
5454

55-
this.reporter.onSudoPasswordSubmitted(async (password: string) => {
55+
this.reporter.onSudoPasswordSubmitted((password: string) => {
5656
const isValid = SudoUtils.validate(password);
5757
if (isValid) {
5858
cachedSudoPassword = password;
5959
}
60-
(this.reporter as DefaultReporter).notifySudoPasswordResult(isValid);
60+
return isValid;
6161
});
6262
}
6363

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

Lines changed: 6 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { PasswordInput } from '@inkjs/ui';
21
import { Box, Text, useInput } from 'ink';
32
import { useAtom } from 'jotai';
43
import { EventEmitter } from 'node:events';
@@ -29,43 +28,16 @@ export function ProgressDisplay(props: { emitter: EventEmitter }) {
2928
const { emitter } = props;
3029
const [progress] = useAtom(store.progressState);
3130
const [isVerbose, setIsVerbose] = useState(false);
32-
const [isEnteringPassword, setIsEnteringPassword] = useState(false);
33-
const [passwordError, setPasswordError] = useState(false);
34-
const [passwordAttempts, setPasswordAttempts] = useState(0);
3531
const [passwordSaved, setPasswordSaved] = useState(false);
36-
const [passwordInputKey, setPasswordInputKey] = useState(0);
3732

3833
const isApplyOrDestroy = progress?.name === ProcessName.APPLY || progress?.name === ProcessName.DESTROY;
3934

4035
useLayoutEffect(() => {
41-
const onResult = ({ success }: { success: boolean }) => {
42-
if (success) {
43-
setPasswordSaved(true);
44-
setIsEnteringPassword(false);
45-
setPasswordError(false);
46-
setPasswordAttempts(0);
47-
} else {
48-
setPasswordAttempts((prev) => {
49-
const next = prev + 1;
50-
if (next >= 3) {
51-
setIsEnteringPassword(false);
52-
setPasswordError(false);
53-
return 0;
54-
}
55-
setPasswordError(true);
56-
setPasswordInputKey((k) => k + 1);
57-
return next;
58-
});
59-
}
60-
};
61-
6236
const onPreSupplied = () => setPasswordSaved(true);
6337

64-
emitter.on(RenderEvent.SUDO_PASSWORD_RESULT, onResult);
6538
emitter.on(RenderEvent.SUDO_PASSWORD_PRE_SUPPLIED, onPreSupplied);
6639

6740
return () => {
68-
emitter.off(RenderEvent.SUDO_PASSWORD_RESULT, onResult);
6941
emitter.off(RenderEvent.SUDO_PASSWORD_PRE_SUPPLIED, onPreSupplied);
7042
};
7143
}, []);
@@ -77,10 +49,7 @@ export function ProgressDisplay(props: { emitter: EventEmitter }) {
7749
emitter.emit(RenderEvent.TOGGLE_VERBOSITY);
7850
}
7951
if (input === 'p' && !passwordSaved) {
80-
setIsEnteringPassword((prev) => !prev);
81-
setPasswordError(false);
82-
setPasswordAttempts(0);
83-
setPasswordInputKey((k) => k + 1);
52+
emitter.emit(RenderEvent.SUDO_PASSWORD_TOGGLE);
8453
}
8554
});
8655

@@ -96,33 +65,15 @@ export function ProgressDisplay(props: { emitter: EventEmitter }) {
9665
? <Spinner label={label} type="dots" />
9766
: <Text><Text color='greenBright'></Text> {label}</Text>
9867
}
99-
100-
{!isEnteringPassword && (
101-
<Box flexDirection="column" marginLeft={2}>
102-
<SubProgressDisplay subProgresses={subProgresses}/>
103-
</Box>
104-
)}
105-
106-
{isEnteringPassword && (
107-
<Box flexDirection="column" marginTop={1}>
108-
<Text color={passwordError ? 'red' : 'cyan'}>{'─'.repeat(40)}</Text>
109-
<Box>
110-
<Text> Password: </Text>
111-
<PasswordInput key={passwordInputKey} onSubmit={(pw) => emitter.emit(RenderEvent.SUDO_PASSWORD_SUBMITTED, pw)} />
112-
</Box>
113-
{passwordError && (
114-
<Text color="red">{` Incorrect password, try again (${passwordAttempts}/3)`}</Text>
115-
)}
116-
<Text color={passwordError ? 'red' : 'cyan'}>{'─'.repeat(40)}</Text>
117-
</Box>
118-
)}
119-
68+
<Box flexDirection="column" marginLeft={2}>
69+
<SubProgressDisplay subProgresses={subProgresses}/>
70+
</Box>
12071
{isApplyOrDestroy && (
121-
<Box flexDirection="column">
72+
<Box flexDirection="row" gap={2}>
12273
<Text dimColor>{isVerbose ? '[v] Hide verbose logs' : '[v] Show verbose logs'}</Text>
12374
{passwordSaved
12475
? <Text color="green">✓ sudo password</Text>
125-
: <Text dimColor>{isEnteringPassword ? '[p] Cancel' : '[p] Enter sudo password'}</Text>
76+
: <Text dimColor>[p] Enter sudo password</Text>
12677
}
12778
</Box>
12879
)}

src/ui/reporters/default-reporter.tsx

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export class DefaultReporter implements Reporter {
4747
private renderEmitter = new EventEmitter();
4848
private progressState: ProgressState | null = null
4949
private verbosityToggleCallback: (() => void) | null = null;
50-
private sudoPasswordSubmittedCallback: ((password: string) => void) | null = null;
50+
private sudoPasswordSubmittedCallback: ((password: string) => boolean) | null = null;
5151
silent = false;
5252

5353
constructor() {
@@ -63,23 +63,19 @@ export class DefaultReporter implements Reporter {
6363
this.verbosityToggleCallback?.();
6464
});
6565

66-
this.renderEmitter.on(RenderEvent.SUDO_PASSWORD_SUBMITTED, (password: string) => {
67-
this.sudoPasswordSubmittedCallback?.(password);
66+
this.renderEmitter.on(RenderEvent.SUDO_PASSWORD_TOGGLE, () => {
67+
this.handleInlineSudoPassword();
6868
});
6969
}
7070

7171
onVerbosityToggle(callback: () => void): void {
7272
this.verbosityToggleCallback = callback;
7373
}
7474

75-
onSudoPasswordSubmitted(callback: (password: string) => void): void {
75+
onSudoPasswordSubmitted(callback: (password: string) => boolean): void {
7676
this.sudoPasswordSubmittedCallback = callback;
7777
}
7878

79-
notifySudoPasswordResult(success: boolean): void {
80-
this.renderEmitter.emit(RenderEvent.SUDO_PASSWORD_RESULT, { success });
81-
}
82-
8379
notifySudoPasswordPreSupplied(): void {
8480
setImmediate(() => this.renderEmitter.emit(RenderEvent.SUDO_PASSWORD_PRE_SUPPLIED));
8581
}
@@ -327,6 +323,41 @@ export class DefaultReporter implements Reporter {
327323
store.set(store.progressState, structuredClone(this.progressState));
328324
}
329325

326+
private async handleInlineSudoPassword(): Promise<void> {
327+
let attemptCount = 0;
328+
329+
while (attemptCount < 3) {
330+
this.renderEmitter.emit(RenderEvent.DISABLE_SUDO_PROMPT, false);
331+
const passwordAttempt = await this.updateStateAndAwaitEvent<string>(
332+
() => this.updateRenderState(RenderStatus.SUDO_PROMPT, attemptCount),
333+
RenderEvent.SUDO_PROMPT_RESULT,
334+
);
335+
this.renderEmitter.emit(RenderEvent.DISABLE_SUDO_PROMPT, true);
336+
337+
const isValid = this.sudoPasswordSubmittedCallback?.(passwordAttempt) ?? false;
338+
if (isValid) {
339+
await sleep(50);
340+
this.updateRenderState(RenderStatus.NOTHING, null);
341+
await sleep(50);
342+
await this.displayProgress();
343+
this.renderEmitter.emit(RenderEvent.SUDO_PASSWORD_PRE_SUPPLIED);
344+
return;
345+
}
346+
347+
if (attemptCount + 1 < 3) {
348+
ctx.log(chalk.red(`Sorry, try again. (${attemptCount + 1}/3)`));
349+
}
350+
351+
attemptCount++;
352+
}
353+
354+
// All attempts exhausted — restore progress display without marking saved
355+
await sleep(50);
356+
this.updateRenderState(RenderStatus.NOTHING, null);
357+
await sleep(50);
358+
await this.displayProgress();
359+
}
360+
330361
private async getUserPassword(): Promise<string> {
331362
let attemptCount = 0;
332363

src/ui/reporters/reporter.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ export enum RenderEvent {
2424
SUDO_PROMPT_RESULT = 'promptSudoResult',
2525
STATE_TRANSITION = 'stateTransition',
2626
TOGGLE_VERBOSITY = 'toggleVerbosity',
27-
SUDO_PASSWORD_SUBMITTED = 'sudoPasswordSubmitted',
28-
SUDO_PASSWORD_RESULT = 'sudoPasswordResult',
27+
SUDO_PASSWORD_TOGGLE = 'sudoPasswordToggle',
2928
SUDO_PASSWORD_PRE_SUPPLIED = 'sudoPasswordPreSupplied',
3029
}
3130

0 commit comments

Comments
 (0)