Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements if conditions for pane and window #942

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

soraxas
Copy link

@soraxas soraxas commented Jul 21, 2024

Closes #741

This implements both shell and python conditions.

  • if can be a:

    • string or, (short-hand for shell key)
    • dict that contains
      • python or
      • shell key.
  • A python key will be evaluated as a python expression, and will test for its pythonic truthfulness

  • A shell key will test as a shell variable and any of the following will be evaluated to true: ("y", "yes", "1", "on", "true", "t") (case-insensitivity)

session_name: if conditions test
environment:
  Foo: 'false'
  show_htop: 'true'
windows:
  # the following would not shows up as it evaluates to false
  - window_name: window 1 ${ha} $Foo
    if:
      shell: ${Foo}
    panes:
      - shell_command:
          - echo "this shouldn't shows up"
      - echo neither should this $Foo
  - window_name: window 2
    panes:
      # should not shows up
      - if:
          python: 1+1==3
        shell_command:
          - echo the above is a false statement
      # no if conditions
      - shell_command:
          - echo no condition
          - python -m http.server
      # display by default, but can be disabled by running `show_htop=false tmuxp load .....`
      - if: ${show_htop}
        shell_command:
          - echo the above is a true statement (by default), but can be disabled on-demand
          - htop

In the example, by default, show_htop=true, but since it's a shell variable, it can be override by user. Hence, use can on-deamnd customise their pane/window configuration by

tmuxp load examples/if-conditions.yaml

and

show_htop=false tmuxp load examples/if-conditions.yaml

which will have different behaviour, for different use-cases

@CLAassistant
Copy link

CLAassistant commented Jul 21, 2024

CLA assistant check
All committers have signed the CLA.

@tony tony self-requested a review July 21, 2024 18:38
examples/if-conditions.json Outdated Show resolved Hide resolved
examples/if-conditions.yaml Outdated Show resolved Hide resolved
examples/if-conditions.yaml Outdated Show resolved Hide resolved
Copy link
Member

@tony tony left a comment

Choose a reason for hiding this comment

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

Nice documentation, example, well annotated!

Allowed CI to run.

  • Rebase to get the latest updates

  • Adding a test would make it 5-stars (example: EXAMPLE_PATH w/ blank-panes.yaml in test_builder.py::test_blank_pane_spawn)

  • ruff check has some complaints (job)

    You may get automated fixes from something like ruff check --select ALL . --fix --unsafe-fixes --preview --show-fixes --ignore T201 --ignore F401 --ignore PT014 --ignore RUF100; ruff format ..

    (While I actually prefer your styling better than what ruff would do above, I use ruff to enforce consistency at scale)

@soraxas soraxas force-pushed the feat-if-conditions branch from 1b2fff5 to 5b27974 Compare July 22, 2024 12:18
@soraxas
Copy link
Author

soraxas commented Jul 22, 2024

I've expanded the approach, where shell now refers to actual shell expressions, for expressions like:

[ 5 -gt 1 ]

or

echo ${MY_ENV} | grep foo

The shell_var now refers to just testing for variables.

python now supports statements (using eval only supports expression, so 1+2>=3 works but not import sys; 1+2>=3). Instead, We can use exec and extract the results (from the last expression) afterwards.

Signed-off-by: Tin Lai <[email protected]>
@soraxas soraxas requested a review from tony July 25, 2024 03:05
Copy link
Member

@tony tony left a comment

Choose a reason for hiding this comment

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

Rewinding a bit, it this safe?

Can you think of any other software projects that have declarative configurations directly execute code? 1

I suppose:

  • CI: Jenkins, CircleCI, GitHub actions
  • Container software: Docker (perhaps the CI systems could be considered a subset of that)
  • Configuration managers: Ansible, Salt
  • Shell script: Run commands like bashrc, zshrc
  • Vim: Vim configuration

Add second qualifier on top of that: when the code is also executed in an if expression.

Is eval() or subprocess.run(..., shell=True) too permissive for an if?

I can't tell yet. I would like to see more examples from other projects to see their best practices.

Alternative approach?

Could you make a second PR with a more limited if that avoid eval / subprocess passthroughs?

operator expressions

One way, it could have something wrapping operator:

if:
- a: ${SHELL}
  op: eq
  b: 'bash'

Where a and b could be replaced by envionmental variables via os.path.expandvars() and op could accept a method of operator.

Separate script

To make the conditional more explicit, they can be separate shell scripts and run though a subprocess.Popen, similar to run_before_script(), but they're explicit commands and piped through libtmux.

Anything else?

Anything that could make if flexible, yet constrained?

Footnotes

  1. The shell_commands in tmuxp configurations are typed into a pty / tmux pane, to be fair.

@soraxas
Copy link
Author

soraxas commented Aug 9, 2024

Yes I also partially agree that the use of eval and supprocess tends to be discourage (especially when we are executing unsanitised user input); therefore I did put comments on stating the potential danger within this approach in previous revision.

However, the more I think about it, the more I reckon that the nature of tmuxp or other similar tools like tmuxinator might render these concerns a bit pointless. The fact is, tmuxp will already be executing arbitrary commands based on the given list of shell_command.

From my point of view,

  1. The use of subprocess to evaluate shell output shouldn't be of concern, as it's not too different than what tmuxp is designed to do (execute arbitrary commands to setup workspace).
  2. My only potential concern is using eval / exec for python statements, for the fact that they are executing inside the python interpreter, and they might change the internal state inside tmuxp (e.g. they can modify some variables within tmuxp).
    • Perhaps this concern can be eliminated by evaluating the python statement inside a subprocess as well (to isolate the effect)? We can probably re-export the environment variable to the subprocess Python to achieve the same effect.

RE:

Can you think of any other software projects that have declarative configurations directly execute code? 1

Aside from the examples that you have given (many of which just execute any commands given without any security measure), my experience with direnv is that it requires user to explicitly opt-in when encountering a new .envrc file.

  • However, the main difference is that direnv auto-load files when cd-ing, so it provides user a chance to manually review the file before auto-loading (it hashes the file to monitor changes to .envrc file), so even if they had just cloned some repository online direnv would prompt user before auto-loading it.
  • In tmuxp's case, users manually issues a load command, so they should be in-charge of reviewing the file (if it comes from internet; the same pretty goes for any scripts that comes from the internet)?

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.

Feature request: if: to optionally open windows / panes
3 participants