Skip to content

Commit 7ccf7c3

Browse files
committed
fix(cli): iterate all apps in config show and add --app filter
Previously `shipnode config show` only rendered `apps[0]` via `getActiveApp(config)`, hiding every additional app in a workspace. Split the output into workspace-level sections (SSH, database, redis, cloudflare) shown once and per-app sections rendered for each app in `config.apps`. Add a `--app <name>` flag to narrow the view to a single app.
1 parent ea57f89 commit 7ccf7c3

2 files changed

Lines changed: 75 additions & 45 deletions

File tree

src/cli/commands/config.ts

Lines changed: 74 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,71 +4,100 @@ import { runLocalCommand } from '../runner.js';
44
import { ui } from '../ui.js';
55
import { loadConfig } from '../../config/loader.js';
66
import { getActiveApp } from '../../domain/workspace.js';
7+
import type { ShipnodeApp } from '../../shared/types.js';
78

8-
export async function cmdConfigShow(cwd: string, options: { config?: string }): Promise<void> {
9+
function showApp(app: ShipnodeApp, nodeVersion: string): void {
10+
ui.section(`App: ${app.name}`, [
11+
['type', app.appType],
12+
['nodeVersion', nodeVersion],
13+
['appRoot', app.appRoot ?? '(repo root)'],
14+
['envFile', app.envFile],
15+
['keepReleases', String(app.keepReleases)],
16+
]);
17+
18+
if (app.pm2) {
19+
for (const pm2App of app.pm2.apps) {
20+
const rows: [string, string][] = [['name', pm2App.name]];
21+
if (pm2App.command) rows.push(['command', pm2App.command]);
22+
if (pm2App.port !== undefined) rows.push(['port', String(pm2App.port)]);
23+
if (pm2App.instances !== undefined) rows.push(['instances', String(pm2App.instances)]);
24+
if (pm2App.maxMemory !== undefined) rows.push(['maxMemory', pm2App.maxMemory]);
25+
if (pm2App.env) {
26+
for (const [k, v] of Object.entries(pm2App.env)) rows.push([`env.${k}`, v]);
27+
}
28+
ui.section(pm2App.port !== undefined ? `PM2 process: ${pm2App.name} (web)` : `PM2 process: ${pm2App.name}`, rows);
29+
}
30+
}
31+
32+
if (app.domain) {
33+
ui.section('Domain', [['domain', app.domain]]);
34+
}
35+
36+
if (app.appType === 'backend') {
37+
ui.section('Health Check', [
38+
['enabled', String(app.healthCheck.enabled)],
39+
['path', app.healthCheck.path],
40+
['timeout', String(app.healthCheck.timeout)],
41+
['retries', String(app.healthCheck.retries)],
42+
['startupDelay', String(app.healthCheck.startupDelay)],
43+
]);
44+
}
45+
46+
if (app.sharedDirs && app.sharedDirs.length > 0) {
47+
ui.section('Shared Dirs', app.sharedDirs.map((d, i) => [`[${i}]`, d]));
48+
}
49+
50+
if (app.sharedFiles && app.sharedFiles.length > 0) {
51+
ui.section('Shared Files', app.sharedFiles.map((f, i) => [`[${i}]`, f]));
52+
}
53+
}
54+
55+
export async function cmdConfigShow(
56+
cwd: string,
57+
options: { config?: string; app?: string },
58+
): Promise<void> {
959
await runLocalCommand(
1060
cwd,
1161
async (config) => {
12-
const app = getActiveApp(config);
1362
ui.heading('Shipnode Configuration');
1463

15-
ui.section('App', [
16-
['app', app.appType],
17-
['nodeVersion', config.nodeVersion],
18-
['envFile', app.envFile],
19-
['keepReleases', String(app.keepReleases)],
20-
]);
21-
22-
ui.section('SSH', [
64+
ui.section('Workspace', [
2365
['ssh', `${config.ssh.user}@${config.ssh.host}:${config.ssh.port}`],
2466
['remotePath', config.remotePath],
67+
['nodeVersion', config.nodeVersion],
68+
['apps', config.apps.map((a) => a.name).join(', ')],
2569
]);
2670

27-
if (app.pm2) {
28-
for (const pm2App of app.pm2.apps) {
29-
const rows: [string, string][] = [['name', pm2App.name]];
30-
if (pm2App.command) rows.push(['command', pm2App.command]);
31-
if (pm2App.port !== undefined) rows.push(['port', String(pm2App.port)]);
32-
if (pm2App.instances !== undefined) rows.push(['instances', String(pm2App.instances)]);
33-
if (pm2App.maxMemory !== undefined) rows.push(['maxMemory', pm2App.maxMemory]);
34-
if (pm2App.env) {
35-
for (const [k, v] of Object.entries(pm2App.env)) rows.push([`env.${k}`, v]);
36-
}
37-
ui.section(pm2App.port !== undefined ? `PM2 app: ${pm2App.name} (web)` : `PM2 app: ${pm2App.name}`, rows);
71+
if (config.database) {
72+
const db = config.database;
73+
const rows: [string, string][] = [['type', db.type], ['name', db.name]];
74+
if (db.type !== 'sqlite') {
75+
rows.push(['host', db.host], ['port', String(db.port)], ['user', db.user]);
3876
}
77+
ui.section('Database', rows);
3978
}
4079

41-
if (app.domain) {
42-
ui.section('Domain', [
43-
['domain', app.domain],
80+
if (config.redis) {
81+
ui.section('Redis', [
82+
['host', config.redis.host],
83+
['port', String(config.redis.port)],
4484
]);
4585
}
4686

47-
if (app.appType === 'backend') {
48-
ui.section('Health Check', [
49-
['enabled', String(app.healthCheck.enabled)],
50-
['path', app.healthCheck.path],
51-
['timeout', String(app.healthCheck.timeout)],
52-
['retries', String(app.healthCheck.retries)],
53-
['startupDelay', String(app.healthCheck.startupDelay)],
87+
if (config.cloudflare) {
88+
ui.section('Cloudflare', [
89+
['zone', config.cloudflare.zone],
90+
['tunnelName', config.cloudflare.tunnelName ?? '(default)'],
91+
['lockdownFirewall', String(config.cloudflare.lockdownFirewall ?? false)],
5492
]);
5593
}
5694

57-
if (app.sharedDirs && app.sharedDirs.length > 0) {
58-
ui.section('Shared Dirs', app.sharedDirs.map((d, i) => [`[${i}]`, d]));
59-
}
95+
const apps = options.app
96+
? [getActiveApp(config, options.app)]
97+
: config.apps;
6098

61-
if (app.sharedFiles && app.sharedFiles.length > 0) {
62-
ui.section('Shared Files', app.sharedFiles.map((f, i) => [`[${i}]`, f]));
63-
}
64-
65-
if (config.database) {
66-
const db = config.database;
67-
const rows: [string, string][] = [['type', db.type], ['name', db.name]];
68-
if (db.type !== 'sqlite') {
69-
rows.push(['host', db.host], ['port', String(db.port)], ['user', db.user]);
70-
}
71-
ui.section('Database', rows);
99+
for (const app of apps) {
100+
showApp(app, config.nodeVersion);
72101
}
73102
},
74103
{ configPath: options.config },

src/cli/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ const config = program.command('config').description('Manage configuration');
180180

181181
config.command('show')
182182
.description('Show resolved configuration')
183+
.option('--app <name>', 'Show only a specific app (default: all)')
183184
.option('--config <path>', 'Use a specific config file')
184185
.action((opts) => cmdConfigShow(process.cwd(), opts));
185186

0 commit comments

Comments
 (0)