remote-deploy-cli 1.2.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -66,20 +66,24 @@ These commands are executed on your **local machine** or **CI/CD runner**.
66
66
  Triggers a deployment on the remote server.
67
67
 
68
68
  **Syntax:**
69
+
69
70
  ```bash
70
71
  redep deploy <type>
71
72
  ```
72
73
 
73
74
  **Parameters:**
75
+
74
76
  - `<type>`: The service type to deploy.
75
77
  - `fe`: Frontend (pre-configured to run `docker compose pull && docker compose up -d`).
76
78
  - `custom`: Custom command (configured on server via `deployment_command`).
77
79
 
78
80
  **Requirements:**
81
+
79
82
  - `SERVER_URL` must be configured.
80
83
  - `SECRET_KEY` must match the server's key.
81
84
 
82
85
  **Example:**
86
+
83
87
  ```bash
84
88
  # Deploy frontend service
85
89
  redep deploy fe
@@ -89,6 +93,7 @@ redep deploy custom
89
93
  ```
90
94
 
91
95
  **Expected Output:**
96
+
92
97
  ```
93
98
  [INFO] Deploying fe to http://192.168.1.50:3000...
94
99
  [SUCCESS] Deployment triggered successfully.
@@ -117,14 +122,17 @@ These commands are executed on the **remote server** (VPS, VM, etc.).
117
122
  Starts the server in the foreground. Useful for debugging or running inside Docker.
118
123
 
119
124
  **Syntax:**
125
+
120
126
  ```bash
121
127
  redep listen [--port <number>]
122
128
  ```
123
129
 
124
130
  **Options:**
131
+
125
132
  - `-p, --port`: Specify port (default: 3000).
126
133
 
127
134
  **Example:**
135
+
128
136
  ```bash
129
137
  redep listen --port 4000
130
138
  ```
@@ -132,19 +140,23 @@ redep listen --port 4000
132
140
  ### `start` (Background)
133
141
 
134
142
  Starts the server in background mode (Daemon).
135
- * **Auto-PM2**: If `pm2` is installed, it uses PM2 for process management.
136
- * **Native Fallback**: If `pm2` is missing, it uses Node.js `child_process` to detach.
143
+
144
+ - **Auto-PM2**: If `pm2` is installed, it uses PM2 for process management.
145
+ - **Native Fallback**: If `pm2` is missing, it uses Node.js `child_process` to detach.
137
146
 
138
147
  **Syntax:**
148
+
139
149
  ```bash
140
150
  redep start [--port <number>]
141
151
  ```
142
152
 
143
153
  **Related Commands:**
154
+
144
155
  - `redep stop`: Stops the background server.
145
156
  - `redep status`: Checks if the server is running.
146
157
 
147
158
  **Example:**
159
+
148
160
  ```bash
149
161
  redep start
150
162
  # [SUCCESS] Server started in background using PM2
@@ -185,6 +197,20 @@ services:
185
197
 
186
198
  ---
187
199
 
200
+ ### `generate`
201
+
202
+ Helper to generate configuration values.
203
+
204
+ - `generate secret_key`: Generates a secure 32-character secret key and saves it to config.
205
+ - `generate working_dir`: Sets the current directory as the `working_dir`.
206
+
207
+ ### `init`
208
+
209
+ Interactive initialization for configuration.
210
+
211
+ - `init client`: Prompts for `server_url` and `secret_key`.
212
+ - `init server`: Prompts for `server_port`, `working_dir`, `deployment_command`, and `secret_key` (auto-generates if empty).
213
+
188
214
  ## Command Interactions
189
215
 
190
216
  ### Workflow: Deployment
@@ -193,11 +219,11 @@ services:
193
219
  sequenceDiagram
194
220
  participant Client (CI/Local)
195
221
  participant Server (Remote)
196
-
222
+
197
223
  Note over Client: User runs "redep deploy fe"
198
224
  Client->>Client: Read Config (URL, Secret)
199
225
  Client->>Server: POST /deploy { type: "fe" } (Auth: Bearer Token)
200
-
226
+
201
227
  Note over Server: Verify Secret Key
202
228
  alt Invalid Key
203
229
  Server-->>Client: 403 Forbidden
@@ -213,27 +239,30 @@ sequenceDiagram
213
239
 
214
240
  ## Configuration Reference
215
241
 
216
- | Config Key | Env Variable | Description | Context |
217
- | :------------ | :------------ | :----------------------------------------------- | :--------------- |
218
- | `server_port` | `SERVER_PORT` | Port for the server to listen on (Default: 3000) | **Server** |
219
- | `working_dir` | `WORKING_DIR` | Directory to execute commands in | **Server** |
220
- | `deployment_command` | `DEPLOYMENT_COMMAND` | Custom command for `deploy custom` | **Server** |
221
- | `server_url` | `SERVER_URL` | URL of the remote `redep` server | **Client** |
222
- | `secret_key` | `SECRET_KEY` | Shared secret for authentication | **Both** |
242
+ | Config Key | Env Variable | Description | Context |
243
+ | :------------------- | :------------------- | :----------------------------------------------- | :--------- |
244
+ | `server_port` | `SERVER_PORT` | Port for the server to listen on (Default: 3000) | **Server** |
245
+ | `working_dir` | `WORKING_DIR` | Directory to execute commands in | **Server** |
246
+ | `deployment_command` | `DEPLOYMENT_COMMAND` | Custom command for `deploy custom` | **Server** |
247
+ | `server_url` | `SERVER_URL` | URL of the remote `redep` server | **Client** |
248
+ | `secret_key` | `SECRET_KEY` | Shared secret for authentication | **Both** |
223
249
 
224
250
  ---
225
251
 
226
252
  ## Troubleshooting
227
253
 
228
254
  ### `Error: "working_dir" is not set`
255
+
229
256
  - **Context**: Server
230
257
  - **Fix**: Run `redep config set working_dir /path` or check `docker-compose.yml` environment.
231
258
 
232
259
  ### `Connection Refused`
260
+
233
261
  - **Context**: Client
234
262
  - **Fix**: Ensure server is running (`redep status` or `docker ps`) and port 3000 is open.
235
263
 
236
264
  ### `403 Forbidden`
265
+
237
266
  - **Context**: Client
238
267
  - **Fix**: Re-check `SECRET_KEY` on both machines. They must match exactly.
239
268
 
package/bin/index.js CHANGED
@@ -3,21 +3,138 @@
3
3
  import 'dotenv/config';
4
4
  import { Command } from 'commander';
5
5
  import { spawn } from 'child_process';
6
+ import crypto from 'crypto';
7
+ import inquirer from 'inquirer';
6
8
  import { logger } from '../lib/logger.js';
7
9
  import { getConfig, setConfig, getAllConfig, clearConfig } from '../lib/config.js';
8
10
  import { startServer } from '../lib/server/index.js';
9
11
  import { deploy } from '../lib/client/index.js';
12
+ import pkg from '../package.json' assert { type: 'json' };
10
13
 
11
14
  const program = new Command();
12
15
 
16
+ program.name('redep').description(pkg.description).version(pkg.version);
17
+
18
+ // Helper to generate secure token
19
+ const generateSecureToken = (length = 32) => {
20
+ return crypto
21
+ .randomBytes(Math.ceil(length * 0.75))
22
+ .toString('base64')
23
+ .slice(0, length)
24
+ .replace(/\+/g, '-')
25
+ .replace(/\//g, '_');
26
+ };
27
+
28
+ // Init Command
13
29
  program
14
- .name('redep')
15
- .description('Remote execution CLI for deployment')
16
- .version('1.0.0');
30
+ .command('init <type>')
31
+ .description('Initialize configuration for client or server')
32
+ .action(async (type) => {
33
+ if (type !== 'client' && type !== 'server') {
34
+ logger.error('Type must be either "client" or "server"');
35
+ process.exit(1);
36
+ }
37
+
38
+ try {
39
+ if (type === 'client') {
40
+ const answers = await inquirer.prompt([
41
+ {
42
+ type: 'input',
43
+ name: 'server_url',
44
+ message: 'Enter Server URL:',
45
+ validate: (input) => (input ? true : 'Server URL is required'),
46
+ },
47
+ {
48
+ type: 'input',
49
+ name: 'secret_key',
50
+ message: 'Enter Secret Key:',
51
+ validate: (input) => (input ? true : 'Secret Key is required'),
52
+ },
53
+ ]);
54
+
55
+ setConfig('server_url', answers.server_url);
56
+ setConfig('secret_key', answers.secret_key);
57
+ logger.success('Client configuration saved successfully.');
58
+ } else {
59
+ const answers = await inquirer.prompt([
60
+ {
61
+ type: 'input',
62
+ name: 'server_port',
63
+ message: 'Enter Server Port:',
64
+ default: '3000',
65
+ validate: (input) => (!isNaN(input) ? true : 'Port must be a number'),
66
+ },
67
+ {
68
+ type: 'input',
69
+ name: 'working_dir',
70
+ message: 'Enter Working Directory:',
71
+ validate: (input) => (input ? true : 'Working Directory is required'),
72
+ },
73
+ {
74
+ type: 'input',
75
+ name: 'deployment_command',
76
+ message: 'Enter Custom Deployment Command (Optional):',
77
+ },
78
+ {
79
+ type: 'input',
80
+ name: 'secret_key',
81
+ message: 'Enter Secret Key (Leave empty to generate):',
82
+ },
83
+ ]);
84
+
85
+ let secret = answers.secret_key;
86
+ if (!secret) {
87
+ secret = generateSecureToken();
88
+ logger.info(`Generated Secret Key: ${secret}`);
89
+ }
90
+
91
+ setConfig('server_port', answers.server_port);
92
+ setConfig('working_dir', answers.working_dir);
93
+ if (answers.deployment_command) {
94
+ setConfig('deployment_command', answers.deployment_command);
95
+ }
96
+ setConfig('secret_key', secret);
97
+ logger.success('Server configuration saved successfully.');
98
+ }
99
+ } catch (error) {
100
+ logger.error(`Initialization failed: ${error.message}`);
101
+ process.exit(1);
102
+ }
103
+ });
104
+
105
+ // Generate Command
106
+ const generateCommand = new Command('generate').description('Generate configuration values');
107
+
108
+ generateCommand
109
+ .command('secret_key')
110
+ .description('Generate a new secret key')
111
+ .action(() => {
112
+ try {
113
+ const secret = generateSecureToken();
114
+ setConfig('secret_key', secret);
115
+ logger.success(`Secret Key generated and saved: ${secret}`);
116
+ } catch (error) {
117
+ logger.error(`Generation failed: ${error.message}`);
118
+ }
119
+ });
120
+
121
+ generateCommand
122
+ .command('working_dir')
123
+ .description('Set working directory to current path')
124
+ .action(() => {
125
+ try {
126
+ const cwd = process.cwd();
127
+ setConfig('working_dir', cwd);
128
+ logger.success(`Working Directory set to: ${cwd}`);
129
+ } catch (error) {
130
+ logger.error(`Failed to set working directory: ${error.message}`);
131
+ }
132
+ });
133
+
134
+ program.addCommand(generateCommand);
17
135
 
18
136
  // Configuration Command
19
- const configCommand = new Command('config')
20
- .description('Manage configuration');
137
+ const configCommand = new Command('config').description('Manage configuration');
21
138
 
22
139
  configCommand
23
140
  .command('set <key> <value>')
@@ -65,20 +182,19 @@ program
65
182
  // Check if PM2 is available via API
66
183
  // We'll use a dynamic import or checking for the pm2 binary in a real scenario
67
184
  // But here we can just try to spawn 'pm2' command
68
-
185
+
69
186
  // Use dedicated server entry point for PM2 to avoid CLI/ESM issues
70
187
  // Resolve absolute path to server-entry.js
71
- const scriptPath = new URL('../server-entry.js', import.meta.url).pathname.replace(/^\/([A-Za-z]:)/, '$1');
72
-
73
- const args = [
74
- 'start',
75
- scriptPath,
76
- '--name', 'redep-server'
77
- ];
78
-
188
+ const scriptPath = new URL('../server-entry.js', import.meta.url).pathname.replace(
189
+ /^\/([A-Za-z]:)/,
190
+ '$1'
191
+ );
192
+
193
+ const args = ['start', scriptPath, '--name', 'redep-server'];
194
+
79
195
  // We don't pass 'listen' arg because server-entry.js starts immediately
80
196
  // But we do need to ensure env vars are passed if port is customized
81
-
197
+
82
198
  const env = { ...process.env };
83
199
  if (options.port) {
84
200
  env.SERVER_PORT = options.port;
@@ -87,7 +203,7 @@ program
87
203
  const pm2 = spawn('pm2', args, {
88
204
  stdio: 'inherit',
89
205
  shell: true,
90
- env: env // Pass modified env with port
206
+ env: env, // Pass modified env with port
91
207
  });
92
208
 
93
209
  pm2.on('error', () => {
@@ -98,46 +214,45 @@ program
98
214
 
99
215
  pm2.on('close', (code) => {
100
216
  if (code !== 0) {
101
- logger.warn('PM2 start failed, falling back to native background process...');
102
- startNativeBackground(options);
217
+ logger.warn('PM2 start failed, falling back to native background process...');
218
+ startNativeBackground(options);
103
219
  } else {
104
- logger.success('Server started in background using PM2');
220
+ logger.success('Server started in background using PM2');
105
221
  }
106
222
  });
107
-
108
223
  } catch (e) {
109
224
  startNativeBackground(options);
110
225
  }
111
226
  });
112
227
 
113
228
  function startNativeBackground(options) {
114
- const existingPid = getConfig('server_pid');
115
-
116
- if (existingPid) {
117
- try {
118
- process.kill(existingPid, 0);
119
- logger.warn(`Server is already running with PID ${existingPid}`);
120
- return;
121
- } catch (e) {
122
- // Process doesn't exist, clear stale PID
123
- setConfig('server_pid', null);
124
- }
125
- }
229
+ const existingPid = getConfig('server_pid');
126
230
 
127
- const args = ['listen'];
128
- if (options.port) {
129
- args.push('--port', options.port);
231
+ if (existingPid) {
232
+ try {
233
+ process.kill(existingPid, 0);
234
+ logger.warn(`Server is already running with PID ${existingPid}`);
235
+ return;
236
+ } catch (e) {
237
+ // Process doesn't exist, clear stale PID
238
+ setConfig('server_pid', null);
130
239
  }
240
+ }
131
241
 
132
- const child = spawn(process.argv[0], [process.argv[1], ...args], {
133
- detached: true,
134
- stdio: 'ignore',
135
- windowsHide: true
136
- });
242
+ const args = ['listen'];
243
+ if (options.port) {
244
+ args.push('--port', options.port);
245
+ }
246
+
247
+ const child = spawn(process.argv[0], [process.argv[1], ...args], {
248
+ detached: true,
249
+ stdio: 'ignore',
250
+ windowsHide: true,
251
+ });
137
252
 
138
- child.unref();
139
- setConfig('server_pid', child.pid);
140
- logger.success(`Server started in background (native) with PID ${child.pid}`);
253
+ child.unref();
254
+ setConfig('server_pid', child.pid);
255
+ logger.success(`Server started in background (native) with PID ${child.pid}`);
141
256
  }
142
257
 
143
258
  program
@@ -146,13 +261,13 @@ program
146
261
  .action(() => {
147
262
  // Try PM2 stop first
148
263
  const pm2 = spawn('pm2', ['stop', 'redep-server'], { stdio: 'ignore', shell: true });
149
-
264
+
150
265
  pm2.on('close', (code) => {
151
266
  if (code === 0) {
152
267
  logger.success('Server stopped (PM2)');
153
268
  return;
154
269
  }
155
-
270
+
156
271
  // Fallback to native stop
157
272
  const pid = getConfig('server_pid');
158
273
  if (!pid) {
@@ -179,28 +294,28 @@ program
179
294
  .command('status')
180
295
  .description('Check server status')
181
296
  .action(() => {
182
- // Try PM2 status first
183
- const pm2 = spawn('pm2', ['describe', 'redep-server'], { stdio: 'inherit', shell: true });
297
+ // Try PM2 status first
298
+ const pm2 = spawn('pm2', ['describe', 'redep-server'], { stdio: 'inherit', shell: true });
184
299
 
185
- pm2.on('close', (code) => {
186
- if (code !== 0) {
187
- // Fallback to native status
188
- const pid = getConfig('server_pid');
189
-
190
- if (!pid) {
191
- logger.info('Server is NOT running.');
192
- return;
193
- }
194
-
195
- try {
196
- process.kill(pid, 0);
197
- logger.success(`Server is RUNNING (PID ${pid})`);
198
- } catch (e) {
199
- logger.warn(`Server is NOT running (Stale PID ${pid} found).`);
200
- setConfig('server_pid', null);
201
- }
300
+ pm2.on('close', (code) => {
301
+ if (code !== 0) {
302
+ // Fallback to native status
303
+ const pid = getConfig('server_pid');
304
+
305
+ if (!pid) {
306
+ logger.info('Server is NOT running.');
307
+ return;
308
+ }
309
+
310
+ try {
311
+ process.kill(pid, 0);
312
+ logger.success(`Server is RUNNING (PID ${pid})`);
313
+ } catch (e) {
314
+ logger.warn(`Server is NOT running (Stale PID ${pid} found).`);
315
+ setConfig('server_pid', null);
202
316
  }
203
- });
317
+ }
318
+ });
204
319
  });
205
320
 
206
321
  // Server Command
@@ -211,15 +326,19 @@ program
211
326
  .action((options) => {
212
327
  const port = options.port || getConfig('server_port') || process.env.SERVER_PORT || 3000;
213
328
  const secret = getConfig('secret_key') || process.env.SECRET_KEY;
214
-
329
+
215
330
  if (!secret) {
216
- logger.warn('Warning: No "secret_key" set in config or SECRET_KEY env var. Communication might be insecure or fail if client requires it.');
331
+ logger.warn(
332
+ 'Warning: No "secret_key" set in config or SECRET_KEY env var. Communication might be insecure or fail if client requires it.'
333
+ );
217
334
  logger.info('Run "redep config set secret_key <your-secret>" or set SECRET_KEY env var.');
218
335
  }
219
336
 
220
337
  const workingDir = getConfig('working_dir') || process.env.WORKING_DIR;
221
338
  if (!workingDir) {
222
- logger.error('Error: "working_dir" is not set. Please set it using "redep config set working_dir <path>" or WORKING_DIR env var.');
339
+ logger.error(
340
+ 'Error: "working_dir" is not set. Please set it using "redep config set working_dir <path>" or WORKING_DIR env var.'
341
+ );
223
342
  process.exit(1);
224
343
  }
225
344
 
@@ -235,12 +354,16 @@ program
235
354
  const secret = getConfig('secret_key') || process.env.SECRET_KEY;
236
355
 
237
356
  if (!serverUrl) {
238
- logger.error('Error: "server_url" is not set. Set SERVER_URL env var or run "redep config set server_url <url>"');
357
+ logger.error(
358
+ 'Error: "server_url" is not set. Set SERVER_URL env var or run "redep config set server_url <url>"'
359
+ );
239
360
  process.exit(1);
240
361
  }
241
362
 
242
363
  if (!secret) {
243
- logger.error('Error: "secret_key" is not set. Set SECRET_KEY env var or run "redep config set secret_key <your-secret>"');
364
+ logger.error(
365
+ 'Error: "secret_key" is not set. Set SECRET_KEY env var or run "redep config set secret_key <your-secret>"'
366
+ );
244
367
  process.exit(1);
245
368
  }
246
369
 
@@ -3,12 +3,12 @@ services:
3
3
  build: .
4
4
  container_name: deploy-server
5
5
  restart: unless-stopped
6
-
6
+
7
7
  env_file:
8
8
  - .env
9
9
 
10
10
  ports:
11
- - "${SERVER_PORT}:3000"
11
+ - '${SERVER_PORT}:3000'
12
12
  environment:
13
13
  # Override WORKING_DIR to match the internal container path
14
14
  - WORKING_DIR=/workspace
@@ -9,14 +9,16 @@ export const sendCommand = async (url, endpoint, secret, data) => {
9
9
 
10
10
  const response = await axios.post(fullUrl, data, {
11
11
  headers: {
12
- 'Authorization': `Bearer ${secret}`,
13
- 'Content-Type': 'application/json'
14
- }
12
+ Authorization: `Bearer ${secret}`,
13
+ 'Content-Type': 'application/json',
14
+ },
15
15
  });
16
16
  return response.data;
17
17
  } catch (error) {
18
18
  if (error.response) {
19
- throw new Error(`Server error (${error.response.status}): ${JSON.stringify(error.response.data)}`);
19
+ throw new Error(
20
+ `Server error (${error.response.status}): ${JSON.stringify(error.response.data)}`
21
+ );
20
22
  } else if (error.request) {
21
23
  throw new Error(`Connection failed: No response from ${url}. Is the server running?`);
22
24
  } else {
@@ -3,13 +3,13 @@ import { logger } from '../logger.js';
3
3
 
4
4
  export const deploy = async (type, serverUrl, secret) => {
5
5
  logger.info(`Starting deployment sequence for target: ${type}`);
6
-
6
+
7
7
  if (type === 'fe') {
8
8
  logger.info('Sending deployment instruction to server machine...');
9
-
9
+
10
10
  try {
11
11
  const result = await sendCommand(serverUrl, '/deploy/fe', secret, {});
12
-
12
+
13
13
  if (result.status === 'success') {
14
14
  logger.success('Server successfully executed the deployment command.');
15
15
  logger.log('--- Remote Output ---');
@@ -17,34 +17,33 @@ export const deploy = async (type, serverUrl, secret) => {
17
17
  if (result.output.stderr) console.log(result.output.stderr);
18
18
  logger.log('---------------------');
19
19
  } else if (type === 'custom') {
20
- logger.info('Sending custom deployment instruction to server machine...');
21
-
22
- try {
23
- const result = await sendCommand(serverUrl, '/deploy/custom', secret, {});
24
-
25
- if (result.status === 'success') {
26
- logger.success('Server successfully executed the custom deployment command.');
27
- logger.log('--- Remote Output ---');
28
- if (result.output.stdout) console.log(result.output.stdout);
29
- if (result.output.stderr) console.log(result.output.stderr);
30
- logger.log('---------------------');
31
- } else {
32
- logger.error('Server reported failure.');
33
- console.error(result);
34
- }
35
- } catch (err) {
36
- throw err;
37
- }
38
- } else {
20
+ logger.info('Sending custom deployment instruction to server machine...');
21
+
22
+ try {
23
+ const result = await sendCommand(serverUrl, '/deploy/custom', secret, {});
24
+
25
+ if (result.status === 'success') {
26
+ logger.success('Server successfully executed the custom deployment command.');
27
+ logger.log('--- Remote Output ---');
28
+ if (result.output.stdout) console.log(result.output.stdout);
29
+ if (result.output.stderr) console.log(result.output.stderr);
30
+ logger.log('---------------------');
31
+ } else {
39
32
  logger.error('Server reported failure.');
40
33
  console.error(result);
41
34
  }
42
35
  } catch (err) {
43
36
  throw err;
44
37
  }
45
-
46
38
  } else {
47
- logger.error(`Deployment type "${type}" is not supported yet.`);
48
- throw new Error(`Unsupported deployment type: ${type}`);
39
+ logger.error('Server reported failure.');
40
+ console.error(result);
49
41
  }
50
- };
42
+ } catch (err) {
43
+ throw err;
44
+ }
45
+ } else {
46
+ logger.error(`Deployment type "${type}" is not supported yet.`);
47
+ throw new Error(`Unsupported deployment type: ${type}`);
48
+ }
49
+ };
package/lib/config.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import Conf from 'conf';
2
2
 
3
3
  const config = new Conf({
4
- projectName: 'redep'
4
+ projectName: 'redep',
5
5
  });
6
6
 
7
7
  export const getConfig = (key) => {
package/lib/logger.js CHANGED
@@ -5,5 +5,5 @@ export const logger = {
5
5
  success: (msg) => console.log(chalk.green('✔') + ' ' + msg),
6
6
  warn: (msg) => console.log(chalk.yellow('⚠') + ' ' + msg),
7
7
  error: (msg) => console.error(chalk.red('✖') + ' ' + msg),
8
- log: (msg) => console.log(msg)
8
+ log: (msg) => console.log(msg),
9
9
  };
@@ -4,13 +4,13 @@ import { logger } from '../logger.js';
4
4
  export const executeCommand = (command, workingDir) => {
5
5
  return new Promise((resolve, reject) => {
6
6
  logger.info(`Executing: ${command} in ${workingDir}`);
7
-
7
+
8
8
  exec(command, { cwd: workingDir }, (error, stdout, stderr) => {
9
9
  if (error) {
10
10
  logger.error(`Execution error: ${error.message}`);
11
11
  return reject({ error: error.message, stderr });
12
12
  }
13
-
13
+
14
14
  if (stderr) {
15
15
  // Docker often outputs to stderr even for info, so we log it but don't fail unless error is set
16
16
  logger.warn(`Stderr: ${stderr}`);
@@ -6,9 +6,9 @@ export const startServer = (port, secret, workingDir) => {
6
6
  logger.error('Cannot start server: Secret key is required for security.');
7
7
  process.exit(1);
8
8
  }
9
-
9
+
10
10
  const app = createServer(secret, workingDir);
11
-
11
+
12
12
  app.listen(port, () => {
13
13
  logger.success(`Server is running on port ${port}`);
14
14
  logger.info(`Working Directory: ${workingDir}`);
@@ -47,7 +47,7 @@ export const createServer = (secretKey, workingDir) => {
47
47
  // Specific endpoint for deploy fe to map the requirement exactly
48
48
  app.post('/deploy/fe', authenticate, async (req, res) => {
49
49
  logger.info('Received deploy fe request');
50
-
50
+
51
51
  // Requirement: cd to working dir and run docker compose up -d
52
52
  // We use 'docker compose pull && docker compose up -d' to ensure latest image is used
53
53
  const command = 'docker compose pull && docker compose up -d';
@@ -69,9 +69,10 @@ export const createServer = (secretKey, workingDir) => {
69
69
 
70
70
  if (!deploymentCommand) {
71
71
  logger.error('No DEPLOYMENT_COMMAND set');
72
- return res.status(400).json({
73
- status: 'error',
74
- error: 'No DEPLOYMENT_COMMAND configured. Set it via env var or "redep config set deployment_command <cmd>"'
72
+ return res.status(400).json({
73
+ status: 'error',
74
+ error:
75
+ 'No DEPLOYMENT_COMMAND configured. Set it via env var or "redep config set deployment_command <cmd>"',
75
76
  });
76
77
  }
77
78
 
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "remote-deploy-cli",
3
- "version": "1.2.0",
3
+ "author": "nafies1",
4
+ "version": "1.3.1",
4
5
  "main": "index.js",
5
6
  "type": "module",
6
7
  "bin": {
@@ -14,7 +15,6 @@
14
15
  "sonar": "node sonar-project.js"
15
16
  },
16
17
  "keywords": [],
17
- "author": "",
18
18
  "license": "ISC",
19
19
  "description": "Remote execution CLI for deployment",
20
20
  "dependencies": {
@@ -27,11 +27,17 @@
27
27
  "dotenv": "^17.2.3",
28
28
  "express": "^4.18.2",
29
29
  "helmet": "^7.1.0",
30
+ "inquirer": "^13.2.0",
30
31
  "morgan": "^1.10.0",
31
32
  "pm2": "^6.0.14"
32
33
  },
33
34
  "devDependencies": {
35
+ "@sonar/scan": "^4.3.4",
34
36
  "prettier": "^3.8.0",
35
37
  "sonarqube-scanner": "^4.3.4"
38
+ },
39
+ "redep": {
40
+ "secret_key": "4k94jLuEIT_jWPHJYPActmGxn2x72eR0",
41
+ "working_dir": "C:\\Users\\nafie\\Documents\\trae_projects\\remote-deploy-cli"
36
42
  }
37
43
  }
package/scripts/update.js CHANGED
@@ -83,7 +83,6 @@ try {
83
83
  execSync('git push origin main', { stdio: 'inherit' });
84
84
 
85
85
  console.log('Update process completed successfully!');
86
-
87
86
  } catch (error) {
88
87
  console.error(`Error during update process: ${error.message}`);
89
88
  process.exit(1);
package/sonar-project.js CHANGED
@@ -1,15 +1,19 @@
1
- const scanner = require('sonarqube-scanner');
1
+ import { createRequire } from 'module';
2
2
 
3
- scanner(
3
+ const require = createRequire(import.meta.url);
4
+ const sonarScannerModule = require('@sonar/scan');
5
+ const sonarScanner = sonarScannerModule.scan || sonarScannerModule.default || sonarScannerModule;
6
+
7
+ sonarScanner(
4
8
  {
5
9
  serverUrl: process.env.SONAR_HOST_URL || 'http://localhost:9000',
6
10
  token: process.env.SONAR_TOKEN,
7
11
  options: {
8
12
  'sonar.projectKey': 'remote-deploy-cli',
9
13
  'sonar.projectName': 'Remote Deploy CLI',
10
- 'sonar.projectVersion': '1.0.0',
14
+ 'sonar.projectVersion': pkg.version,
11
15
  'sonar.sources': 'bin,lib',
12
- 'sonar.tests': 'test', // Assuming tests are in a 'test' directory
16
+ 'sonar.tests': 'test',
13
17
  'sonar.javascript.lcov.reportPaths': 'coverage/lcov.info',
14
18
  'sonar.sourceEncoding': 'UTF-8',
15
19
  },
Binary file