devbox icon indicating copy to clipboard operation
devbox copied to clipboard

feat: exit_hook

Open pythoninthegrass opened this issue 1 year ago • 4 comments

What problem are you trying to solve?

init_hooks are cool. I'd like exit_hooks that do the opposite. For instance, I'm always starting up an ephemeral redis container for ansible facts that only lasts during my project work.

You already implemented process-compose for redis (and other services!), which precludes a docker step. Now turning it off automatically would complete the loop.

What solution would you like?

With exit_hooks, in addition to returning to the original shell, it would have some kind of de-scaffolding.

If we keep it general, exit_hooks could act like bash traps or python's try/except/finally blocks.

Essentially, the workflow would resemble:

{
  "packages": [
    "redis@latest"
  ],
  "env": {
    "REDIS_PORT":      "6379",
    "REDIS_CONF":      "./devbox.d/redis/redis.conf"
  },
  "shell": {
    "init_hook": [
      "devbox services start redis"
    ],
    "exit_hook": [
      "devbox services stop redis"
    ],
    "scripts": {}
  }
}

Alternatives you've considered

Didn't find anything in my cursory search.

However, it wouldn't take a lot of effort to write a shell script or python script to do the same thing. But that defeats the utility of devbox and requires bespoke solutions.

pythoninthegrass avatar Oct 17 '24 02:10 pythoninthegrass

Would also solve (or at least mitigate) #2354.

Novel use of a script though.

pythoninthegrass avatar Oct 17 '24 02:10 pythoninthegrass

Besides stopping running services (which imo should happen when the shell dies anyway), is there an actual use case for exit_hooks?

As a workaround you could use your mentioned traps in the init_hooks like the following:

{
    "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.13.5/.schema/devbox.schema.json",
    "packages": [],
    "shell": {
      "init_hook": [
        "echo 'Welcome to devbox!'",
        "trap 'echo Goodbye from devbox!' EXIT"
      ],
    }
}

This will result in the following: image

truearken avatar Oct 17 '24 20:10 truearken

Appreciate the response @DerArkeN !

This works

"shell": {
    "init_hook": [
      ". $VENV_DIR/bin/activate",
      "uv pip install -r requirements.txt",
      "[ $(uname -s ) = 'Darwin' ] && export ANSIBLE_BECOME_PASS=$(askpass --sudo)",
      "devbox services start redis --quiet",
      "trap 'devbox services stop redis --quiet' EXIT"
    ],

but it's kludgey. (Side note: had no idea traps worked outside of shell scripts! Unless ofc this is converted into one haha)

I can work around it with a taskfile

version: "3.0"

set: ['e', 'u', 'pipefail']
shopt: ['globstar']

env:
  REDIS_PORT: "6379"
  REDIS_CONF: "./devbox.d/redis/redis.conf"

tasks:
  start:
    desc: "Start Redis server"
    cmds:
      - devbox services start redis --quiet
    silent: true

  restart:
    desc: "Restart Redis server"
    cmds:
      - devbox services restart redis --quiet
    silent: true

  stop:
    desc: "Stop Redis server"
    cmds:
      - devbox services stop redis --quiet
    silent: true

then

"shell": {
    "init_hook": [
      ". $VENV_DIR/bin/activate",
      "uv pip install -r requirements.txt",
      "[ $(uname -s ) = 'Darwin' ] && export ANSIBLE_BECOME_PASS=$(askpass --sudo)",
      "task redis:start",
      "trap 'task redis:stop' EXIT"
    ],

but a separation of concerns would be a more streamlined approach.

Services do not die with the shell fwiw. I agree that if they did, exit hooks wouldn't be as applicable to process-compose managed services. For everything else, still think exit hooks would be handy.

pythoninthegrass avatar Oct 19 '24 18:10 pythoninthegrass

@pythoninthegrass glad I could help, looks good! Didn't know about the traps either, saw it once in a nix discussion before😄

@Lagoja what's the expected behaviour for running services when killing a shell? is it planned to implement stopping services when killing a shell?

truearken avatar Oct 19 '24 19:10 truearken

@DerArkeN the behavior depends on how the services are started. Services run in their own shell, so they are not necessarily affected by the devbox shell used to start them

  1. If the services are started in the background with devbox services up -b, then process-compose and the services will continue running until stopped with devbox services stop.

  2. If the services are started in the foreground with devbox services up, then terminating the parent shell (and the process-compose UI) should also terminate the underlying services

You could add devbox service stop to your shell trap to ensure that they are terminated?

Lagoja avatar Oct 28 '24 21:10 Lagoja