remote-deploy-cli 1.0.0 → 1.1.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.
Files changed (3) hide show
  1. package/README.md +192 -77
  2. package/bin/index.js +141 -5
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -1,119 +1,234 @@
1
1
  # redep (Remote Deploy CLI)
2
2
 
3
- A lightweight Node.js CLI tool for remote execution using a Client-Server architecture. Designed to simplify deployment workflows (e.g., triggering Docker Compose on a remote server).
3
+ > A lightweight, secure Node.js CLI tool for remote execution using a Client-Server architecture. Designed to simplify deployment workflows by triggering commands (like Docker Compose) on remote servers securely.
4
4
 
5
- ## Features
5
+ ![npm version](https://img.shields.io/npm/v/remote-deploy-cli.svg)
6
+ ![License](https://img.shields.io/npm/l/remote-deploy-cli.svg)
6
7
 
7
- - **Client-Server Architecture**: Centralized control where Client sends commands and Server executes them.
8
- - **Secure Communication**: Uses Token-based authentication (Bearer Token) to ensure only authorized clients can execute commands.
9
- - **Configurable**: Easy configuration management for both Client and Server via CLI or Environment Variables.
10
- - **Docker Integration**: Built-in support for `deploy fe` which pulls and restarts containers using Docker Compose.
11
- - **Docker Support**: Can be run as a Docker container itself on the server machine.
12
- - **Logging**: Detailed logs for tracking execution status.
8
+ **Last Updated:** 2026-01-17
13
9
 
14
- ## Architecture
10
+ ## Table of Contents
15
11
 
16
- 1. **Client Machine**: The client that issues deployment commands (e.g., your laptop or CI runner).
17
- 2. **Server Machine**: The server that hosts the application and executes the deployment commands (e.g., `docker compose up`).
12
+ - [Description](#description)
13
+ - [Architecture Overview](#architecture-overview)
14
+ - [Installation](#installation)
15
+ - [Client Commands](#client-commands)
16
+ - [deploy](#deploy)
17
+ - [Client Configuration](#client-configuration)
18
+ - [Server Commands](#server-commands)
19
+ - [listen](#listen)
20
+ - [start (Background)](#start-background)
21
+ - [Server Configuration](#server-configuration)
22
+ - [Command Interactions](#command-interactions)
23
+ - [Configuration Reference](#configuration-reference)
24
+ - [Troubleshooting](#troubleshooting)
25
+ - [License](#license)
18
26
 
19
- Communication happens via HTTP/REST with a shared secret for authentication.
27
+ ---
28
+
29
+ ## Description
30
+
31
+ **redep** allows you to securely trigger deployment scripts on a remote server from your local machine or CI/CD pipeline.
32
+
33
+ ## Architecture Overview
34
+
35
+ 1. **Server (Remote Machine)**: Runs the listener process (`redep listen`). It waits for authenticated HTTP requests and executes local shell commands (e.g., `docker compose up`).
36
+ 2. **Client (Local Machine/CI)**: Sends commands (`redep deploy`) to the Server URL.
37
+
38
+ ---
20
39
 
21
40
  ## Installation
22
41
 
23
- You can install this package globally using npm:
42
+ ### Global Installation (Recommended)
43
+
44
+ To use `redep` as a command-line tool anywhere on your system:
24
45
 
25
46
  ```bash
26
- # Clone the repository
27
- git clone <repo-url>
28
- cd remote-deploy-cli
47
+ npm install -g remote-deploy-cli
48
+ ```
29
49
 
30
- # Install globally (for development/local use)
31
- npm install -g .
50
+ ### Local Installation
51
+
52
+ If you prefer to use it within a specific project (e.g., via `npm scripts`):
53
+
54
+ ```bash
55
+ npm install remote-deploy-cli --save-dev
32
56
  ```
33
57
 
34
- ## Usage
58
+ ---
35
59
 
36
- ### 1. Configure the Server Machine
60
+ ## Client Commands
37
61
 
38
- #### Option A: Running directly on Node.js
62
+ These commands are executed on your **local machine** or **CI/CD runner**.
39
63
 
40
- On the machine where you want to run your application (Server):
64
+ ### `deploy`
41
65
 
42
- 1. **Set the Working Directory**: This is where your `docker-compose.yml` is located.
43
- ```bash
44
- redep config set working_dir /path/to/your/project
45
- ```
66
+ Triggers a deployment on the remote server.
46
67
 
47
- 2. **Set a Secret Key**: A secure password for authentication.
48
- ```bash
49
- redep config set secret_key super-secure-secret-123
50
- ```
68
+ **Syntax:**
69
+ ```bash
70
+ redep deploy <type>
71
+ ```
51
72
 
52
- 3. **Start the Listener**:
53
- ```bash
54
- redep listen
55
- # Optional: Specify port (default 3000)
56
- # redep listen --port 4000
57
- ```
73
+ **Parameters:**
74
+ - `<type>`: The service type to deploy. Currently supports `fe` (frontend).
58
75
 
59
- #### Option B: Running with Docker (Recommended)
76
+ **Requirements:**
77
+ - `SERVER_URL` must be configured.
78
+ - `SECRET_KEY` must match the server's key.
60
79
 
61
- You can run the server itself inside a Docker container. We provide a `docker-compose.server.yml` as an example.
80
+ **Example:**
81
+ ```bash
82
+ # Deploy frontend service
83
+ redep deploy fe
84
+ ```
85
+
86
+ **Expected Output:**
87
+ ```
88
+ [INFO] Deploying fe to http://192.168.1.50:3000...
89
+ [SUCCESS] Deployment triggered successfully.
90
+ ```
91
+
92
+ ### Client Configuration
62
93
 
63
- 1. **Prerequisites**: Ensure Docker and Docker Compose are installed on the server machine.
64
- 2. **Run the Server**:
94
+ Configure the client to know where the server is.
65
95
 
66
- ```bash
67
- # Edit docker-compose.server.yml to map your project directory
68
- # Then run:
69
- docker compose -f docker-compose.server.yml up -d --build
70
- ```
96
+ ```bash
97
+ # Set Server URL
98
+ redep config set server_url http://<server-ip>:3000
99
+
100
+ # Set Secret Key
101
+ redep config set secret_key my-secret-key
102
+ ```
71
103
 
72
- **Configuration via `docker-compose.server.yml`**:
73
- - `volumes`:
74
- - `/var/run/docker.sock:/var/run/docker.sock`: Required to allow the server to manage sibling containers.
75
- - `./your-project:/workspace`: Mount your project directory containing `docker-compose.yml` to `/workspace` inside the container.
76
- - `environment`:
77
- - `WORKING_DIR=/workspace`: Must match the container mount path.
78
- - `SECRET_KEY=your-secret-key`: Set the shared secret.
104
+ ---
79
105
 
80
- ### 2. Configure the Client Machine
106
+ ## Server Commands
81
107
 
82
- On your local machine or CI/CD server (Client):
108
+ These commands are executed on the **remote server** (VPS, VM, etc.).
83
109
 
84
- 1. **Set the Server URL**: The address of your server machine.
85
- ```bash
86
- redep config set server_url http://<server-ip>:3000
87
- ```
88
- Or use `SERVER_URL` env var.
110
+ ### `listen`
89
111
 
90
- 2. **Set the Secret Key**: Must match the key set on the Server.
91
- ```bash
92
- redep config set secret_key super-secure-secret-123
93
- ```
94
- Or use `SECRET_KEY` env var.
112
+ Starts the server in the foreground. Useful for debugging or running inside Docker.
95
113
 
96
- ### 3. Execute Deployment
114
+ **Syntax:**
115
+ ```bash
116
+ redep listen [--port <number>]
117
+ ```
97
118
 
98
- To deploy the Frontend (triggers `docker compose up -d` on the server):
119
+ **Options:**
120
+ - `-p, --port`: Specify port (default: 3000).
99
121
 
122
+ **Example:**
100
123
  ```bash
101
- redep deploy fe
124
+ redep listen --port 4000
102
125
  ```
103
126
 
104
- ## Security Best Practices
127
+ ### `start` (Background)
128
+
129
+ Starts the server in background mode (Daemon).
130
+ * **Auto-PM2**: If `pm2` is installed, it uses PM2 for process management.
131
+ * **Native Fallback**: If `pm2` is missing, it uses Node.js `child_process` to detach.
132
+
133
+ **Syntax:**
134
+ ```bash
135
+ redep start [--port <number>]
136
+ ```
137
+
138
+ **Related Commands:**
139
+ - `redep stop`: Stops the background server.
140
+ - `redep status`: Checks if the server is running.
141
+
142
+ **Example:**
143
+ ```bash
144
+ redep start
145
+ # [SUCCESS] Server started in background using PM2
146
+ ```
147
+
148
+ ### Server Configuration
149
+
150
+ Configure the runtime environment for the server.
151
+
152
+ ```bash
153
+ # Set Working Directory (Where docker-compose.yml lives)
154
+ redep config set working_dir /path/to/project
155
+
156
+ # Set Secret Key
157
+ redep config set secret_key my-secret-key
158
+ ```
159
+
160
+ #### Running with Docker (Recommended)
161
+
162
+ Instead of manual configuration, run the server as a container:
163
+
164
+ ```yaml
165
+ # docker-compose.server.yml
166
+ services:
167
+ deploy-server:
168
+ image: remote-deploy-cli
169
+ volumes:
170
+ - /var/run/docker.sock:/var/run/docker.sock
171
+ - ./my-app:/workspace
172
+ environment:
173
+ - WORKING_DIR=/workspace
174
+ - SECRET_KEY=secure-key
175
+ ```
176
+
177
+ ---
178
+
179
+ ## Command Interactions
180
+
181
+ ### Workflow: Deployment
182
+
183
+ ```mermaid
184
+ sequenceDiagram
185
+ participant Client (CI/Local)
186
+ participant Server (Remote)
187
+
188
+ Note over Client: User runs "redep deploy fe"
189
+ Client->>Client: Read Config (URL, Secret)
190
+ Client->>Server: POST /deploy { type: "fe" } (Auth: Bearer Token)
191
+
192
+ Note over Server: Verify Secret Key
193
+ alt Invalid Key
194
+ Server-->>Client: 403 Forbidden
195
+ Client->>User: Error: Authentication Failed
196
+ else Valid Key
197
+ Server->>Server: Execute "docker compose up" in WORKING_DIR
198
+ Server-->>Client: 200 OK { status: "success" }
199
+ Client->>User: Success Message
200
+ end
201
+ ```
202
+
203
+ ---
204
+
205
+ ## Configuration Reference
206
+
207
+ | Config Key | Env Variable | Description | Context |
208
+ | :------------ | :------------ | :----------------------------------------------- | :--------------- |
209
+ | `server_port` | `SERVER_PORT` | Port for the server to listen on (Default: 3000) | **Server** |
210
+ | `working_dir` | `WORKING_DIR` | Directory to execute commands in | **Server** |
211
+ | `server_url` | `SERVER_URL` | URL of the remote `redep` server | **Client** |
212
+ | `secret_key` | `SECRET_KEY` | Shared secret for authentication | **Both** |
213
+
214
+ ---
215
+
216
+ ## Troubleshooting
217
+
218
+ ### `Error: "working_dir" is not set`
219
+ - **Context**: Server
220
+ - **Fix**: Run `redep config set working_dir /path` or check `docker-compose.yml` environment.
105
221
 
106
- - **Secret Management**: Ensure your `secret_key` is strong and not committed to version control.
107
- - **Network Security**: In production, it is recommended to run the Server behind a reverse proxy (like Nginx) with SSL/TLS (HTTPS) to encrypt the traffic, especially since the secret key is sent in the header.
108
- - **Firewall**: Restrict access to the Server port (default 3000) to only allow IPs from known Client machines.
222
+ ### `Connection Refused`
223
+ - **Context**: Client
224
+ - **Fix**: Ensure server is running (`redep status` or `docker ps`) and port 3000 is open.
109
225
 
110
- ## Development
226
+ ### `403 Forbidden`
227
+ - **Context**: Client
228
+ - **Fix**: Re-check `SECRET_KEY` on both machines. They must match exactly.
111
229
 
112
- - `bin/index.js`: CLI entry point.
113
- - `lib/server/`: Server and execution logic.
114
- - `lib/client/`: Client and command logic.
115
- - `lib/config.js`: Configuration management using `conf`.
230
+ ---
116
231
 
117
232
  ## License
118
233
 
119
- ISC
234
+ ISC © 2026
package/bin/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  import 'dotenv/config';
4
4
  import { Command } from 'commander';
5
+ import { spawn } from 'child_process';
5
6
  import { logger } from '../lib/logger.js';
6
7
  import { getConfig, setConfig, getAllConfig, clearConfig } from '../lib/config.js';
7
8
  import { startServer } from '../lib/server/index.js';
@@ -53,21 +54,156 @@ configCommand
53
54
 
54
55
  program.addCommand(configCommand);
55
56
 
57
+ // Background Process Management
58
+ program
59
+ .command('start')
60
+ .description('Start the server in background (daemon mode) using PM2 if available')
61
+ .option('-p, --port <port>', 'Port to listen on')
62
+ .action((options) => {
63
+ // Try to use PM2 first
64
+ try {
65
+ // Check if PM2 is available via API
66
+ // We'll use a dynamic import or checking for the pm2 binary in a real scenario
67
+ // But here we can just try to spawn 'pm2' command
68
+
69
+ const args = ['start', process.argv[1], '--name', 'redep-server', '--', 'listen'];
70
+ if (options.port) {
71
+ args.push('--port', options.port);
72
+ }
73
+
74
+ const pm2 = spawn('pm2', args, {
75
+ stdio: 'inherit',
76
+ shell: true // Required to find pm2 in PATH
77
+ });
78
+
79
+ pm2.on('error', () => {
80
+ // Fallback to native spawn if PM2 is not found/fails
81
+ logger.info('PM2 not found, falling back to native background process...');
82
+ startNativeBackground(options);
83
+ });
84
+
85
+ pm2.on('close', (code) => {
86
+ if (code !== 0) {
87
+ logger.warn('PM2 start failed, falling back to native background process...');
88
+ startNativeBackground(options);
89
+ } else {
90
+ logger.success('Server started in background using PM2');
91
+ }
92
+ });
93
+
94
+ } catch (e) {
95
+ startNativeBackground(options);
96
+ }
97
+ });
98
+
99
+ function startNativeBackground(options) {
100
+ const existingPid = getConfig('server_pid');
101
+
102
+ if (existingPid) {
103
+ try {
104
+ process.kill(existingPid, 0);
105
+ logger.warn(`Server is already running with PID ${existingPid}`);
106
+ return;
107
+ } catch (e) {
108
+ // Process doesn't exist, clear stale PID
109
+ setConfig('server_pid', null);
110
+ }
111
+ }
112
+
113
+ const args = ['listen'];
114
+ if (options.port) {
115
+ args.push('--port', options.port);
116
+ }
117
+
118
+ const child = spawn(process.argv[0], [process.argv[1], ...args], {
119
+ detached: true,
120
+ stdio: 'ignore',
121
+ windowsHide: true
122
+ });
123
+
124
+ child.unref();
125
+ setConfig('server_pid', child.pid);
126
+ logger.success(`Server started in background (native) with PID ${child.pid}`);
127
+ }
128
+
129
+ program
130
+ .command('stop')
131
+ .description('Stop the background server')
132
+ .action(() => {
133
+ // Try PM2 stop first
134
+ const pm2 = spawn('pm2', ['stop', 'redep-server'], { stdio: 'ignore', shell: true });
135
+
136
+ pm2.on('close', (code) => {
137
+ if (code === 0) {
138
+ logger.success('Server stopped (PM2)');
139
+ return;
140
+ }
141
+
142
+ // Fallback to native stop
143
+ const pid = getConfig('server_pid');
144
+ if (!pid) {
145
+ logger.warn('No active server found.');
146
+ return;
147
+ }
148
+
149
+ try {
150
+ process.kill(pid);
151
+ setConfig('server_pid', null);
152
+ logger.success(`Server stopped (PID ${pid})`);
153
+ } catch (e) {
154
+ if (e.code === 'ESRCH') {
155
+ logger.warn(`Process ${pid} not found. Cleaning up config.`);
156
+ setConfig('server_pid', null);
157
+ } else {
158
+ logger.error(`Failed to stop server: ${e.message}`);
159
+ }
160
+ }
161
+ });
162
+ });
163
+
164
+ program
165
+ .command('status')
166
+ .description('Check server status')
167
+ .action(() => {
168
+ // Try PM2 status first
169
+ const pm2 = spawn('pm2', ['describe', 'redep-server'], { stdio: 'inherit', shell: true });
170
+
171
+ pm2.on('close', (code) => {
172
+ if (code !== 0) {
173
+ // Fallback to native status
174
+ const pid = getConfig('server_pid');
175
+
176
+ if (!pid) {
177
+ logger.info('Server is NOT running.');
178
+ return;
179
+ }
180
+
181
+ try {
182
+ process.kill(pid, 0);
183
+ logger.success(`Server is RUNNING (PID ${pid})`);
184
+ } catch (e) {
185
+ logger.warn(`Server is NOT running (Stale PID ${pid} found).`);
186
+ setConfig('server_pid', null);
187
+ }
188
+ }
189
+ });
190
+ });
191
+
56
192
  // Server Command
57
193
  program
58
194
  .command('listen')
59
195
  .description('Start the server to listen for commands')
60
196
  .option('-p, --port <port>', 'Port to listen on', 3000)
61
197
  .action((options) => {
62
- const port = options.port || process.env.SERVER_PORT || getConfig('server_port') || 3000;
63
- const secret = process.env.SECRET_KEY || getConfig('secret_key');
198
+ const port = options.port || getConfig('server_port') || process.env.SERVER_PORT || 3000;
199
+ const secret = getConfig('secret_key') || process.env.SECRET_KEY;
64
200
 
65
201
  if (!secret) {
66
202
  logger.warn('Warning: No "secret_key" set in config or SECRET_KEY env var. Communication might be insecure or fail if client requires it.');
67
203
  logger.info('Run "redep config set secret_key <your-secret>" or set SECRET_KEY env var.');
68
204
  }
69
205
 
70
- const workingDir = process.env.WORKING_DIR || getConfig('working_dir');
206
+ const workingDir = getConfig('working_dir') || process.env.WORKING_DIR;
71
207
  if (!workingDir) {
72
208
  logger.error('Error: "working_dir" is not set. Please set it using "redep config set working_dir <path>" or WORKING_DIR env var.');
73
209
  process.exit(1);
@@ -81,8 +217,8 @@ program
81
217
  .command('deploy <type>')
82
218
  .description('Deploy a service (e.g., "fe") to the server machine')
83
219
  .action(async (type) => {
84
- const serverUrl = process.env.SERVER_URL || getConfig('server_url');
85
- const secret = process.env.SECRET_KEY || getConfig('secret_key');
220
+ const serverUrl = getConfig('server_url') || process.env.SERVER_URL;
221
+ const secret = getConfig('secret_key') || process.env.SECRET_KEY;
86
222
 
87
223
  if (!serverUrl) {
88
224
  logger.error('Error: "server_url" is not set. Set SERVER_URL env var or run "redep config set server_url <url>"');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remote-deploy-cli",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,6 +23,7 @@
23
23
  "dotenv": "^17.2.3",
24
24
  "express": "^4.18.2",
25
25
  "helmet": "^7.1.0",
26
- "morgan": "^1.10.0"
26
+ "morgan": "^1.10.0",
27
+ "pm2": "^6.0.14"
27
28
  }
28
29
  }