devbox
devbox copied to clipboard
feat: exit_hook
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.
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:
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 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?
@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
-
If the services are started in the background with
devbox services up -b, then process-compose and the services will continue running until stopped withdevbox services stop. -
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?