mxup 0.2.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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +395 -0
- data/bin/mxup +10 -0
- data/examples/myapp-dev.yml +110 -0
- data/lib/mxup/cli.rb +170 -0
- data/lib/mxup/config.rb +240 -0
- data/lib/mxup/exec_runner.rb +178 -0
- data/lib/mxup/graceful_stop.rb +72 -0
- data/lib/mxup/launcher.rb +135 -0
- data/lib/mxup/layout_manager.rb +145 -0
- data/lib/mxup/pane_resolver.rb +70 -0
- data/lib/mxup/process_probe.rb +27 -0
- data/lib/mxup/reconciler.rb +240 -0
- data/lib/mxup/runner.rb +253 -0
- data/lib/mxup/status_view.rb +149 -0
- data/lib/mxup/tmux.rb +143 -0
- data/lib/mxup/version.rb +5 -0
- data/lib/mxup.rb +43 -0
- metadata +99 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f2f166f4e87656548399fe98f82650123ef4fe98091cef4c3e3cc1d123ea8506
|
|
4
|
+
data.tar.gz: 5aea8148ab5bd0cbee4e3635a0df5a3e0513c8dfc3e110408db674c795552b84
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 10a37a6fad2a6409ceabf94f7bd0c63d36645a7a402d609b4207255753cefeffeee162fefcc0aee8d01777ac7535fba71842b5ff9298a43b48b5cf1c6393f5de
|
|
7
|
+
data.tar.gz: 4463a0a269f5cd7fa1ffecc85e30f05822420afe8796df195ffadc8fdab05191bb03f8bd590d99b1c01f60f1a4a199184535badb2e3ec410fe701bbcc6ce0c08
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vladislav Saifulin
|
|
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.
|
data/README.md
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
# mxup
|
|
2
|
+
|
|
3
|
+
Declarative tmux session manager with reconciliation.
|
|
4
|
+
|
|
5
|
+
Run `mxup up` any time — it creates what's missing, restarts what crashed, removes what's not declared, and leaves healthy processes alone.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
Requires `tmux` and Ruby 3.1+ (stdlib only — no runtime gem dependencies).
|
|
10
|
+
|
|
11
|
+
### RubyGems
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
gem install mxup
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Homebrew
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
brew install Recognized/mxup/mxup
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### From source
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
git clone https://github.com/Recognized/mxup.git ~/src/mxup
|
|
27
|
+
ln -sf ~/src/mxup/bin/mxup ~/.local/bin/mxup # ensure ~/.local/bin is on PATH
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Create a config
|
|
34
|
+
mkdir -p ~/.config/mxup
|
|
35
|
+
cp ~/IdeaProjects/mxup/examples/myapp-dev.yml ~/.config/mxup/
|
|
36
|
+
|
|
37
|
+
# Bring the session up (reconcile)
|
|
38
|
+
mxup up myapp-dev
|
|
39
|
+
|
|
40
|
+
# Check what's running
|
|
41
|
+
mxup status myapp-dev
|
|
42
|
+
|
|
43
|
+
# Restart specific windows
|
|
44
|
+
mxup restart myapp-dev:api
|
|
45
|
+
mxup restart myapp-dev:api,worker
|
|
46
|
+
|
|
47
|
+
# Restart all windows
|
|
48
|
+
mxup restart myapp-dev
|
|
49
|
+
|
|
50
|
+
# Tear everything down
|
|
51
|
+
mxup down myapp-dev
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Config format
|
|
55
|
+
|
|
56
|
+
Configs live in `~/.config/mxup/<name>.yml` or can be passed via `-f path`.
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
session: my-project
|
|
60
|
+
|
|
61
|
+
# Shell snippet run in every window before the command
|
|
62
|
+
setup: |
|
|
63
|
+
direnv allow . 2>/dev/null
|
|
64
|
+
eval "$(direnv export zsh 2>/dev/null)"
|
|
65
|
+
|
|
66
|
+
windows:
|
|
67
|
+
database:
|
|
68
|
+
root: ~/projects/my-app
|
|
69
|
+
command: docker compose up postgres redis
|
|
70
|
+
|
|
71
|
+
backend:
|
|
72
|
+
root: ~/projects/my-app/backend
|
|
73
|
+
wait_for: localhost:5432
|
|
74
|
+
env:
|
|
75
|
+
DATABASE_URL: postgres://localhost/myapp_dev
|
|
76
|
+
command: ./start-server.sh
|
|
77
|
+
|
|
78
|
+
frontend:
|
|
79
|
+
root: ~/projects/my-app/frontend
|
|
80
|
+
command: npm run dev
|
|
81
|
+
|
|
82
|
+
shell:
|
|
83
|
+
root: ~/projects/my-app
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Fields
|
|
87
|
+
|
|
88
|
+
| Field | Required | Description |
|
|
89
|
+
|-------|----------|-------------|
|
|
90
|
+
| `session` | yes | tmux session name |
|
|
91
|
+
| `setup` | no | Shell snippet prepended to every window's command |
|
|
92
|
+
| `windows` | yes | Ordered map of window definitions |
|
|
93
|
+
|
|
94
|
+
Per window:
|
|
95
|
+
|
|
96
|
+
| Field | Required | Description |
|
|
97
|
+
|-------|----------|-------------|
|
|
98
|
+
| `root` | yes | Working directory (supports `~`) |
|
|
99
|
+
| `command` | no | Command to run. Omit for an interactive shell. |
|
|
100
|
+
| `env` | no | Map of environment variables to export |
|
|
101
|
+
| `wait_for` | no | Readiness check to pass before running command (see below) |
|
|
102
|
+
|
|
103
|
+
### Wait-for checks
|
|
104
|
+
|
|
105
|
+
`wait_for` blocks a window's command until a readiness condition is met.
|
|
106
|
+
|
|
107
|
+
**Shorthand** — TCP check (backward compatible):
|
|
108
|
+
|
|
109
|
+
```yaml
|
|
110
|
+
wait_for: localhost:5432
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Expanded form** with explicit check type:
|
|
114
|
+
|
|
115
|
+
```yaml
|
|
116
|
+
# TCP port open
|
|
117
|
+
wait_for:
|
|
118
|
+
tcp: localhost:5432
|
|
119
|
+
|
|
120
|
+
# HTTP 2xx response
|
|
121
|
+
wait_for:
|
|
122
|
+
http: http://localhost:8080/health
|
|
123
|
+
|
|
124
|
+
# File or socket exists
|
|
125
|
+
wait_for:
|
|
126
|
+
path: /tmp/app.sock
|
|
127
|
+
|
|
128
|
+
# Arbitrary script (exit 0 = ready)
|
|
129
|
+
wait_for:
|
|
130
|
+
script: pg_isready -h localhost -p 5432
|
|
131
|
+
label: postgres # shown in wait/ready messages
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
All forms support optional `timeout` (seconds, default: unlimited) and `interval` (seconds between retries, default: 2):
|
|
135
|
+
|
|
136
|
+
```yaml
|
|
137
|
+
wait_for:
|
|
138
|
+
tcp: localhost:5432
|
|
139
|
+
timeout: 60
|
|
140
|
+
interval: 5
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
| Option | Default | Description |
|
|
144
|
+
|--------|---------|-------------|
|
|
145
|
+
| `timeout` | unlimited | Max seconds to wait before giving up |
|
|
146
|
+
| `interval` | 2 | Seconds between retry attempts |
|
|
147
|
+
| `label` | target value | Display name in wait/ready messages |
|
|
148
|
+
|
|
149
|
+
### Parameterization
|
|
150
|
+
|
|
151
|
+
Use standard shell variable expansion in commands:
|
|
152
|
+
|
|
153
|
+
```yaml
|
|
154
|
+
command: ./run.sh --env=${APP_ENV:-development}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Then override at invocation:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
APP_ENV=production mxup up my-project
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Layouts
|
|
164
|
+
|
|
165
|
+
Define multiple named layouts to control how windows are grouped as tmux panes:
|
|
166
|
+
|
|
167
|
+
```yaml
|
|
168
|
+
layouts:
|
|
169
|
+
full:
|
|
170
|
+
services:
|
|
171
|
+
panes: [database, backend]
|
|
172
|
+
split: even-horizontal
|
|
173
|
+
frontend:
|
|
174
|
+
panes: [frontend]
|
|
175
|
+
|
|
176
|
+
compact:
|
|
177
|
+
all:
|
|
178
|
+
panes: [database, backend, frontend]
|
|
179
|
+
split: tiled
|
|
180
|
+
|
|
181
|
+
flat: {}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Each layout is a map of **group names** to group definitions. Windows in a group share a single tmux window as split panes. Windows not mentioned in any group remain standalone.
|
|
185
|
+
|
|
186
|
+
| Field | Required | Description |
|
|
187
|
+
|-------|----------|-------------|
|
|
188
|
+
| `layouts` | no | Map of named layout definitions |
|
|
189
|
+
|
|
190
|
+
Per group:
|
|
191
|
+
|
|
192
|
+
| Field | Required | Description |
|
|
193
|
+
|-------|----------|-------------|
|
|
194
|
+
| `panes` | yes | List of window names to group as panes |
|
|
195
|
+
| `split` | no | tmux layout: `even-horizontal`, `even-vertical`, `main-horizontal`, `main-vertical`, `tiled` (default: `tiled`) |
|
|
196
|
+
|
|
197
|
+
The first layout is used by default. Override with `--layout`:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
mxup up my-project --layout=compact
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Switch layouts on a running session without killing processes:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
mxup layout my-project compact
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Profiles
|
|
210
|
+
|
|
211
|
+
A single project often needs to run under different stacks — "local
|
|
212
|
+
everything", "staging backend with local frontend", etc. Profiles express
|
|
213
|
+
those variants as a set of overrides on top of a shared base. Only one
|
|
214
|
+
profile of a given config may be live at a time; `mxup up` of a different
|
|
215
|
+
profile automatically tears the current one down first.
|
|
216
|
+
|
|
217
|
+
```yaml
|
|
218
|
+
session: my-project
|
|
219
|
+
|
|
220
|
+
windows:
|
|
221
|
+
backend:
|
|
222
|
+
root: ~/projects/my-app/backend
|
|
223
|
+
command: ./start-server.sh
|
|
224
|
+
env:
|
|
225
|
+
DATABASE_URL: postgres://localhost/myapp_dev
|
|
226
|
+
|
|
227
|
+
frontend:
|
|
228
|
+
root: ~/projects/my-app/frontend
|
|
229
|
+
command: npm run dev
|
|
230
|
+
|
|
231
|
+
profiles:
|
|
232
|
+
local: {} # uses the base as-is
|
|
233
|
+
|
|
234
|
+
staging:
|
|
235
|
+
windows:
|
|
236
|
+
backend:
|
|
237
|
+
command: ./connect-staging.sh
|
|
238
|
+
env:
|
|
239
|
+
DATABASE_URL: postgres://staging-db/myapp
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Pick a profile with `--profile` (short: `-p`):
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
mxup up my-project --profile=local
|
|
246
|
+
mxup up my-project -p staging # tears down 'local' first
|
|
247
|
+
mxup status my-project # shows "profile: staging" in the header
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
| Field | Required | Description |
|
|
251
|
+
|-------|----------|-------------|
|
|
252
|
+
| `profiles` | no | Map of profile name → override block |
|
|
253
|
+
| `default_profile` | no | Profile to use when `--profile` is omitted (defaults to the first declared) |
|
|
254
|
+
|
|
255
|
+
**Override semantics**: the active profile's `setup`, `windows`, and
|
|
256
|
+
`layouts` override the base. Window overrides are merged per-key (so you
|
|
257
|
+
can tweak just `command` or `env` without redeclaring `root`). `env` maps
|
|
258
|
+
are themselves merged — keys not in the profile are inherited from the
|
|
259
|
+
base. A profile may not override `session`; profiles of the same group
|
|
260
|
+
must share one tmux session.
|
|
261
|
+
|
|
262
|
+
**Dropping windows**: to exclude a base window from a profile, map it to
|
|
263
|
+
`~` (YAML null):
|
|
264
|
+
|
|
265
|
+
```yaml
|
|
266
|
+
profiles:
|
|
267
|
+
minimal:
|
|
268
|
+
windows:
|
|
269
|
+
dev-kit: ~ # don't start dev-kit under the `minimal` profile
|
|
270
|
+
scratch: ~
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Any layout groups that reference a dropped window are automatically
|
|
274
|
+
pruned — entries are stripped from `panes:` lists, and a group that ends
|
|
275
|
+
up empty is removed from its layout.
|
|
276
|
+
|
|
277
|
+
**Switching**: if the tmux session is already running under a different
|
|
278
|
+
profile, `mxup up` for a new profile runs `down` first (including the
|
|
279
|
+
graceful-stop dance), then brings the new profile up from a clean slate.
|
|
280
|
+
`mxup status` always shows the currently live profile in its header.
|
|
281
|
+
|
|
282
|
+
## Commands
|
|
283
|
+
|
|
284
|
+
| Command | Description |
|
|
285
|
+
|---------|-------------|
|
|
286
|
+
| `mxup up [name]` | Reconcile session to match config (default when no subcommand) |
|
|
287
|
+
| `mxup status [name]` | Show per-window status with recent output |
|
|
288
|
+
| `mxup down [name]` | Kill the session |
|
|
289
|
+
| `mxup restart [name:]<w1,w2,...>` | Restart specific window(s) (comma-separated) |
|
|
290
|
+
| `mxup restart [name]` | Restart all windows in the session |
|
|
291
|
+
| `mxup layout [name]` | Show available layouts and which is active |
|
|
292
|
+
| `mxup layout [name] <layout>` | Switch to a different layout (preserves running processes) |
|
|
293
|
+
| `mxup target [name:]<window>` | Print the tmux target (`session:window.pane`) for a logical window |
|
|
294
|
+
| `mxup target [name]` | Print targets for every declared window (tab-separated) |
|
|
295
|
+
| `mxup exec -t [name:]<window> "<cmd>"` | Run `<cmd>` in a pane, wait for completion, print output, exit with its status |
|
|
296
|
+
|
|
297
|
+
### Flags
|
|
298
|
+
|
|
299
|
+
| Flag | Description |
|
|
300
|
+
|------|-------------|
|
|
301
|
+
| `-f path` | Use a specific config file |
|
|
302
|
+
| `--dry-run` | Preview changes without applying (for `up`, `restart`, `exec`) |
|
|
303
|
+
| `--lines N` | Output lines to show (for `status` default 15, for `exec` default 50) |
|
|
304
|
+
| `--layout NAME` | Layout to use (for `up`) |
|
|
305
|
+
| `-p`, `--profile NAME` | Profile to use; auto-teardowns a live session running under a different profile (for `up`, `status`, `restart`) |
|
|
306
|
+
| `-t TARGET` | Pane target (for `exec`); accepts `name:window`, `window`, or `window.pane` |
|
|
307
|
+
| `--timeout N` | Max seconds to wait for the command (for `exec`; exit 124 on timeout) |
|
|
308
|
+
| `--force` | Send the command even if the pane is busy with another process (for `exec`) |
|
|
309
|
+
| `-q`, `--quiet` | Don't print captured output (for `exec`) |
|
|
310
|
+
|
|
311
|
+
### Running one-off commands in a pane (`mxup exec`)
|
|
312
|
+
|
|
313
|
+
`mxup exec` is a shortcut for the common "send a command to a tmux pane, wait
|
|
314
|
+
for it to finish, and show the output" loop. It handles the three annoying
|
|
315
|
+
parts for you:
|
|
316
|
+
|
|
317
|
+
1. **Resolving logical names to real tmux targets** — `api` may actually
|
|
318
|
+
live as pane `services.1`; `mxup exec -t myapp-dev:api` figures that
|
|
319
|
+
out via the active layout.
|
|
320
|
+
2. **Waiting for the command to finish** — uses `tmux wait-for` with a unique
|
|
321
|
+
marker under the hood, so `exec` blocks until the command actually exits.
|
|
322
|
+
3. **Capturing output and exit status** — prints the last `--lines N` lines of
|
|
323
|
+
the pane and exits with the command's own exit code.
|
|
324
|
+
|
|
325
|
+
So instead of the verbose recipe:
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
MARKER="fulltest-$(date +%s%N)"
|
|
329
|
+
tmux send-keys -t myapp-dev:scratch \
|
|
330
|
+
"./gradlew test 2>&1 | tail -n 30; echo FULLTEST_EXIT=\$?; tmux wait-for -S $MARKER" Enter \
|
|
331
|
+
&& tmux wait-for $MARKER
|
|
332
|
+
tmux capture-pane -t myapp-dev:scratch -p -S -50
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
you write:
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
mxup exec -t myapp-dev:scratch "./gradlew test 2>&1 | tail -n 30"
|
|
339
|
+
echo "exit: $?"
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
The user command is wrapped in a subshell, so `exit`, `set -e`, or a failing
|
|
343
|
+
command won't kill the pane's interactive shell. By default `exec` refuses to
|
|
344
|
+
send to a pane that's currently running a non-shell process — pass `--force`
|
|
345
|
+
to override. Use `--timeout N` to avoid hanging indefinitely on a runaway
|
|
346
|
+
command (exits 124 on timeout).
|
|
347
|
+
|
|
348
|
+
## Reconciliation
|
|
349
|
+
|
|
350
|
+
`mxup up` compares the declared config against the running tmux session:
|
|
351
|
+
|
|
352
|
+
- **Missing windows** → created and command started
|
|
353
|
+
- **Extra windows** → killed (with warning)
|
|
354
|
+
- **Idle/crashed windows** (shell prompt visible) → command re-sent
|
|
355
|
+
- **Healthy running windows** → left untouched
|
|
356
|
+
- **Layout changed** → panes rearranged without killing processes
|
|
357
|
+
|
|
358
|
+
## Releasing
|
|
359
|
+
|
|
360
|
+
Releases are automated by `.github/workflows/release.yml`. To cut a new version:
|
|
361
|
+
|
|
362
|
+
1. Bump `Mxup::VERSION` in `lib/mxup/version.rb`.
|
|
363
|
+
2. Commit and tag: `git commit -am "Release vX.Y.Z" && git tag vX.Y.Z`.
|
|
364
|
+
3. `git push origin main --tags`.
|
|
365
|
+
|
|
366
|
+
The workflow then runs the test suite, verifies the tag matches
|
|
367
|
+
`Mxup::VERSION`, publishes the gem to RubyGems via [trusted publishing][tp]
|
|
368
|
+
(OIDC — no API keys stored), creates a GitHub release with the built `.gem`
|
|
369
|
+
attached, and — if a Homebrew tap is configured — opens a PR in the tap repo
|
|
370
|
+
bumping the formula.
|
|
371
|
+
|
|
372
|
+
### One-time setup
|
|
373
|
+
|
|
374
|
+
**RubyGems trusted publishing.** Claim `mxup` on rubygems.org, then under
|
|
375
|
+
_Settings → Trusted publishers_ add:
|
|
376
|
+
|
|
377
|
+
- Repository: `Recognized/mxup`
|
|
378
|
+
- Workflow: `release.yml`
|
|
379
|
+
- Environment: _(leave blank)_
|
|
380
|
+
|
|
381
|
+
**Homebrew tap (optional).** Create a `Recognized/homebrew-mxup` repo,
|
|
382
|
+
copy `packaging/homebrew/mxup.rb` to `Formula/mxup.rb` in it, then in the
|
|
383
|
+
`mxup` repo's settings add:
|
|
384
|
+
|
|
385
|
+
- Variable `HOMEBREW_TAP` = `Recognized/homebrew-mxup`
|
|
386
|
+
- Secret `HOMEBREW_TAP_TOKEN` = a PAT with `contents: write` on the tap repo
|
|
387
|
+
|
|
388
|
+
The `homebrew` job in `release.yml` will then run automatically on every
|
|
389
|
+
tag push and keep the formula in sync.
|
|
390
|
+
|
|
391
|
+
[tp]: https://guides.rubygems.org/trusted-publishing/
|
|
392
|
+
|
|
393
|
+
## License
|
|
394
|
+
|
|
395
|
+
MIT
|
data/bin/mxup
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Thin entrypoint. All behaviour lives under lib/mxup/.
|
|
5
|
+
lib = File.expand_path('../lib', __dir__)
|
|
6
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
7
|
+
|
|
8
|
+
require 'mxup'
|
|
9
|
+
|
|
10
|
+
Mxup::CLI.new.run(ARGV) if __FILE__ == $PROGRAM_NAME
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Example: local development stack for a fictional app called "myapp"
|
|
2
|
+
#
|
|
3
|
+
# This is a showcase config — it exercises most mxup features (multiple
|
|
4
|
+
# windows, wait_for probes, per-window env, alternative layouts, and
|
|
5
|
+
# profiles that drop or override windows). Adapt the paths, commands,
|
|
6
|
+
# and env vars to your own project.
|
|
7
|
+
#
|
|
8
|
+
# Place your copy at ~/.config/mxup/myapp-dev.yml, then:
|
|
9
|
+
# mxup up myapp-dev
|
|
10
|
+
# mxup status myapp-dev
|
|
11
|
+
# mxup restart myapp-dev:api
|
|
12
|
+
# mxup down myapp-dev
|
|
13
|
+
#
|
|
14
|
+
# Switch layouts:
|
|
15
|
+
# mxup layout myapp-dev compact
|
|
16
|
+
# mxup up myapp-dev --layout=flat
|
|
17
|
+
#
|
|
18
|
+
# Switch profiles (tears down the current profile first):
|
|
19
|
+
# mxup up myapp-dev --profile=local # default, everything on localhost
|
|
20
|
+
# mxup up myapp-dev --profile=staging # frontend only, talks to hosted API
|
|
21
|
+
#
|
|
22
|
+
# Parameterize via env vars:
|
|
23
|
+
# DOCKER_PROFILES=core mxup up myapp-dev
|
|
24
|
+
|
|
25
|
+
session: myapp-dev
|
|
26
|
+
|
|
27
|
+
setup: |
|
|
28
|
+
# Runs in every window before its command. Replace with whatever your
|
|
29
|
+
# project needs (direnv, nvm, asdf, sourcing a .env, etc.).
|
|
30
|
+
eval "$(direnv export zsh)"
|
|
31
|
+
|
|
32
|
+
windows:
|
|
33
|
+
docker:
|
|
34
|
+
root: ~/code/myapp-infra
|
|
35
|
+
command: docker compose --profile ${DOCKER_PROFILES:-core} up
|
|
36
|
+
|
|
37
|
+
api:
|
|
38
|
+
root: ~/code/myapp/api
|
|
39
|
+
wait_for:
|
|
40
|
+
tcp: localhost:5432
|
|
41
|
+
timeout: 120
|
|
42
|
+
env:
|
|
43
|
+
APP_ENV: development
|
|
44
|
+
DATABASE_URL: postgres://localhost/myapp_dev
|
|
45
|
+
command: ./gradlew run
|
|
46
|
+
|
|
47
|
+
worker:
|
|
48
|
+
root: ~/code/myapp/worker
|
|
49
|
+
wait_for: localhost:6379 # shorthand for a tcp check (redis)
|
|
50
|
+
env:
|
|
51
|
+
APP_ENV: development
|
|
52
|
+
DATABASE_URL: postgres://localhost/myapp_dev
|
|
53
|
+
REDIS_URL: redis://localhost:6379
|
|
54
|
+
command: ./gradlew run
|
|
55
|
+
|
|
56
|
+
scheduler:
|
|
57
|
+
root: ~/code/myapp/scheduler
|
|
58
|
+
wait_for: localhost:5432
|
|
59
|
+
env:
|
|
60
|
+
APP_ENV: development
|
|
61
|
+
DATABASE_URL: postgres://localhost/myapp_dev
|
|
62
|
+
command: ./gradlew run
|
|
63
|
+
|
|
64
|
+
frontend:
|
|
65
|
+
root: ~/code/myapp/frontend
|
|
66
|
+
wait_for:
|
|
67
|
+
http: http://localhost:8080/health
|
|
68
|
+
timeout: 60
|
|
69
|
+
env:
|
|
70
|
+
API_URL: http://localhost:8080
|
|
71
|
+
command: pnpm run dev
|
|
72
|
+
|
|
73
|
+
storybook:
|
|
74
|
+
root: ~/code/myapp/frontend
|
|
75
|
+
command: pnpm run storybook
|
|
76
|
+
|
|
77
|
+
scratch:
|
|
78
|
+
# No `command` -> plain interactive shell, handy for ad-hoc work
|
|
79
|
+
# (e.g. `mxup exec -t myapp-dev:scratch "./gradlew test"`).
|
|
80
|
+
root: ~/code/myapp
|
|
81
|
+
|
|
82
|
+
layouts:
|
|
83
|
+
full:
|
|
84
|
+
services:
|
|
85
|
+
panes: [docker, api, worker, scheduler]
|
|
86
|
+
split: main-vertical
|
|
87
|
+
frontend:
|
|
88
|
+
panes: [frontend, storybook]
|
|
89
|
+
split: even-horizontal
|
|
90
|
+
|
|
91
|
+
compact:
|
|
92
|
+
all:
|
|
93
|
+
panes: [docker, api, worker, scheduler, frontend, storybook]
|
|
94
|
+
split: tiled
|
|
95
|
+
|
|
96
|
+
flat: {}
|
|
97
|
+
|
|
98
|
+
profiles:
|
|
99
|
+
# Everything runs locally via docker + ./gradlew run (the base config).
|
|
100
|
+
local: {}
|
|
101
|
+
|
|
102
|
+
# Staging: skip local backends, point the frontend at the hosted API.
|
|
103
|
+
staging:
|
|
104
|
+
windows:
|
|
105
|
+
api: ~
|
|
106
|
+
worker: ~
|
|
107
|
+
scheduler: ~
|
|
108
|
+
frontend:
|
|
109
|
+
env:
|
|
110
|
+
API_URL: https://api.staging.example.com
|