Skip to Content
ScriptingBest Practices

Best Practices

Always Include an Extension

Miso uses the file extension to select the interpreter. A file without a recognized extension defaults to sh, which may not be what you want:

scripts/migrate.py # runs with python3 scripts/migrate # runs with sh — probably wrong

Always be explicit.

Shebang vs. Extension

Extension-based dispatch is fine for most scripts. Use a shebang when you need a specific binary path or want the script to be portable outside of miso:

#!/usr/bin/env python3 import sys ...

When both are present, the shebang takes precedence over the extension.

Organizing with Subdirectories

Group scripts by domain using subdirectories. Invoke them with a / separator:

scripts/ db/ migrate.sh seed.sh reset.sh ci/ lint.sh test.sh release.sh
miso db/migrate miso ci/release

Nest as deeply as you need. An index file in a subdirectory acts as the default for that directory:

# scripts/db/index.sh runs when you call: miso db

Nest related commands instead of prefixing flat filenames. Prefer scripts/docker/up.sh + scripts/docker/build.sh (invoked as miso docker/up, miso docker/build) over the stuttering flat form scripts/docker-up.sh + scripts/docker-build.sh.

package.json Scripts vs. the scripts/ Folder

Pick the surface based on the command’s size:

  • One-liners → package.json scripts. A single command like vite --config vite.config.ts reads best as a package.json script — miso resolves it just like a folder script. (Exception: simple mode ignores package.json.)
  • Multi-line or chained commands → scripts/ folder. Once a command spans multiple lines, chains with &&, or needs real logic, a .sh file in scripts/ is far more readable than a cramped JSON string.

Whichever you pick, give each command one home. If the same name lives in both your scripts folder and package.json, Miso treats it as ambiguous and stops with an error rather than guessing which one you meant.

Helper Files

Prefix files with _ to exclude them from discovery. Miso skips them during scanning, so they’re never exposed as runnable commands:

scripts/ deploy.sh _utils.sh # shared helpers — not a command _colors.sh # not a command

Source them from other scripts as needed:

# scripts/deploy.sh source "$(dirname "$0")/_utils.sh"

Exit Codes

Always exit with a meaningful code. Miso propagates the exit code to the calling shell, so CI pipelines and build tools see failures correctly:

if [ -z "$DEPLOY_ENV" ]; then echo "Error: DEPLOY_ENV is not set" exit 1 fi

A script that exits 0 signals success. Any non-zero value signals failure.

Last updated on