@abyrd9/harbor-cli 1.0.1 ā 1.1.0
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 +248 -37
- package/dist/index.js +87 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,47 +1,58 @@
|
|
|
1
1
|
# Harbor CLI
|
|
2
2
|
|
|
3
|
-
A CLI tool for
|
|
3
|
+
A CLI tool for managing local development services with ease. Harbor helps you orchestrate multiple development services in tmux sessions, perfect for microservices and polyglot projects.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
## ⨠Features
|
|
6
|
+
|
|
7
|
+
- š ļø **Automatic Service Discovery**: Detects project types and suggests appropriate commands
|
|
8
|
+
- š **Flexible Configuration**: Store config in `harbor.json` or `package.json`
|
|
9
|
+
- š **One-Command Launch**: Start all services with a single command
|
|
10
|
+
- š **Dependency Management**: Automatically checks for required system dependencies
|
|
11
|
+
- šÆ **Multi-Language Support**: Works with Node.js, Go, Rust, Python, PHP, Java, and more
|
|
12
|
+
- š„ļø **Tmux Integration**: Professional terminal multiplexing for service management
|
|
7
13
|
|
|
8
14
|
## Installation
|
|
9
15
|
|
|
10
16
|
```bash
|
|
11
|
-
npm
|
|
17
|
+
npm install -g @abyrd9/harbor-cli
|
|
12
18
|
```
|
|
13
19
|
|
|
14
20
|
## Prerequisites
|
|
15
21
|
|
|
16
|
-
|
|
22
|
+
Harbor automatically checks for these required dependencies:
|
|
23
|
+
|
|
24
|
+
- **[tmux](https://github.com/tmux/tmux/wiki/Installing)** - Terminal multiplexer for managing services
|
|
25
|
+
- **[jq](https://stedolan.github.io/jq/download/)** - JSON processor for service management
|
|
17
26
|
|
|
18
|
-
|
|
19
|
-
- [jq](https://stedolan.github.io/jq/download/) (for JSON processing within tmux)
|
|
27
|
+
If missing, Harbor will provide installation instructions.
|
|
20
28
|
|
|
21
29
|
## Quick Start
|
|
22
30
|
|
|
23
|
-
1. Initialize your
|
|
24
|
-
```bash
|
|
25
|
-
harbor dock
|
|
26
|
-
```
|
|
31
|
+
1. **Initialize your project**:
|
|
32
|
+
```bash
|
|
33
|
+
harbor dock
|
|
34
|
+
```
|
|
35
|
+
This scans your directory structure and creates a harbor configuration.
|
|
27
36
|
|
|
28
|
-
2. Add
|
|
29
|
-
```bash
|
|
30
|
-
harbor moor
|
|
31
|
-
```
|
|
37
|
+
2. **Add more services** (optional):
|
|
38
|
+
```bash
|
|
39
|
+
harbor moor
|
|
40
|
+
```
|
|
41
|
+
Scans for new services and adds them to your configuration.
|
|
32
42
|
|
|
33
|
-
3. Launch
|
|
34
|
-
```bash
|
|
35
|
-
harbor launch
|
|
36
|
-
```
|
|
43
|
+
3. **Launch all services**:
|
|
44
|
+
```bash
|
|
45
|
+
harbor launch
|
|
46
|
+
```
|
|
47
|
+
Starts all configured services in organized tmux windows.
|
|
37
48
|
|
|
38
49
|
## Configuration
|
|
39
50
|
|
|
40
|
-
Harbor
|
|
51
|
+
Harbor supports two configuration formats:
|
|
41
52
|
|
|
42
|
-
### harbor.json
|
|
53
|
+
### Option 1: harbor.json
|
|
43
54
|
|
|
44
|
-
|
|
55
|
+
Create a dedicated configuration file:
|
|
45
56
|
|
|
46
57
|
```json
|
|
47
58
|
{
|
|
@@ -57,34 +68,234 @@ Contains your service configurations that are used to launch the services:
|
|
|
57
68
|
"command": "go run ."
|
|
58
69
|
},
|
|
59
70
|
{
|
|
60
|
-
"name": "
|
|
61
|
-
"path": "./
|
|
62
|
-
"command": "
|
|
71
|
+
"name": "database",
|
|
72
|
+
"path": "./db",
|
|
73
|
+
"command": "docker-compose up"
|
|
74
|
+
}
|
|
75
|
+
],
|
|
76
|
+
"before": [
|
|
77
|
+
{
|
|
78
|
+
"path": "./",
|
|
79
|
+
"command": "echo 'Starting development environment...' && docker-compose up -d"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"path": "./scripts",
|
|
83
|
+
"command": "./setup-dev.sh"
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
"after": [
|
|
87
|
+
{
|
|
88
|
+
"path": "./",
|
|
89
|
+
"command": "echo 'Development environment ready!'"
|
|
63
90
|
}
|
|
64
91
|
]
|
|
65
92
|
}
|
|
66
93
|
```
|
|
67
94
|
|
|
95
|
+
### Option 2: package.json
|
|
96
|
+
|
|
97
|
+
Store configuration directly in your `package.json`:
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"name": "my-project",
|
|
102
|
+
"version": "1.0.0",
|
|
103
|
+
"harbor": {
|
|
104
|
+
"services": [
|
|
105
|
+
{
|
|
106
|
+
"name": "frontend",
|
|
107
|
+
"path": "./frontend",
|
|
108
|
+
"command": "npm run dev"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"name": "api",
|
|
112
|
+
"path": "./api",
|
|
113
|
+
"command": "go run main.go"
|
|
114
|
+
}
|
|
115
|
+
],
|
|
116
|
+
"before": [
|
|
117
|
+
{
|
|
118
|
+
"path": "./",
|
|
119
|
+
"command": "docker-compose up -d database"
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
"after": [
|
|
123
|
+
{
|
|
124
|
+
"path": "./",
|
|
125
|
+
"command": "echo 'All services are running!'"
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
68
132
|
## Commands
|
|
69
133
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
134
|
+
| Command | Description |
|
|
135
|
+
|---------|-------------|
|
|
136
|
+
| `harbor dock` | Initialize harbor configuration by scanning project directories |
|
|
137
|
+
| `harbor moor` | Scan for and add new services to existing configuration |
|
|
138
|
+
| `harbor launch` | Start all services in tmux sessions |
|
|
139
|
+
| `harbor --help` | Show help and available commands |
|
|
140
|
+
| `harbor --version` | Show version information |
|
|
141
|
+
|
|
142
|
+
### Command Options
|
|
143
|
+
|
|
144
|
+
- `-p, --path <path>`: Specify project root path (defaults to `./`)
|
|
145
|
+
|
|
146
|
+
## Supported Project Types
|
|
147
|
+
|
|
148
|
+
Harbor automatically detects and configures services for:
|
|
149
|
+
|
|
150
|
+
- **Node.js**: `package.json` ā `npm run dev`
|
|
151
|
+
- **Go**: `go.mod` ā `go run .`
|
|
152
|
+
- **Rust**: `Cargo.toml` ā `cargo run`
|
|
153
|
+
- **Python**: `requirements.txt` ā `python app.py`
|
|
154
|
+
- **PHP**: `composer.json` ā `php artisan serve`
|
|
155
|
+
- **Java**: `pom.xml`/`build.gradle` ā `mvn spring-boot:run` or `./gradlew bootRun`
|
|
156
|
+
|
|
157
|
+
## How It Works
|
|
158
|
+
|
|
159
|
+
1. **Service Discovery**: Harbor scans your directory for folders containing project files
|
|
160
|
+
2. **Command Detection**: Based on project type, suggests appropriate development commands
|
|
161
|
+
3. **Configuration Generation**: Creates/updates harbor config with discovered services
|
|
162
|
+
4. **Tmux Session**: Launches each service in its own tmux window with proper working directories
|
|
163
|
+
5. **Process Management**: All services run independently with proper I/O handling
|
|
73
164
|
|
|
74
|
-
## Terminal Multiplexer
|
|
165
|
+
## Terminal Multiplexer (Tmux)
|
|
75
166
|
|
|
76
|
-
Harbor uses tmux for
|
|
167
|
+
Harbor uses tmux for professional service management. Essential shortcuts:
|
|
77
168
|
|
|
78
|
-
|
|
79
|
-
- `Ctrl+a
|
|
80
|
-
- `Ctrl+a
|
|
81
|
-
- `Ctrl+a
|
|
82
|
-
- `Ctrl+
|
|
169
|
+
### Session Management
|
|
170
|
+
- `Ctrl+a d` - Detach from session (services continue running)
|
|
171
|
+
- `Ctrl+a :attach` - Reattach to session
|
|
172
|
+
- `Ctrl+a &` - Kill current window
|
|
173
|
+
- `Ctrl+a ?` - Show all keybindings
|
|
174
|
+
|
|
175
|
+
### Window Navigation
|
|
176
|
+
- `Ctrl+a c` - Create new window
|
|
177
|
+
- `Ctrl+a n` - Next window
|
|
178
|
+
- `Ctrl+a p` - Previous window
|
|
179
|
+
- `Ctrl+a 0-9` - Jump to window number
|
|
180
|
+
- `Ctrl+a w` - List all windows
|
|
181
|
+
|
|
182
|
+
### Pane Management
|
|
183
|
+
- `Ctrl+a %` - Split window vertically
|
|
184
|
+
- `Ctrl+a "` - Split window horizontally
|
|
185
|
+
- `Ctrl+a ā/ā/ā/ā` - Navigate between panes
|
|
186
|
+
- `Ctrl+a x` - Close current pane
|
|
187
|
+
|
|
188
|
+
## Advanced Usage
|
|
189
|
+
|
|
190
|
+
### Custom Commands
|
|
191
|
+
Override auto-detected commands by editing your configuration:
|
|
192
|
+
|
|
193
|
+
```json
|
|
194
|
+
{
|
|
195
|
+
"services": [
|
|
196
|
+
{
|
|
197
|
+
"name": "custom-service",
|
|
198
|
+
"path": "./my-service",
|
|
199
|
+
"command": "custom-command --with-flags"
|
|
200
|
+
}
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Before/After Scripts
|
|
206
|
+
Run custom scripts before and after your services start:
|
|
207
|
+
|
|
208
|
+
```json
|
|
209
|
+
{
|
|
210
|
+
"before": [
|
|
211
|
+
{
|
|
212
|
+
"path": "./infrastructure",
|
|
213
|
+
"command": "docker-compose up -d"
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"path": "./",
|
|
217
|
+
"command": "npm run setup:dev"
|
|
218
|
+
}
|
|
219
|
+
],
|
|
220
|
+
"after": [
|
|
221
|
+
{
|
|
222
|
+
"path": "./",
|
|
223
|
+
"command": "npm run seed:database"
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"path": "./scripts",
|
|
227
|
+
"command": "./notify-dev-ready.sh"
|
|
228
|
+
}
|
|
229
|
+
]
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Execution Flow:**
|
|
234
|
+
1. **Before scripts** run sequentially before services start
|
|
235
|
+
2. Services launch in tmux windows
|
|
236
|
+
3. **After scripts** run sequentially after services are ready
|
|
237
|
+
|
|
238
|
+
**Use Cases:**
|
|
239
|
+
- Set up databases or infrastructure
|
|
240
|
+
- Run database migrations or seeds
|
|
241
|
+
- Send notifications when environment is ready
|
|
242
|
+
- Clean up temporary files
|
|
243
|
+
- Run integration tests
|
|
244
|
+
|
|
245
|
+
### Selective Launch
|
|
246
|
+
Currently launches all services. Future versions may support selective service launching.
|
|
247
|
+
|
|
248
|
+
### Environment Variables
|
|
249
|
+
Services inherit your shell environment. Use `.env` files or export variables before running `harbor launch`.
|
|
250
|
+
|
|
251
|
+
## Troubleshooting
|
|
252
|
+
|
|
253
|
+
### Common Issues
|
|
254
|
+
|
|
255
|
+
1. **"Missing required dependencies"**
|
|
256
|
+
- Install tmux and jq as indicated in the error message
|
|
257
|
+
|
|
258
|
+
2. **"No harbor configuration found"**
|
|
259
|
+
- Run `harbor dock` to initialize configuration
|
|
260
|
+
- Ensure you're in the correct project directory
|
|
261
|
+
|
|
262
|
+
3. **Services not starting**
|
|
263
|
+
- Check that commands are correct in your harbor configuration
|
|
264
|
+
- Verify project dependencies are installed in each service directory
|
|
265
|
+
- Check tmux windows for error messages
|
|
266
|
+
|
|
267
|
+
4. **Tmux not responding**
|
|
268
|
+
- Try `tmux kill-session -t harbor` to clean up
|
|
269
|
+
- Restart with `harbor launch`
|
|
270
|
+
|
|
271
|
+
## Examples
|
|
272
|
+
|
|
273
|
+
### Monorepo Setup
|
|
274
|
+
```
|
|
275
|
+
my-project/
|
|
276
|
+
āāā frontend/ (package.json)
|
|
277
|
+
āāā backend/ (go.mod)
|
|
278
|
+
āāā database/ (docker-compose.yml)
|
|
279
|
+
āāā harbor.json
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Polyglot Microservices
|
|
283
|
+
```
|
|
284
|
+
services/
|
|
285
|
+
āāā auth-service/ (Node.js)
|
|
286
|
+
āāā user-service/ (Go)
|
|
287
|
+
āāā payment-service/ (Python)
|
|
288
|
+
āāā notification-service/ (Rust)
|
|
289
|
+
```
|
|
83
290
|
|
|
84
291
|
## Contributing
|
|
85
292
|
|
|
86
|
-
Contributions are welcome! Please feel free to
|
|
293
|
+
Contributions are welcome! Please feel free to:
|
|
294
|
+
|
|
295
|
+
- Report bugs and request features via GitHub Issues
|
|
296
|
+
- Submit pull requests for improvements
|
|
297
|
+
- Help improve documentation
|
|
87
298
|
|
|
88
299
|
## License
|
|
89
300
|
|
|
90
|
-
MIT
|
|
301
|
+
MIT - See LICENSE file for details
|
package/dist/index.js
CHANGED
|
@@ -148,6 +148,36 @@ function validateConfig(config) {
|
|
|
148
148
|
return 'Service path is required';
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
|
+
// Validate before scripts
|
|
152
|
+
if (config.before && !Array.isArray(config.before)) {
|
|
153
|
+
return 'Before scripts must be an array';
|
|
154
|
+
}
|
|
155
|
+
if (config.before) {
|
|
156
|
+
for (let i = 0; i < config.before.length; i++) {
|
|
157
|
+
const script = config.before[i];
|
|
158
|
+
if (!script.path) {
|
|
159
|
+
return `Before script ${i} must have a path`;
|
|
160
|
+
}
|
|
161
|
+
if (!script.command) {
|
|
162
|
+
return `Before script ${i} must have a command`;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Validate after scripts
|
|
167
|
+
if (config.after && !Array.isArray(config.after)) {
|
|
168
|
+
return 'After scripts must be an array';
|
|
169
|
+
}
|
|
170
|
+
if (config.after) {
|
|
171
|
+
for (let i = 0; i < config.after.length; i++) {
|
|
172
|
+
const script = config.after[i];
|
|
173
|
+
if (!script.path) {
|
|
174
|
+
return `After script ${i} must have a path`;
|
|
175
|
+
}
|
|
176
|
+
if (!script.command) {
|
|
177
|
+
return `After script ${i} must have a command`;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
151
181
|
return null;
|
|
152
182
|
}
|
|
153
183
|
async function generateDevFile(dirPath) {
|
|
@@ -178,6 +208,8 @@ async function generateDevFile(dirPath) {
|
|
|
178
208
|
writeToPackageJson = true;
|
|
179
209
|
config = {
|
|
180
210
|
services: [],
|
|
211
|
+
before: [],
|
|
212
|
+
after: [],
|
|
181
213
|
};
|
|
182
214
|
console.log('Creating new harbor config in package.json...');
|
|
183
215
|
}
|
|
@@ -185,6 +217,8 @@ async function generateDevFile(dirPath) {
|
|
|
185
217
|
// No package.json, create harbor.json
|
|
186
218
|
config = {
|
|
187
219
|
services: [],
|
|
220
|
+
before: [],
|
|
221
|
+
after: [],
|
|
188
222
|
};
|
|
189
223
|
console.log('Creating new harbor.json...');
|
|
190
224
|
}
|
|
@@ -296,6 +330,41 @@ async function readHarborConfig() {
|
|
|
296
330
|
}
|
|
297
331
|
throw new Error('No harbor configuration found in harbor.json or package.json');
|
|
298
332
|
}
|
|
333
|
+
async function execute(scripts, scriptType) {
|
|
334
|
+
if (!scripts || scripts.length === 0) {
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
console.log(`\nš Executing ${scriptType} scripts...`);
|
|
338
|
+
for (let i = 0; i < scripts.length; i++) {
|
|
339
|
+
const script = scripts[i];
|
|
340
|
+
console.log(`\nš Running ${scriptType} script ${i + 1}/${scripts.length}: ${script.command}`);
|
|
341
|
+
console.log(` š In directory: ${script.path}`);
|
|
342
|
+
try {
|
|
343
|
+
await new Promise((resolve, reject) => {
|
|
344
|
+
const process = spawn('sh', ['-c', `cd "${script.path}" && ${script.command}`], {
|
|
345
|
+
stdio: 'inherit',
|
|
346
|
+
});
|
|
347
|
+
process.on('close', (code) => {
|
|
348
|
+
if (code === 0) {
|
|
349
|
+
console.log(`ā
${scriptType} script ${i + 1} completed successfully`);
|
|
350
|
+
resolve(null);
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
reject(new Error(`${scriptType} script ${i + 1} exited with code ${code}`));
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
process.on('error', (err) => {
|
|
357
|
+
reject(new Error(`${scriptType} script ${i + 1} failed: ${err.message}`));
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
catch (err) {
|
|
362
|
+
console.error(`ā Error executing ${scriptType} script ${i + 1}:`, err instanceof Error ? err.message : 'Unknown error');
|
|
363
|
+
throw err;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
console.log(`\nā
All ${scriptType} scripts completed successfully`);
|
|
367
|
+
}
|
|
299
368
|
async function runServices() {
|
|
300
369
|
const hasHarborConfig = checkHasHarborConfig();
|
|
301
370
|
if (!hasHarborConfig) {
|
|
@@ -318,6 +387,14 @@ async function runServices() {
|
|
|
318
387
|
console.error('Error reading config:', err);
|
|
319
388
|
process.exit(1);
|
|
320
389
|
}
|
|
390
|
+
// Execute before scripts
|
|
391
|
+
try {
|
|
392
|
+
await execute(config.before || [], 'before');
|
|
393
|
+
}
|
|
394
|
+
catch (err) {
|
|
395
|
+
console.error('ā Before scripts failed, aborting launch');
|
|
396
|
+
process.exit(1);
|
|
397
|
+
}
|
|
321
398
|
// Ensure scripts exist and are executable
|
|
322
399
|
await ensureScriptsExist();
|
|
323
400
|
// Execute the script directly using spawn to handle I/O streams
|
|
@@ -330,12 +407,20 @@ async function runServices() {
|
|
|
330
407
|
console.error(`Error running dev.sh: ${err}`);
|
|
331
408
|
process.exit(1);
|
|
332
409
|
});
|
|
333
|
-
command.on('close', (code) => {
|
|
410
|
+
command.on('close', async (code) => {
|
|
334
411
|
if (code !== 0) {
|
|
335
412
|
console.error(`dev.sh exited with code ${code}`);
|
|
336
413
|
process.exit(1);
|
|
337
414
|
}
|
|
338
|
-
|
|
415
|
+
// Execute after scripts
|
|
416
|
+
try {
|
|
417
|
+
await execute(config.after || [], 'after');
|
|
418
|
+
resolve();
|
|
419
|
+
}
|
|
420
|
+
catch (err) {
|
|
421
|
+
console.error('ā After scripts failed');
|
|
422
|
+
process.exit(1);
|
|
423
|
+
}
|
|
339
424
|
});
|
|
340
425
|
});
|
|
341
426
|
}
|