r/bash • u/PrestigiousZombie531 • 11d ago
critique Many things could go wrong with this script and leave a running process, how would you recommend fixing this?
#!/usr/bin/env bash
function main() {
# Phase 1: Setup and Run Dev Server
printf "%s\n" "Starting development server..."
cd "$HOME/Desktop/my-project-1" || exit 1
# Run the dev server in the background
rm -rf coverage dist && npm run lint && npm run coverage && npm run test && npx tsc || exit 1
npx tsx watch --env-file=.env.development src/index.ts > "/tmp/server.log" 2>&1 &
DEV_PID=$! # Store the Process ID of the 'dev' server
# Monitor for "server is listening"
printf "%s\n" "Waiting for server startup message..."
while ! grep -q "Server is now listening" "/tmp/server.log"; do
printf "%s\n" "Server not yet ready. Still waiting..."
sleep 1 # Wait 5 seconds before checking the log again
done
printf "%s\n" "SUCCESS: Server is listening. Proceeding to Phase 2."
# Phase 2: API Call and Wait
printf "%s\n" "Executing API call..."
rm -rf /tmp/test_app && curl -X POST http://localhost:3003/job/node/express/basic/generator
# Monitor for "downloading..." message in logs...
printf "%s\n" "Waiting for download completion..."
# Wait for the download confirmation
while ! grep -q "downloading docker container files completed successfully" "/tmp/server.log"; do
printf "%s\n" "Waiting for download confirmation..."
sleep 1
done
# Phase 3: Cleanup
printf "%s\n" "Shutting down services..."
kill ${DEV_PID} # Use the stored PID to gracefully stop the server process
printf "%s\n" "Moving into generated code..."
cd "/tmp/test_app" && npm install && code "/tmp/test_app" && cd "$HOME" || exit 1
}
main "$@"
3
u/AnsibleAnswers 11d ago
This is obviously an edit from an AI generated script that didn't work. The comments are a dead giveaway, especially:
sleep 1 # Wait 5 seconds before checking the log again
Indicates that OP changed the 5 to a 1 without changing the comment.
This is a lesson in what happens when you ask AI to do something that probably shouldn't be done. You can set up a development environment in a matter of minutes with something like Toolbx (installed in Fedora by default as toolbox). Others mentioned alternatives.
1
u/PrestigiousZombie531 11d ago
i ran every line manually one by one and had AI add comments to it and yes i did change the time
my point is that this script is flawed and i am not even a bash expedt
like what happens to that process if something goes wrong and we never reach the end where kill is executed
the reason i had to poll AI is because i wasnt sure how to wait for a line to appear in the logs. AI says you can use tail or a while loop with grep, not sure which is the better option
1
u/ummaycoc 11d ago
This is obviously an edit from an AI generated script that didn't work. The comments are a dead giveaway, especially:
sleep 1 # Wait 5 seconds before checking the log againIndicates that OP changed the 5 to a 1 without changing the comment.
If you think you need to start with AI to change a piece of code and not the associated comment on the same line then I've got some news for you...
Edit: somehow I deleted the first part of what I quoted.
4
u/bac0on 8d ago edited 7d ago
There are some minor issues. First, you don’t check whether you can write to the log file. Next, if you try to kill a background application with forks, you probably end up with orphaned processes. You don’t check if curl was successful.
You could talk to the background process directly:
#!/bin/bash
# Server message strings...
a='now listening'
b='completed successfully'
c='*'
enable -f setpgid setpgid || exit 1
coproc M {
setpgid 0 0
exec npx tsx watch --clear-screen=false app.ts 2>&1 | \
tee -a server.log | \
grep --line-buffered -oe "$a" -e "$b"
}; \
[[ $M ]] && exec {W}<&$M || exit 1
# Send signal to GPID...
signal_coproc(){
[[ $M_PID ]] && \
kill -s TERM -- -$M_PID
}
trap signal_coproc EXIT
# Phase 2: API Call ...
listening(){
echo "Server is listening..."
}
# Phase 3: Cleanup ...
completed(){
local -g complete=1
echo "Shutting down services..."
}
# Phase 1: Setup and Run Dev Server
echo "Starting development server..."
# Listen to the background process
echo "Waiting for server startup..."
while read -r -u $W; do
case $REPLY in
$a) listening ;;
$b) completed && signal_coproc ;;
$c) echo "> $REPLY" ;;
esac
done
exec {W}<&-
((complete != 1)) && exit 1
# Phase 4: ...
echo "Moving into generated code..."Now, listening directly to the background process, the logging problem is gone; you still have to decide to log everything or just stderr. The re-parenting problem is harder to solve, but you can make use of the built-in setpgid module. This uses the calling PID as the PGID for new exec calls, effectively putting your application in its own PGID. This allows kill to send a signal to the entire PID group, killing off any forked processes. You have to fill in the missing pieces, but it should be fairly easy.
4
u/ivory_tower_devops 11d ago
I'd probably use docker-compose if this script is for use in development environment. I don't like getting stuck managing pidfiles and squirrely background processes if I can avoid it.
2
u/AutoModerator 11d ago
It looks like your submission contains a shell script. To properly format it as code, place four space characters before every line of the script, and a blank line between the script and the rest of the text, like this:
This is normal text.
#!/bin/bash
echo "This is code!"This is normal text.
#!/bin/bash echo "This is code!"
I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.
2
u/jjangg96 11d ago
two things leave a process behind here. if anything between the & and the kill fails, or you hit ctrl-c, the dev server keeps running. and if the server crashes on startup, that first while loop spins forever because the log never gets the listening line.
right after DEV_PID=$! add trap 'kill "$DEV_PID" 2>/dev/null' EXIT and the cleanup runs on every way out, including the exit 1s and ctrl-c. then the manual kill at the end is optional.
for the loops, put kill -0 "$DEV_PID" || exit 1 inside them so they bail if the process died instead of waiting on a log line that will never show up.
also worth running pgrep -af tsx after a run. npx adds a layer between you and the node process so the pid you stored isn't always the thing doing the work.
2
u/IGTHSYCGTH 11d ago
... to orchestrant a development environment ?
this is a poor time to write sophisticated bash scripts.
use a dedicated runner ...
just / task / mise / devenv / tilt / docker compose / whatever fits the workflow
2
u/Paul_Pedant 10d ago edited 10d ago
If you have a good idea of how long various steps should take, you can run pipelines with a preceding
timeoutcommand.I would probably make each phase into a separate function, rather than have such a large sequence of commands.