Skip to content

Commit 697b553

Browse files
committed
feat(builder): allow environments for windows and panes
This fixes #832.
1 parent b15b27b commit 697b553

File tree

7 files changed

+166
-13
lines changed

7 files changed

+166
-13
lines changed

CHANGES

+22-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,28 @@ $ pipx install --suffix=@next 'tmuxp' --pip-args '\--pre' --force
1717

1818
## tmuxp 1.19.x (unreleased)
1919

20-
- Notes on upcoming releases will be added here
20+
### What's new
21+
22+
- #845: allow to configure window and pane specific environment variables
23+
24+
Having a setup like:
25+
```yaml
26+
session_name: env-demo
27+
environment:
28+
DATABASE_URL: "sqlite3:///default.db"
29+
windows:
30+
- window_name: dev
31+
environment:
32+
DATABASE_URL: "sqlite3:///dev-1.db"
33+
panes:
34+
- pane
35+
- environment:
36+
DATABASE_URL: "sqlite3:///dev-2.db"
37+
```
38+
will result in a window with two panes. In the first pane `$DATABASE_URL` is
39+
`sqlite3:///dev-1.db`, while in the second pane it is `sqlite3://dev-2.db`.
40+
Any freshly created window gets `sqlite3:///default.db` as this is what was
41+
defined for the session.
2142

2243
<!-- Maintainers, insert changes / features for the next release here -->
2344

docs/configuration/examples.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,9 @@ please make a ticket on the [issue tracker][issue tracker].
262262

263263
## Environment variables
264264

265-
tmuxp will set session environment variables.
265+
tmuxp will set session, window and pane environment variables. Note that
266+
setting environment variables for windows and panes requires tmuxp 1.19 or
267+
newer.
266268

267269
````{tab} YAML
268270

examples/session-environment.json

+24-7
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,32 @@
22
"environment": {
33
"EDITOR": "/usr/bin/vim",
44
"DJANGO_SETTINGS_MODULE": "my_app.settings.local",
5-
"SERVER_PORT": "8009",
6-
},
5+
"SERVER_PORT": "8009"
6+
},
77
"windows": [
88
{
99
"panes": [
10-
"./manage.py runserver 0.0.0.0:${SERVER_PORT}"
11-
],
10+
"./manage.py runserver 0.0.0.0:${SERVER_PORT}"
11+
],
1212
"window_name": "Django project"
13-
},
14-
],
13+
},
14+
{
15+
"environment": {
16+
"DJANGO_SETTINGS_MODULE": "my_app.settings.local",
17+
"SERVER_PORT": "8010"
18+
},
19+
"panes": [
20+
"./manage.py runserver 0.0.0.0:${SERVER_PORT}",
21+
{
22+
"environment": {
23+
"DJANGO_SETTINGS_MODULE": "my_app.settings.local-testing",
24+
"SERVER_PORT": "8011"
25+
},
26+
"shell_command": "./manage.py runserver 0.0.0.0:${SERVER_PORT}"
27+
}
28+
],
29+
"window_name": "Another Django project"
30+
}
31+
],
1532
"session_name": "Environment variables test"
16-
}
33+
}

examples/session-environment.yaml

+10
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,13 @@ windows:
77
- window_name: Django project
88
panes:
99
- ./manage.py runserver 0.0.0.0:${SERVER_PORT}
10+
- window_name: Another Django project
11+
environment:
12+
DJANGO_SETTINGS_MODULE: my_app.settings.local
13+
SERVER_PORT: "8010"
14+
panes:
15+
- ./manage.py runserver 0.0.0.0:${SERVER_PORT}
16+
- environment:
17+
DJANGO_SETTINGS_MODULE: my_app.settings.local-testing
18+
SERVER_PORT: "8011"
19+
shell_command: ./manage.py runserver 0.0.0.0:${SERVER_PORT}

src/tmuxp/workspace/builder.py

+21
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import logging
88
import time
99

10+
from libtmux.common import has_gte_version
1011
from libtmux.exc import TmuxSessionExists
1112
from libtmux.pane import Pane
1213
from libtmux.server import Server
@@ -346,12 +347,22 @@ def iter_create_windows(self, session, append=False):
346347
except (KeyError, IndexError):
347348
pass
348349

350+
if has_gte_version("3.0"):
351+
environment = panes[0].get("environment", wconf.get("environment"))
352+
else:
353+
logging.warning(
354+
"Cannot set environment for new window. "
355+
"You need tmux 3.0 or newer for this."
356+
)
357+
environment = {}
358+
349359
w = session.new_window(
350360
window_name=window_name,
351361
start_directory=sd,
352362
attach=False, # do not move to the new window
353363
window_index=wconf.get("window_index", ""),
354364
window_shell=ws,
365+
environment=environment,
355366
)
356367

357368
if is_first_window_pass: # if first window, use window 1
@@ -418,11 +429,21 @@ def get_pane_shell():
418429
else:
419430
return None
420431

432+
if has_gte_version("3.0"):
433+
environment = pconf.get("environment", wconf.get("environment"))
434+
else:
435+
logging.warning(
436+
"Cannot set environment for new pane. "
437+
"You need tmux 3.0 or newer for this."
438+
)
439+
environment = {}
440+
421441
p = w.split_window(
422442
attach=True,
423443
start_directory=get_pane_start_directory(),
424444
shell=get_pane_shell(),
425445
target=p.id,
446+
environment=environment,
426447
)
427448

428449
assert isinstance(p, Pane)
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,25 @@
11
session_name: test env vars
2-
start_directory: '~'
2+
start_directory: "~"
33
environment:
4-
FOO: BAR
4+
FOO: SESSION
55
PATH: /tmp
66
windows:
7-
- window_name: editor
7+
- window_name: no_overrides
88
panes:
99
- pane
10+
- window_name: window_overrides
11+
environment:
12+
FOO: WINDOW
13+
panes:
14+
- pane
15+
- window_name: pane_overrides
16+
panes:
17+
- environment:
18+
FOO: PANE
19+
- window_name: both_overrides
20+
environment:
21+
FOO: WINDOW
22+
panes:
23+
- pane
24+
- environment:
25+
FOO: PANE

tests/workspace/test_builder.py

+67-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import libtmux
1111
from libtmux.common import has_gte_version, has_lt_version
12+
from libtmux.session import Session
1213
from libtmux.test import retry_until, temp_session
1314
from libtmux.window import Window
1415
from tmuxp import exc
@@ -331,18 +332,83 @@ def f():
331332
assert w.name != "top"
332333

333334

335+
@pytest.mark.skipif(
336+
has_lt_version("3.0"),
337+
reason="needs -e flag for new-window and split-window introduced in tmux 3.0",
338+
)
334339
def test_environment_variables(session):
335340
workspace = ConfigReader._from_file(
336341
test_utils.get_workspace_file("workspace/builder/environment_vars.yaml")
337342
)
338343
workspace = loader.expand(workspace)
339344

345+
builder = WorkspaceBuilder(sconf=workspace)
346+
builder.build(session)
347+
# Give slow shells some time to settle as otherwise tests might fail.
348+
time.sleep(0.3)
349+
350+
assert session.getenv("FOO") == "SESSION"
351+
assert session.getenv("PATH") == "/tmp"
352+
353+
no_overrides_win = session.windows[0]
354+
pane = no_overrides_win.panes[0]
355+
pane.send_keys("echo $FOO")
356+
assert pane.capture_pane()[1] == "SESSION"
357+
358+
window_overrides_win = session.windows[1]
359+
pane = window_overrides_win.panes[0]
360+
pane.send_keys("echo $FOO")
361+
assert pane.capture_pane()[1] == "WINDOW"
362+
363+
pane_overrides_win = session.windows[2]
364+
pane = pane_overrides_win.panes[0]
365+
pane.send_keys("echo $FOO")
366+
assert pane.capture_pane()[1] == "PANE"
367+
368+
both_overrides_win = session.windows[3]
369+
pane = both_overrides_win.panes[0]
370+
pane.send_keys("echo $FOO")
371+
assert pane.capture_pane()[1] == "WINDOW"
372+
pane = both_overrides_win.panes[1]
373+
pane.send_keys("echo $FOO")
374+
assert pane.capture_pane()[1] == "PANE"
375+
376+
377+
@pytest.mark.skipif(
378+
has_gte_version("3.0"),
379+
reason="warnings are not needed for tmux >= 3.0",
380+
)
381+
def test_environment_variables_logs(session: Session, caplog: pytest.LogCaptureFixture):
382+
workspace = ConfigReader._from_file(
383+
test_utils.get_workspace_file("workspace/builder/environment_vars.yaml")
384+
)
385+
workspace = loader.expand(workspace)
386+
340387
builder = WorkspaceBuilder(sconf=workspace)
341388
builder.build(session)
342389

343-
assert session.getenv("FOO") == "BAR"
390+
# environment on sessions should work as this is done using set-environment
391+
# on the session itself
392+
assert session.getenv("FOO") == "SESSION"
344393
assert session.getenv("PATH") == "/tmp"
345394

395+
assert (
396+
sum(
397+
1
398+
for record in caplog.records
399+
if "Cannot set environment for new window." in record.msg
400+
)
401+
== 4
402+
), "Warning on creating windows missing"
403+
assert (
404+
sum(
405+
1
406+
for record in caplog.records
407+
if "Cannot set environment for new pane." in record.msg
408+
)
409+
== 1
410+
), "Warning on creating panes missing"
411+
346412

347413
def test_automatic_rename_option(session):
348414
"""With option automatic-rename: on."""

0 commit comments

Comments
 (0)