Skip to content

Commit 1d54f2e

Browse files
committed
feat: show server output during deploy
- LoggingExecutor wraps the SSH executor and prints each remote command's stdout/stderr prefixed with "remote:" so users can see npm install output, PM2 reload, etc. in real time - rsync now uses stdio:inherit so transfer progress prints directly - Replace top-level spinner with a plain step line; output is no longer hidden behind the spinner during the deploy run
1 parent b9e10bc commit 1d54f2e

4 files changed

Lines changed: 28 additions & 14 deletions

File tree

src/cli/commands/deploy.ts

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import chalk from 'chalk';
22
import { loadConfig } from '../../config/loader.js';
33
import { DeployService } from '../../services/deploy.service.js';
4+
import { LoggingExecutor } from '../../infrastructure/ssh/logging-executor.js';
45
import { runRemoteCommand } from '../runner.js';
56
import { ui } from '../ui.js';
67
import type { ShipnodeConfig } from '../../shared/types.js';
@@ -17,19 +18,10 @@ export async function cmdDeploy(cwd: string, options: { dryRun?: boolean; skipBu
1718
cwd,
1819
async ({ config, executor }) => {
1920
ui.banner();
21+
ui.step(`Deploying ${chalk.bold(config.pm2?.name ?? config.app)}${config.ssh.user}@${config.ssh.host}`);
2022

21-
const spin = ui.spinner();
22-
spin.start(`Deploying ${chalk.bold(config.pm2?.name ?? config.app)}${config.ssh.user}@${config.ssh.host}`);
23-
24-
const deployer = new DeployService(executor, config, cwd);
25-
try {
26-
await deployer.execute(options.skipBuild ?? false);
27-
} catch (err) {
28-
spin.stop('Deploy failed');
29-
throw err;
30-
}
31-
32-
spin.stop(`Deployed ${chalk.bold(config.pm2?.name ?? config.app)}`);
23+
const deployer = new DeployService(new LoggingExecutor(executor), config, cwd);
24+
await deployer.execute(options.skipBuild ?? false);
3325

3426
const lines = [
3527
`host ${config.ssh.user}@${config.ssh.host}`,

src/domain/deploy/backend-strategy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export class BackendStrategy implements DeploymentStrategy {
2929
`${this.config.ssh.user}@${this.config.ssh.host}:${ctx.workDir}/`,
3030
];
3131

32-
await execa('rsync', args);
32+
await execa('rsync', args, { stdio: 'inherit' });
3333
}
3434

3535
async setupEnvironment(ctx: StrategyContext): Promise<void> {

src/domain/deploy/frontend-strategy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export class FrontendStrategy implements DeploymentStrategy {
3434
...(hasIgnoreFile ? ['--exclude-from', ignoreFile] : []),
3535
`${this.cwd}/${buildDir}/`,
3636
`${this.config.ssh.user}@${this.config.ssh.host}:${ctx.workDir}/`,
37-
]);
37+
], { stdio: 'inherit' });
3838
}
3939

4040
private async buildFrontend(): Promise<void> {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import chalk from 'chalk';
2+
import type { RemoteExecutor } from '../../domain/remote/executor.js';
3+
import type { ExecResult } from '../../shared/types.js';
4+
5+
export class LoggingExecutor implements RemoteExecutor {
6+
constructor(private inner: RemoteExecutor) {}
7+
8+
async exec(command: string, options?: { timeout?: number }): Promise<ExecResult> {
9+
const result = await this.inner.exec(command, options);
10+
if (result.stdout) {
11+
for (const line of result.stdout.split('\n')) {
12+
if (line.trim()) process.stdout.write(chalk.dim(' remote: ' + line) + '\n');
13+
}
14+
}
15+
if (result.stderr) {
16+
for (const line of result.stderr.split('\n')) {
17+
if (line.trim()) process.stderr.write(chalk.yellow(' remote: ' + line) + '\n');
18+
}
19+
}
20+
return result;
21+
}
22+
}

0 commit comments

Comments
 (0)