I'm new to Linux and Bash and wrote this script to build and publish my Bevy game to GitHub Pages. It detects Ubuntu, Arch, or CachyOS, updates the system, installs native dependencies, updates and configures Rust nightly with the wasm32-unknown-unknown target, installs wasm-bindgen-cli when needed, builds the project in release mode, and assembles the final dist/ directory with the web files, assets, and generated WebAssembly bindings. The script works currently, but I'm worried about quoting, portability, error handling, and unusual edge cases. What should I improve, and are there any practices I should follow before using it in an automated workflow?
4 Answers
This is a perfectly reasonable first Bash script, and the fact that you are thinking about edge cases is a good sign. Don’t stop writing it just because it is verbose right now. Improve it incrementally: use ShellCheck, quote expansions, simplify the nested conditionals, and test failures such as missing files, a failed package install, or an existing dist directory.
Quote your variable expansions consistently, especially in the file-copy and directory commands. Unquoted values can break if a path ever contains whitespace or wildcard characters. Prefer forms such as mkdir -p -- "$DIST_FOLDER" and cp -a "$WEB_FOLDER" "$DIST_FOLDER". Also consider sending diagnostics and error messages to stderr instead of mixing them into normal output. Run the finished script through ShellCheck; it catches many quoting, portability, and Bash-specific mistakes and explains why they matter.
A lot of the script is devoted to printing notifications, which makes the actual build logic harder to see. Functions for success, informational, and error messages would reduce repetition. You should also review the package-manager commands carefully for each distribution and avoid doing a full system upgrade inside a publishing script unless that is intentional. Keep the workflow environment setup as predictable and limited as possible.
The biggest readability issue is the deeply nested error handling. Instead of putting every later command inside another if/else block, you can run each command sequentially and handle failure immediately, for example: command || { echo "operation failed" >&2; exit 1; }. That keeps the main flow much easier to follow. You could also put repeated status messages into functions so the build stages stand out more clearly.
That makes sense. Flattening the stages and moving the repeated notifications into functions should make the script much easier to maintain.

ShellCheck is especially useful while learning Bash because it points out problems that may not show up during a simple successful run.