Skip to content

Introduce "partials" to support config inheritance - #987

Closed
ethagnawl wants to merge 13 commits into
tmuxinator:masterfrom
ethagnawl:feature/project-partials
Closed

ethagnawl wants to merge 13 commits into
tmuxinator:masterfrom
ethagnawl:feature/project-partials

Conversation

@ethagnawl

@ethagnawl ethagnawl commented May 15, 2026

Copy link
Copy Markdown
Member

Metadata

This is a first attempt at addressing the "project config inheritance" feature requested in #979

Problem / Motivation

This change set introduces a new top-level config property called "partials". It allows the user to specify a list of partial or base (I'm not satisfied with either name yet) config files that their project config will extend.

This is still WIP but the current strategy collapses all top-level config options and the last specified in the list wins. As far as windows go, the current iteration accrues them as it processes the partials and eventually the project config and doesn't attempt to do anything clever about merging windows with the same name, deduping commands, etc. There are a few ways this could be made better but this initial pass is simple and unsurprising. We could potentially make the window treatment configurable but I think that's best left for a future PR.

Errors resulting from the parsing and merging of partials bubble up to the exsting #load exception handler and are handled in the same way: tmuxinator exits with an error message. We could handle the errors and continue with just the project but, again, I think simple and obvious is the preferred approach.

Solution

In practice, the feature looks like:

# foo.yaml
partials:
  - "/home/peter/.config/tmuxinator/partial-1.yaml"
  - "/home/peter/.config/tmuxinator/partial-2.yaml"
name: foo
attach: true
windows:
  - editor:
      layout: main-vertical
      panes:
        - editor:
          - vim

# partial-1.yaml
pre:
  - echo "partial is working"
  
# partial-2.yaml 
socket_name: qux
windows:
  - editor:
      layout: main-vertical
      panes:
        - editor:
          - emacs

The resulting tmuxinator commands are as follows:

#!/bin/bash


  # Clear rbenv variables before starting tmux
  unset RBENV_VERSION
  unset RBENV_DIR

  tmux -L qux start-server;

cd .

# Run on_project_start command.



  # Run pre command.
  

  # Run on_project_first_start command.
  

  tmux -L qux new-session -d -s foo -n editor



  # Create windows.
  tmux -L qux new-window  -k -t foo:0 -n editor
  tmux -L qux new-window  -k -t foo:1 -n editor


  # Window "editor"


  tmux -L qux select-pane -t foo:0.0 -T editor
  tmux -L qux send-keys -t foo:0.0 emacs C-m

  tmux -L qux select-layout -t foo:0 tiled

  tmux -L qux select-layout -t foo:0 main-vertical
  tmux -L qux select-pane -t foo:0.0


  # Window "editor"


  tmux -L qux select-pane -t foo:1.0 -T editor
  tmux -L qux send-keys -t foo:1.0 vim C-m

  tmux -L qux select-layout -t foo:1 tiled

  tmux -L qux select-layout -t foo:1 main-vertical
  tmux -L qux select-pane -t foo:1.0


  tmux -L qux select-window -t foo:0
  tmux -L qux select-pane -t foo:0.0

  if [ -z "$TMUX" ]; then
    tmux -L qux -u attach-session -t foo
  else
    tmux -L qux -u switch-client -t foo
  fi



# Run on_project_exit command.

TODO

  • Use project config lookup for partials so full paths aren't required
  • Decide if the current window treatment is satisfactory
  • Test degenerate cases:
    • Malformed partial
    • Missing partial
  • Change "partials" to ... something else?
  • Anything else?

Any thoughts about this feature at either the micro or macro levels, @tmuxinator/tmuxinator?

@ethagnawl
ethagnawl marked this pull request as draft May 15, 2026 15:21

@akofink akofink left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for getting this functional PoC written up!

Similar to my comments on the related issue/feature request, I think this feature is useful but should be weighed against tmuxinator's existing args/options mechanism (mux <project> <arg> <option>=<value>).

Partials and runtime args solve related extensibility problems from different directions: args are good for parameterizing a single project at invocation time, while partials are better for sharing reusable baseline config across projects.

That distinction may justify having both, but it would be worth keeping the merge contract very small and explicit so the feature remains as easy to reason about as these helper methods currently are.

Comment thread lib/tmuxinator/project.rb Outdated
Comment thread lib/tmuxinator/project.rb Outdated
Comment on lines +81 to +83
partial_yamls.merge(project_yaml) do |_key, partial_val, project_val|
if partial_val.is_a?(Array) && project_val.is_a?(Array)
partial_val + project_val

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The array merge applies to every top-level array, not just windows. That changes override semantics for options such as pre, post, pre_window, and project hooks: a project that defines its own pre array will unexpectedly run the partial's pre commands first instead of replacing the partial value. The PR description says top-level options should be last-one-wins while only windows accrue, so this should probably check _key == "windows" before concatenating.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. This is also a great point. I'd forgotten there were other top-level options which could also be arrays. (I wish there was a typed project schema to reference ... ). The current, permissive behavior actually results in some weird edge cases:
Screenshot from 2026-06-01 13-16-04
Screenshot from 2026-06-01 13-14-50

I might open a separate issue to track the above.

Anyways, in those cases where arrays are valid for top-level options, I think it would probably make sense to have these accumulate, too, and only use the last-one-wins behavior in cases where accumulation isn't possible.

@ethagnawl

Copy link
Copy Markdown
Member Author

I'm going to close this for now. I may pick it back up at some point if I find time or if anyone else starts actively asking for this feature.

@ethagnawl ethagnawl closed this Jun 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants