@hackmd/spm 0.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/LICENSE +21 -0
- package/README.md +170 -0
- package/completions/bash/spm.bash +93 -0
- package/completions/bash/spm_app_selector.sh +81 -0
- package/completions/fish/functions/spm_app_selector.fish +61 -0
- package/completions/fish/spm.fish +44 -0
- package/completions/zsh/_spm +57 -0
- package/completions/zsh/spm_app_selector.zsh +80 -0
- package/dist/index.js +2604 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 HackMD Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# SPM — Simpler Process Manager
|
|
2
|
+
|
|
3
|
+
A minimal process manager implementing a lean subset of [pm2](https://pm2.keymetrics.io/) features, with some extensions, designed for better development script setup. Lightweight, zero daemon, and ecosystem config compatible.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Start / Stop / Restart** — Manage multiple services from a single config
|
|
8
|
+
- **Multi-instance** — Run multiple instances with port/env auto-increment
|
|
9
|
+
- **Log management** — Tail, filter, flush, and rotate logs
|
|
10
|
+
- **JSON output** — `jlist` for machine-readable status (CI/scripting)
|
|
11
|
+
- **Log rotation** — Size-based rotation and retention with optional background watcher
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g @hackmd/spm
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or with Bun:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
bun add -g @hackmd/spm
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
1. Create an ecosystem config file (e.g. `ecosystem.config.js`) in your project:
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
export default {
|
|
31
|
+
apps: [
|
|
32
|
+
{
|
|
33
|
+
name: 'api',
|
|
34
|
+
script: 'node',
|
|
35
|
+
args: 'server.js',
|
|
36
|
+
instances: 2,
|
|
37
|
+
env: { PORT: '3000' },
|
|
38
|
+
increment_vars: ['PORT'],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'worker',
|
|
42
|
+
script: 'node',
|
|
43
|
+
args: 'worker.js',
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
2. Run SPM:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
spm start # Start all services
|
|
53
|
+
spm start api # Start specific service
|
|
54
|
+
spm list # List services and PIDs
|
|
55
|
+
spm stop # Stop all
|
|
56
|
+
spm restart api # Restart specific service
|
|
57
|
+
spm logs api -t # Tail logs
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Configuration
|
|
61
|
+
|
|
62
|
+
| Option | Description |
|
|
63
|
+
|--------|-------------|
|
|
64
|
+
| `name` | Service identifier |
|
|
65
|
+
| `script` | Command to run (e.g. `node`, `bun`, `python`) |
|
|
66
|
+
| `args` | Arguments passed to the script |
|
|
67
|
+
| `instances` | Number of instances (default: 1) |
|
|
68
|
+
| `env` | Environment variables |
|
|
69
|
+
| `increment_vars` | Env vars to increment per instance (e.g. `PORT`) |
|
|
70
|
+
|
|
71
|
+
Config file is resolved from `./ecosystem.custom.config.js` by default. Override with `--config`:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
spm --config ./my-ecosystem.config.js start
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Commands
|
|
78
|
+
|
|
79
|
+
| Command | Description |
|
|
80
|
+
|---------|-------------|
|
|
81
|
+
| `spm start [service]` | Start all or a specific service |
|
|
82
|
+
| `spm stop [service]` | Stop processes (alias: `kill`) |
|
|
83
|
+
| `spm restart [service]` | Restart processes |
|
|
84
|
+
| `spm list` | List services and running PIDs |
|
|
85
|
+
| `spm jlist` | JSON output of service status |
|
|
86
|
+
| `spm logs [service]` | View logs (`-t` tail, `-n` lines, `-f` filter) |
|
|
87
|
+
| `spm flush [service]` | Clear log files |
|
|
88
|
+
| `spm rotate start` | Start log rotation watcher |
|
|
89
|
+
| `spm rotate stop` | Stop rotation watcher |
|
|
90
|
+
|
|
91
|
+
## Log Rotation
|
|
92
|
+
|
|
93
|
+
Logs are stored in `~/.spm2/logs/`. Rotation:
|
|
94
|
+
|
|
95
|
+
- Rotates when a log exceeds 10MB
|
|
96
|
+
- Removes logs older than 3 days
|
|
97
|
+
- Run `spm rotate start` for a background watcher
|
|
98
|
+
|
|
99
|
+
## Shell Integration
|
|
100
|
+
|
|
101
|
+
Completions require [jq](https://jqlang.github.io/jq/). App selector also requires [fzf](https://github.com/junegunn/fzf).
|
|
102
|
+
|
|
103
|
+
### Bash
|
|
104
|
+
|
|
105
|
+
**Completions** — Source the script (requires [bash-completion](https://github.com/scop/bash-completion); on macOS: `brew install bash-completion@2`):
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# After npm install -g @hackmd/spm:
|
|
109
|
+
source $(npm root -g)/@hackmd/spm/completions/bash/spm.bash
|
|
110
|
+
# Or add to ~/.bashrc:
|
|
111
|
+
# source /path/to/spm/completions/bash/spm.bash
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**App selector** — Source the script:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
source $(npm root -g)/@hackmd/spm/completions/bash/spm_app_selector.sh
|
|
118
|
+
# Then:
|
|
119
|
+
spm_app_selector start
|
|
120
|
+
spm_app_selector --appName=api restart
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Zsh
|
|
124
|
+
|
|
125
|
+
**Completions** — Add the completion dir to `fpath` and ensure compinit runs:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# After npm install -g @hackmd/spm:
|
|
129
|
+
fpath=($(npm root -g)/@hackmd/spm/completions/zsh $fpath)
|
|
130
|
+
compinit
|
|
131
|
+
# Or copy to a dir in $fpath:
|
|
132
|
+
mkdir -p ~/.zsh/completions
|
|
133
|
+
cp $(npm root -g)/@hackmd/spm/completions/zsh/_spm ~/.zsh/completions/
|
|
134
|
+
# Add to ~/.zshrc: fpath=(~/.zsh/completions $fpath)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**App selector** — Source the script:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
source $(npm root -g)/@hackmd/spm/completions/zsh/spm_app_selector.zsh
|
|
141
|
+
spm_app_selector start
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Fish
|
|
145
|
+
|
|
146
|
+
**Completions** — Copy to your Fish config:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
mkdir -p ~/.config/fish/completions
|
|
150
|
+
cp $(npm root -g)/@hackmd/spm/completions/fish/spm.fish ~/.config/fish/completions/
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**App selector** — Copy the function:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
mkdir -p ~/.config/fish/functions
|
|
157
|
+
cp $(npm root -g)/@hackmd/spm/completions/fish/functions/spm_app_selector.fish ~/.config/fish/functions/
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Usage:
|
|
161
|
+
|
|
162
|
+
```fish
|
|
163
|
+
spm_app_selector start
|
|
164
|
+
spm_app_selector --appName=api restart
|
|
165
|
+
spm_app_selector --config=ecosystem.config.js logs
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
MIT
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Bash completion for spm
|
|
2
|
+
# Usage: source this file or copy to /etc/bash_completion.d/spm
|
|
3
|
+
# Requires: bash-completion (Linux) or bash-completion2 (macOS: brew install bash-completion@2)
|
|
4
|
+
|
|
5
|
+
_spm_services() {
|
|
6
|
+
spm jlist 2>/dev/null | jq -r '.[].name'
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
_spm() {
|
|
10
|
+
local cur prev words cword
|
|
11
|
+
if declare -f _init_completion &>/dev/null; then
|
|
12
|
+
_init_completion -s || return
|
|
13
|
+
else
|
|
14
|
+
words=("${COMP_WORDS[@]}")
|
|
15
|
+
cword=$COMP_CWORD
|
|
16
|
+
cur="${words[cword]}"
|
|
17
|
+
prev="${words[cword-1]}"
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
local subcommands="start stop kill logs list jlist restart flush rotate"
|
|
21
|
+
local rotate_subcommands="start watch stop"
|
|
22
|
+
|
|
23
|
+
case $prev in
|
|
24
|
+
-c|--config)
|
|
25
|
+
if declare -f _filedir &>/dev/null; then
|
|
26
|
+
_filedir
|
|
27
|
+
else
|
|
28
|
+
COMPREPLY=($(compgen -f -X '!*.@(js|mjs|cjs)' -- "$cur"))
|
|
29
|
+
fi
|
|
30
|
+
return
|
|
31
|
+
;;
|
|
32
|
+
-s|--signal)
|
|
33
|
+
COMPREPLY=($(compgen -W "SIGTERM SIGINT SIGKILL" -- "$cur"))
|
|
34
|
+
return
|
|
35
|
+
;;
|
|
36
|
+
-n|--lines)
|
|
37
|
+
return
|
|
38
|
+
;;
|
|
39
|
+
-f|--filter)
|
|
40
|
+
return
|
|
41
|
+
;;
|
|
42
|
+
--cleanup-interval)
|
|
43
|
+
return
|
|
44
|
+
;;
|
|
45
|
+
spm)
|
|
46
|
+
COMPREPLY=($(compgen -W "$subcommands -c --config -v --verbose -h --help" -- "$cur"))
|
|
47
|
+
return
|
|
48
|
+
;;
|
|
49
|
+
rotate)
|
|
50
|
+
COMPREPLY=($(compgen -W "$rotate_subcommands" -- "$cur"))
|
|
51
|
+
return
|
|
52
|
+
;;
|
|
53
|
+
esac
|
|
54
|
+
|
|
55
|
+
# Check if we're completing after a subcommand that takes a service name
|
|
56
|
+
for ((i = 1; i < cword; i++)); do
|
|
57
|
+
case ${words[i]} in
|
|
58
|
+
start|stop|kill|restart|logs|flush)
|
|
59
|
+
COMPREPLY=($(compgen -W "$(_spm_services)" -- "$cur"))
|
|
60
|
+
return
|
|
61
|
+
;;
|
|
62
|
+
rotate)
|
|
63
|
+
if [[ $i -eq $((cword - 1)) ]]; then
|
|
64
|
+
COMPREPLY=($(compgen -W "$rotate_subcommands" -- "$cur"))
|
|
65
|
+
fi
|
|
66
|
+
return
|
|
67
|
+
;;
|
|
68
|
+
esac
|
|
69
|
+
done
|
|
70
|
+
|
|
71
|
+
# Check for logs options
|
|
72
|
+
if [[ " ${words[@]} " =~ " logs " ]]; then
|
|
73
|
+
case $cur in
|
|
74
|
+
-*) COMPREPLY=($(compgen -W "-t --tail -n --lines -f --filter -h --help" -- "$cur")) ;;
|
|
75
|
+
*) COMPREPLY=($(compgen -W "$(_spm_services)" -- "$cur")) ;;
|
|
76
|
+
esac
|
|
77
|
+
return
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# Check for stop/kill options
|
|
81
|
+
if [[ " ${words[@]} " =~ " stop " ]] || [[ " ${words[@]} " =~ " kill " ]]; then
|
|
82
|
+
case $cur in
|
|
83
|
+
-*) COMPREPLY=($(compgen -W "-s --signal -h --help" -- "$cur")) ;;
|
|
84
|
+
*) COMPREPLY=($(compgen -W "$(_spm_services)" -- "$cur")) ;;
|
|
85
|
+
esac
|
|
86
|
+
return
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
# Default: complete subcommands or global options
|
|
90
|
+
COMPREPLY=($(compgen -W "$subcommands -c --config -v --verbose -h --help" -- "$cur"))
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
complete -F _spm spm
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Interactive service picker for spm. Requires fzf and jq.
|
|
3
|
+
# Usage: source this file, then run spm_app_selector start|restart|logs|stop|flush
|
|
4
|
+
|
|
5
|
+
spm_app_selector() {
|
|
6
|
+
local command=""
|
|
7
|
+
local config=""
|
|
8
|
+
local app_name=""
|
|
9
|
+
|
|
10
|
+
while [[ $# -gt 0 ]]; do
|
|
11
|
+
case $1 in
|
|
12
|
+
--config=*)
|
|
13
|
+
config="${1#*=}"
|
|
14
|
+
shift
|
|
15
|
+
;;
|
|
16
|
+
--appName=*)
|
|
17
|
+
app_name="${1#*=}"
|
|
18
|
+
shift
|
|
19
|
+
;;
|
|
20
|
+
-h|--help)
|
|
21
|
+
echo "Usage: spm_app_selector [OPTIONS] COMMAND"
|
|
22
|
+
echo ""
|
|
23
|
+
echo "Options:"
|
|
24
|
+
echo " --appName=APP_NAME Specify the application name directly."
|
|
25
|
+
echo " --config=CONFIG_FILE Specify the ecosystem configuration file."
|
|
26
|
+
echo " -h, --help Display this help message."
|
|
27
|
+
echo ""
|
|
28
|
+
echo "Examples:"
|
|
29
|
+
echo " spm_app_selector start"
|
|
30
|
+
echo " spm_app_selector --appName=api restart"
|
|
31
|
+
echo " spm_app_selector --config=ecosystem.config.js logs"
|
|
32
|
+
return 0
|
|
33
|
+
;;
|
|
34
|
+
start|stop|kill|restart|logs|flush)
|
|
35
|
+
command="$1"
|
|
36
|
+
shift
|
|
37
|
+
break
|
|
38
|
+
;;
|
|
39
|
+
*)
|
|
40
|
+
echo "Unknown option or command: $1"
|
|
41
|
+
return 1
|
|
42
|
+
;;
|
|
43
|
+
esac
|
|
44
|
+
done
|
|
45
|
+
|
|
46
|
+
if [[ -z "$command" ]]; then
|
|
47
|
+
echo "Usage: spm_app_selector [OPTIONS] COMMAND"
|
|
48
|
+
echo "COMMAND must be one of: start stop kill restart logs flush"
|
|
49
|
+
return 1
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
if [[ -z "$config" ]]; then
|
|
53
|
+
if [[ -f ecosystem.config.js ]]; then
|
|
54
|
+
config="ecosystem.config.js"
|
|
55
|
+
elif [[ -f ecosystem.config.cjs ]]; then
|
|
56
|
+
config="ecosystem.config.cjs"
|
|
57
|
+
elif [[ -f ecosystem.custom.config.js ]]; then
|
|
58
|
+
config="ecosystem.custom.config.js"
|
|
59
|
+
fi
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
if [[ -z "$config" ]]; then
|
|
63
|
+
echo "No ecosystem configuration file found."
|
|
64
|
+
return 1
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
if [[ -z "$app_name" ]]; then
|
|
68
|
+
app_name=$(spm ${config:+--config "$config"} jlist 2>/dev/null | jq -r '.[].name' | fzf)
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
if [[ -n "$app_name" ]]; then
|
|
72
|
+
if [[ -n "$config" ]]; then
|
|
73
|
+
spm --config "$config" "$command" "$app_name"
|
|
74
|
+
else
|
|
75
|
+
spm "$command" "$app_name"
|
|
76
|
+
fi
|
|
77
|
+
else
|
|
78
|
+
echo "No application selected."
|
|
79
|
+
return 1
|
|
80
|
+
fi
|
|
81
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
function spm_app_selector
|
|
2
|
+
argparse 'h/help' 'appName=' 'config=' -- $argv
|
|
3
|
+
or return
|
|
4
|
+
|
|
5
|
+
if set -q _flag_help
|
|
6
|
+
echo "Usage: spm_app_selector [OPTIONS] -- [COMMAND]"
|
|
7
|
+
echo ""
|
|
8
|
+
echo "Options:"
|
|
9
|
+
echo " --appName=APP_NAME Specify the application name directly."
|
|
10
|
+
echo " --config=CONFIG_FILE Specify the ecosystem configuration file (ecosystem.config.js or ecosystem.custom.config.js)."
|
|
11
|
+
echo " -h, --help Display this help message and exit."
|
|
12
|
+
echo ""
|
|
13
|
+
echo "Examples:"
|
|
14
|
+
echo " spm_app_selector start"
|
|
15
|
+
echo " spm_app_selector --appName=api restart"
|
|
16
|
+
echo " spm_app_selector --config=ecosystem.config.js logs"
|
|
17
|
+
return
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
if not set -q _flag_config
|
|
21
|
+
if test -f ecosystem.config.js
|
|
22
|
+
set _flag_config ecosystem.config.js
|
|
23
|
+
else if test -f ecosystem.config.cjs
|
|
24
|
+
set _flag_config ecosystem.config.cjs
|
|
25
|
+
else if test -f ecosystem.custom.config.js
|
|
26
|
+
set _flag_config ecosystem.custom.config.js
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
if not set -q _flag_config
|
|
31
|
+
echo "No ecosystem configuration file found."
|
|
32
|
+
return
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
set -l command $argv[1]
|
|
36
|
+
if test -z "$command"
|
|
37
|
+
echo "Usage: spm_app_selector [OPTIONS] COMMAND"
|
|
38
|
+
echo "COMMAND must be one of: start stop kill restart logs flush"
|
|
39
|
+
return 1
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
if set -q _flag_appName
|
|
43
|
+
set APP_NAME $_flag_appName
|
|
44
|
+
else
|
|
45
|
+
set spm_args
|
|
46
|
+
if set -q _flag_config
|
|
47
|
+
set -a spm_args --config $_flag_config
|
|
48
|
+
end
|
|
49
|
+
set APP_NAME (spm $spm_args jlist 2>/dev/null | jq -r '.[].name' | fzf)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
if test -n "$APP_NAME"
|
|
53
|
+
set spm_args
|
|
54
|
+
if set -q _flag_config
|
|
55
|
+
set -a spm_args --config $_flag_config
|
|
56
|
+
end
|
|
57
|
+
spm $spm_args $command $APP_NAME
|
|
58
|
+
else
|
|
59
|
+
echo "No application selected."
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Disable file completions globally for spm
|
|
2
|
+
complete -c spm -f
|
|
3
|
+
|
|
4
|
+
# Helper function to check if spm has not received a subcommand
|
|
5
|
+
function __fish_spm_no_subcommand --description "Check if spm has not received a subcommand"
|
|
6
|
+
for i in (commandline -opc)
|
|
7
|
+
if contains -- $i start stop kill logs list jlist restart flush rotate
|
|
8
|
+
return 1
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
return 0
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Dynamic service names completion using spm jlist
|
|
15
|
+
function __fish_spm_process_names --description "Fetch list of spm services"
|
|
16
|
+
spm jlist 2>/dev/null | jq -r '.[].name'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Root-level subcommands with descriptions
|
|
20
|
+
complete -c spm -n '__fish_spm_no_subcommand' -a "start" -d "Start service instance(s)"
|
|
21
|
+
complete -c spm -n '__fish_spm_no_subcommand' -a "stop" -d "Stop service instance(s)"
|
|
22
|
+
complete -c spm -n '__fish_spm_no_subcommand' -a "kill" -d "Alias for 'stop'"
|
|
23
|
+
complete -c spm -n '__fish_spm_no_subcommand' -a "logs" -d "Display logs for service instance(s)"
|
|
24
|
+
complete -c spm -n '__fish_spm_no_subcommand' -a "list" -d "List services with running PIDs"
|
|
25
|
+
complete -c spm -n '__fish_spm_no_subcommand' -a "jlist" -d "List services in JSON format"
|
|
26
|
+
complete -c spm -n '__fish_spm_no_subcommand' -a "restart" -d "Restart service instance(s)"
|
|
27
|
+
complete -c spm -n '__fish_spm_no_subcommand' -a "flush" -d "Clear log file contents"
|
|
28
|
+
complete -c spm -n '__fish_spm_no_subcommand' -a "rotate" -d "Manage log rotation"
|
|
29
|
+
|
|
30
|
+
# Dynamic service name completion for commands that take a service argument
|
|
31
|
+
for cmd in start stop restart logs flush kill
|
|
32
|
+
complete -c spm -n "__fish_seen_subcommand_from $cmd" -a "(__fish_spm_process_names)" -d "Service names"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Subcommand-specific completions for the logs command
|
|
36
|
+
complete -c spm -n '__fish_seen_subcommand_from logs' -a '--tail' -d "Tail log files in real time"
|
|
37
|
+
complete -c spm -n '__fish_seen_subcommand_from logs' -a '-n --lines' -d "Number of lines to show"
|
|
38
|
+
complete -c spm -n '__fish_seen_subcommand_from logs' -a '-f --filter' -d "Filter logs by pattern"
|
|
39
|
+
complete -c spm -n '__fish_seen_subcommand_from logs' -a '-h --help' -d "Show help"
|
|
40
|
+
|
|
41
|
+
# Completions for the rotate subcommand group
|
|
42
|
+
complete -c spm -n '__fish_seen_subcommand_from rotate' -a "start" -d "Perform log rotation and spawn rotate-watch process"
|
|
43
|
+
complete -c spm -n '__fish_seen_subcommand_from rotate' -a "watch" -d "Continuously watch and rotate logs"
|
|
44
|
+
complete -c spm -n '__fish_seen_subcommand_from rotate' -a "stop" -d "Stop the rotate-watch process"
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#compdef spm
|
|
2
|
+
# Zsh completion for spm
|
|
3
|
+
|
|
4
|
+
_spm_services() {
|
|
5
|
+
spm jlist 2>/dev/null | jq -r '.[].name'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
_spm() {
|
|
9
|
+
local -a subcommands rotate_subcommands
|
|
10
|
+
subcommands=(start:Start\ service\ instance\(s\) stop:Stop\ service\ instance\(s\) kill:Alias\ for\ stop logs:Display\ logs list:List\ services\ with\ running\ PIDs jlist:List\ services\ in\ JSON\ format restart:Restart\ service\ instance\(s\) flush:Clear\ log\ file\ contents rotate:Manage\ log\ rotation)
|
|
11
|
+
rotate_subcommands=(start:Perform\ log\ rotation\ and\ spawn\ rotate-watch\ process watch:Continuously\ watch\ and\ rotate\ logs stop:Stop\ the\ rotate-watch\ process)
|
|
12
|
+
|
|
13
|
+
local context state line
|
|
14
|
+
typeset -A opt_args
|
|
15
|
+
|
|
16
|
+
_arguments -C \
|
|
17
|
+
'(-c --config)'{-c,--config}'[Specify ecosystem config file]:config file:_files' \
|
|
18
|
+
'(-v --verbose)'{-v,--verbose}'[Enable verbose output]' \
|
|
19
|
+
'(-h --help)'{-h,--help}'[Show help]' \
|
|
20
|
+
'1:subcommand:->subcommand' \
|
|
21
|
+
'*::args:->args'
|
|
22
|
+
|
|
23
|
+
case $state in
|
|
24
|
+
subcommand)
|
|
25
|
+
_describe 'subcommand' subcommands
|
|
26
|
+
;;
|
|
27
|
+
args)
|
|
28
|
+
case $words[2] in
|
|
29
|
+
start|restart|flush)
|
|
30
|
+
_values 'service' $(_spm_services)
|
|
31
|
+
;;
|
|
32
|
+
logs)
|
|
33
|
+
_arguments \
|
|
34
|
+
'(-t --tail)'{-t,--tail}'[Tail log files in real time]' \
|
|
35
|
+
'(-n --lines)'{-n,--lines}'[Number of lines to show]:lines' \
|
|
36
|
+
'(-f --filter)'{-f,--filter}'[Filter logs by pattern]:pattern' \
|
|
37
|
+
'(-h --help)'{-h,--help}'[Show help]' \
|
|
38
|
+
'::service:($(_spm_services))'
|
|
39
|
+
;;
|
|
40
|
+
stop|kill)
|
|
41
|
+
_arguments \
|
|
42
|
+
'(-s --signal)'{-s,--signal}'[Signal to send]:signal:(SIGTERM SIGINT SIGKILL)' \
|
|
43
|
+
'(-h --help)'{-h,--help}'[Show help]' \
|
|
44
|
+
'::service:($(_spm_services))'
|
|
45
|
+
;;
|
|
46
|
+
rotate)
|
|
47
|
+
_describe 'rotate subcommand' rotate_subcommands
|
|
48
|
+
;;
|
|
49
|
+
*)
|
|
50
|
+
_normal
|
|
51
|
+
;;
|
|
52
|
+
esac
|
|
53
|
+
;;
|
|
54
|
+
esac
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
_spm "$@"
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Interactive service picker for spm. Requires fzf and jq.
|
|
2
|
+
# Usage: source this file, then run spm_app_selector start|restart|logs|stop|flush
|
|
3
|
+
|
|
4
|
+
spm_app_selector() {
|
|
5
|
+
local command=""
|
|
6
|
+
local config=""
|
|
7
|
+
local app_name=""
|
|
8
|
+
|
|
9
|
+
while [[ $# -gt 0 ]]; do
|
|
10
|
+
case $1 in
|
|
11
|
+
--config=*)
|
|
12
|
+
config="${1#*=}"
|
|
13
|
+
shift
|
|
14
|
+
;;
|
|
15
|
+
--appName=*)
|
|
16
|
+
app_name="${1#*=}"
|
|
17
|
+
shift
|
|
18
|
+
;;
|
|
19
|
+
-h|--help)
|
|
20
|
+
echo "Usage: spm_app_selector [OPTIONS] COMMAND"
|
|
21
|
+
echo ""
|
|
22
|
+
echo "Options:"
|
|
23
|
+
echo " --appName=APP_NAME Specify the application name directly."
|
|
24
|
+
echo " --config=CONFIG_FILE Specify the ecosystem configuration file."
|
|
25
|
+
echo " -h, --help Display this help message."
|
|
26
|
+
echo ""
|
|
27
|
+
echo "Examples:"
|
|
28
|
+
echo " spm_app_selector start"
|
|
29
|
+
echo " spm_app_selector --appName=api restart"
|
|
30
|
+
echo " spm_app_selector --config=ecosystem.config.js logs"
|
|
31
|
+
return 0
|
|
32
|
+
;;
|
|
33
|
+
start|stop|kill|restart|logs|flush)
|
|
34
|
+
command="$1"
|
|
35
|
+
shift
|
|
36
|
+
break
|
|
37
|
+
;;
|
|
38
|
+
*)
|
|
39
|
+
echo "Unknown option or command: $1"
|
|
40
|
+
return 1
|
|
41
|
+
;;
|
|
42
|
+
esac
|
|
43
|
+
done
|
|
44
|
+
|
|
45
|
+
if [[ -z "$command" ]]; then
|
|
46
|
+
echo "Usage: spm_app_selector [OPTIONS] COMMAND"
|
|
47
|
+
echo "COMMAND must be one of: start stop kill restart logs flush"
|
|
48
|
+
return 1
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
if [[ -z "$config" ]]; then
|
|
52
|
+
if [[ -f ecosystem.config.js ]]; then
|
|
53
|
+
config="ecosystem.config.js"
|
|
54
|
+
elif [[ -f ecosystem.config.cjs ]]; then
|
|
55
|
+
config="ecosystem.config.cjs"
|
|
56
|
+
elif [[ -f ecosystem.custom.config.js ]]; then
|
|
57
|
+
config="ecosystem.custom.config.js"
|
|
58
|
+
fi
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
if [[ -z "$config" ]]; then
|
|
62
|
+
echo "No ecosystem configuration file found."
|
|
63
|
+
return 1
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
if [[ -z "$app_name" ]]; then
|
|
67
|
+
app_name=$(spm ${config:+--config "$config"} jlist 2>/dev/null | jq -r '.[].name' | fzf)
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
if [[ -n "$app_name" ]]; then
|
|
71
|
+
if [[ -n "$config" ]]; then
|
|
72
|
+
spm --config "$config" "$command" "$app_name"
|
|
73
|
+
else
|
|
74
|
+
spm "$command" "$app_name"
|
|
75
|
+
fi
|
|
76
|
+
else
|
|
77
|
+
echo "No application selected."
|
|
78
|
+
return 1
|
|
79
|
+
fi
|
|
80
|
+
}
|