Skip to content

Commit c40aeea

Browse files
committed
WIP: fixed the test command and added a promptSecret to collect the user's password initially
1 parent 920fb9f commit c40aeea

File tree

11 files changed

+80
-19
lines changed

11 files changed

+80
-19
lines changed

src/orchestrators/test.ts

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { sleep } from '../utils/index.js';
1010
import { spawn, spawnSafe } from '../utils/spawn.js';
1111
import { PlanOrchestrator, PlanOrchestratorResponse } from './plan.js';
1212
import { ValidateOrchestrator } from './validate.js';
13+
import { FileUtils } from '../utils/file.js';
1314

1415
export interface TestArgs {
1516
path?: string;
@@ -25,12 +26,14 @@ export const TestOrchestrator = {
2526

2627
ctx.subprocessStarted(SubProcessName.TEST_INITIALIZE_AND_VALIDATE);
2728
// Perform validation initially to ensure the project is valid
28-
const initializationResult = await PluginInitOrchestrator.run({ ...args, noProgress: true }, new StubReporter());
29-
await ValidateOrchestrator.run({ existing: initializationResult, noProgress: true }, new StubReporter());
29+
const initializationResult = await PluginInitOrchestrator.run({ ...args, noProgress: true }, reporter);
30+
await ValidateOrchestrator.run({ existing: initializationResult, noProgress: true }, reporter);
3031
ctx.subprocessFinished(SubProcessName.TEST_INITIALIZE_AND_VALIDATE);
3132

3233
await this.ensureVmIsInstalled(reporter, args.vmOs);
3334

35+
const password = await reporter.promptSecret('Password needed to copy Codify installation to VM...');
36+
3437
ctx.subprocessStarted(SubProcessName.TEST_STARTING_VM);
3538
const baseVmName = args.vmOs === OS.Darwin ? 'codify-test-vm-macos' : 'codify-test-vm-linux';
3639
const vmName = this.generateVmName();
@@ -49,7 +52,7 @@ export const TestOrchestrator = {
4952
console.log('VM has been killed... exiting.')
5053
process.exit(1);
5154
})
52-
await sleep(10_000);
55+
await sleep(5_000);
5356
await this.waitUntilVmIsReady(vmName);
5457

5558
ctx.subprocessFinished(SubProcessName.TEST_STARTING_VM);
@@ -58,12 +61,25 @@ export const TestOrchestrator = {
5861
// Install codify on the VM
5962
// await spawn(`tart exec ${vmName} /bin/bash -c "$(curl -fsSL https://releases.codifycli.com/install.sh)"`, { interactive: true });
6063
const { data: ip } = await spawnSafe(`tart ip ${vmName}`, { interactive: true });
61-
await spawn(`sshpass -p "admin" scp -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${initializationResult.project.codifyFiles[0]} admin@${ip}:~/codify.jsonc`, { interactive: true });
6264

63-
if (args.vmOs === OS.Darwin) {
64-
await spawn(`tart exec ${vmName} osascript -e "tell application \\"Terminal\\" to do script \\"cd ~/ && codify apply\\""`, { interactive: true });
65-
} else {
66-
await spawn(`tart exec ${vmName} gnome-terminal -- bash -c "cd ~/ && codify apply"`, { interactive: true });
65+
await spawn(`sshpass -p "admin" rsync -avz -e 'ssh -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' /usr/local/lib/codify admin@${ip}:~/codify-lib`, { requiresRoot: true }, undefined, password);
66+
67+
if (await FileUtils.dirExists('~/.local/share/codify')) {
68+
await spawn(`sshpass -p "admin" rsync -avz -e 'ssh -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' ~/.local/share/codify admin@${ip}:~/.local/share/codify`, { interactive: true });
69+
}
70+
71+
try {
72+
await spawn(`tart exec ${vmName} sudo mv /Users/admin/codify-lib /usr/local/lib`, { interactive: true });
73+
await spawn(`tart exec ${vmName} sudo ln -s /usr/local/lib/codify/bin/codify /usr/local/bin/codify`, { interactive: true });
74+
await spawn(`sshpass -p "admin" scp -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${initializationResult.project.codifyFiles[0]} admin@${ip}:~/codify.jsonc`, { interactive: true });
75+
76+
if (args.vmOs === OS.Darwin) {
77+
await spawn(`tart exec ${vmName} osascript -e "tell application \\"Terminal\\" to do script \\"cd ~/ && codify apply\\""`, { interactive: true });
78+
} else {
79+
await spawn(`tart exec ${vmName} gnome-terminal -- bash -c "cd ~/ && codify apply"`, { interactive: true });
80+
}
81+
} catch (e) {
82+
ctx.log(`Error copying files to VM: ${e}`);
6783
}
6884

6985
ctx.subprocessFinished(SubProcessName.TEST_COPYING_OVER_CONFIGS_AND_OPENING_TERMINAL);

src/ui/components/default-component.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,16 @@ export function DefaultComponent(props: {
8989
</Box>
9090
)
9191
}
92+
{
93+
renderStatus === RenderStatus.SECRET_PROMPT && (
94+
<Box flexDirection="column">
95+
<Text>{renderData as string}</Text>
96+
<PasswordInput isDisabled={disableSudoPrompt} onSubmit={(password) => {
97+
emitter.emit(RenderEvent.PROMPT_RESULT, password);
98+
}}/>
99+
</Box>
100+
)
101+
}
92102
{
93103
renderStatus === RenderStatus.SUDO_PROMPT && (
94104
<Box flexDirection="column">

src/ui/reporters/default-reporter.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const ProgressLabelMapping = {
3838
[SubProcessName.TEST_INITIALIZE_AND_VALIDATE]: 'Initializing and validating your configs',
3939
[SubProcessName.TEST_CHECKING_VM_INSTALLED]: 'Checking if VM is installed',
4040
[SubProcessName.TEST_STARTING_VM]: 'Starting VM',
41-
[SubProcessName.TEST_COPYING_OVER_CONFIGS_AND_OPENING_TERMINAL]: 'Copying over configs and opening terminal',
41+
[SubProcessName.TEST_COPYING_OVER_CONFIGS_AND_OPENING_TERMINAL]: 'Copying over configs and opening terminal (if a confirmation dialog appears within the VM, please confirm it.)',
4242
[SubProcessName.TEST_USER_CONTINUE_ON_VM]: 'Done setup! Please continue on the VM UI',
4343
[SubProcessName.TEST_DELETING_VM]: 'Deleting VM',
4444
}
@@ -189,6 +189,16 @@ export class DefaultReporter implements Reporter {
189189
return password;
190190
}
191191

192+
async promptSecret(prompt: string): Promise<string | undefined> {
193+
const password = await this.updateStateAndAwaitEvent<string>(
194+
() => this.updateRenderState(RenderStatus.SECRET_PROMPT, prompt),
195+
RenderEvent.PROMPT_RESULT,
196+
);
197+
198+
await this.displayProgress();
199+
return password;
200+
}
201+
192202
displayPlan(plan: Plan): void {
193203
this.updateRenderState(RenderStatus.DISPLAY_PLAN, plan)
194204
}

src/ui/reporters/json-reporter.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ export class JsonReporter implements Reporter {
4747
throw new Error(`Json reporter error: sudo required for command: ${data.command}. Make sure to preconfigure the sudo password for the Json reporter using --sudoPassword`);
4848
}
4949

50+
promptSecret(prompt: string): Promise<string | undefined> {
51+
throw new Error('Json reporter error: cannot prompt user for values while using Json reporter. Use a different reporter.');
52+
}
53+
5054
async promptUserForValues(): Promise<ResourceConfig[]> {
5155
throw new Error('Json reporter error: cannot prompt user for values while using Json reporter. Use a different reporter.');
5256
}

src/ui/reporters/plain-reporter.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@ export class PlainReporter implements Reporter {
106106
return result;
107107
}
108108

109+
async promptSecret(prompt: string): Promise<string | undefined> {
110+
return new Promise((resolve) => {
111+
this.rl.question(`${prompt} (leave empty if not needed) `, (answer) => resolve(answer));
112+
});
113+
}
114+
109115
async displayProgress(): Promise<void> {}
110116

111117
async promptInput(prompt: string, error?: string): Promise<string> {

src/ui/reporters/reporter.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ export interface Reporter {
6464

6565
promptSudo(pluginName: string, data: CommandRequestData, secureMode: boolean): Promise<string | undefined>;
6666

67+
promptSecret(prompt: string): Promise<string | undefined>;
68+
6769
promptUserForValues(resources: Array<ResourceInfo>, promptType: PromptType): Promise<ResourceConfig[]>;
6870

6971
promptPressKeyToContinue(message?: string): Promise<void>;

src/ui/reporters/stub-reporter.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export class StubReporter implements Reporter {
1717
async promptConfirmation(message: string): Promise<boolean> { return true; }
1818
async promptOptions(message: string, options: string[]): Promise<number> { throw new Error('Method not implemented.'); }
1919
async promptSudo(pluginName: string, data: CommandRequestData): Promise<string | undefined> { throw new Error('Method not implemented.'); }
20+
async promptSecret(prompt: string): Promise<string | undefined> { throw new Error('Method not implemented.'); }
2021
async promptUserForValues(resources: Array<ResourceInfo>, promptType: PromptType): Promise<ResourceConfig[]> { throw new Error('Method not implemented.'); }
2122
async promptPressKeyToContinue(message?: string): Promise<void> {}
2223
async displayImportResult(importResult: ImportResult): Promise<void> {}

src/ui/store/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export enum RenderStatus {
2222
PROMPT_INPUT,
2323
PROMPT_PRESS_KEY_TO_CONTINUE,
2424
SUDO_PROMPT,
25+
SECRET_PROMPT,
2526
DISPLAY_MESSAGE,
2627
}
2728

src/utils/file.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ export class FileUtils {
2222
}
2323
}
2424

25+
static async dirExists(dirPath: string, throwIfExistsButNotFile = true): Promise<boolean> {
26+
try {
27+
const result = await fs.lstat(path.resolve(dirPath))
28+
if (throwIfExistsButNotFile && !result.isDirectory()) {
29+
throw new Error(`File found at ${dirPath} instead of a file`)
30+
}
31+
return true;
32+
} catch(e) {
33+
return false;
34+
}
35+
}
36+
2537
static async isDir(fileOrDir: string): Promise<boolean> {
2638
const lstat = await fs.lstat(path.resolve(fileOrDir))
2739
return lstat.isDirectory()

src/utils/spawn.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ export async function spawnSafe(cmd: string, options?: SpawnOptions, pluginName?
3737
throw new Error('Password must be specified!');
3838
}
3939

40-
if (cmd.toLowerCase().includes('sudo')) {
41-
throw new Error(`Command must not include sudo. Plugin (${pluginName})`)
42-
}
40+
// if (cmd.toLowerCase().includes('sudo')) {
41+
// throw new Error(`Command must not include sudo. Plugin (${pluginName})`)
42+
// }
4343

4444
if (pluginName) {
4545
ctx.pluginStdout(pluginName, `Running command: ${options?.requiresRoot ? 'sudo' : ''} ${cmd}` + (options?.cwd ? `(${options?.cwd})` : ''))
@@ -65,13 +65,8 @@ export async function spawnSafe(cmd: string, options?: SpawnOptions, pluginName?
6565
const initialCols = process.stdout.columns ?? 80;
6666
const initialRows = process.stdout.rows ?? 24;
6767

68-
// Mac OS uses -SN instead of -Sn
69-
let command;
70-
if (OsUtils.isMacOS()) {
71-
command = options?.requiresRoot ? `sudo -k >/dev/null 2>&1; sudo -SN <<< "${password}" -E ${ShellUtils.getDefaultShell()} ${options?.interactive ? '-i' : ''} -c "${cmd.replaceAll('\'', '\\\'')}"` : cmd;
72-
} else {
73-
command = options?.requiresRoot ? `sudo -k >/dev/null 2>&1; sudo -S <<< "${password}" -E ${ShellUtils.getDefaultShell()} ${options?.interactive ? '-i' : ''} -c '${cmd.replaceAll('\'', '\\\'')}'` : cmd;
74-
}
68+
const command = options?.requiresRoot ? `sudo -k >/dev/null 2>&1; sudo -S <<< "${password}" -E ${ShellUtils.getDefaultShell()} ${options?.interactive ? '-i' : ''} -c "${cmd.replaceAll('"', '\\"')}"` : cmd;
69+
console.log(command);
7570

7671
const args = options?.interactive ? ['-i', '-c', command] : ['-c', command]
7772

0 commit comments

Comments
 (0)