Adopt Bashly (bashly.dev) to generate the installer from a single YAML-defined CLI
## Summary
Adopt [Bashly](https://bashly.dev/) as the framework for building the
installer, replacing the hand-written, hand-maintained bash scripts with a
single generated CLI defined in YAML plus per-command bash partials.
## Current state
The installer is ~750 lines of hand-rolled bash spread across four files on
`1.0.x`:
- `install.sh` (61) — OS/distro detection and dispatch
- `install-macos.sh` (258)
- `install-linux.sh` (283)
- `github-push.sh` (147) — sourced at runtime
Pain points this creates:
- **Duplicated boilerplate.** The color codes and `step`/`ok`/`warn`/`die`
helper functions are copy-pasted into `install.sh`, `install-macos.sh`,
`install-linux.sh`, and `github-push.sh`. A change to one must be repeated in
all four.
- **Runtime script fetching.** `install.sh` does `bash <(curl ... install-<os>.sh)`
and the platform scripts in turn `curl ... github-push.sh` into a temp file
and `source` it. This couples execution to network availability of every
individual file and makes the control flow hard to follow.
- **Manual argument parsing.** `-yolo` is parsed with an ad-hoc
`for arg in "$@"` loop in each platform script. There is no `--help`, no
`--version`, and no validation of unknown flags.
- **No structure for subcommands.** Distinct behaviours (new-site vs
existing-project, the `test-mr` contributor tool) are separate scripts or
inline branches rather than first-class commands.
## What Bashly gives us
[Bashly](https://bashly.dev/) is a Ruby gem that generates a **single,
dependency-free bash script** from a `bashly.yml` definition plus bash partials
for each command:
- One generated `install.sh` bundles everything — no runtime `curl`-and-`source`
of sibling scripts, so `bash <(curl ...)` still works but pulls one file.
- Automatic argument parsing, `--help`/`--version`, flag validation, required
args, and environment-variable handling.
- Shared library functions (colors, `step`/`ok`/`die`) defined once and injected
into the bundle, eliminating the four-way duplication.
- Natural home for subcommands, e.g. `install`, `install --existing`,
`test-mr <MR>`.
- The generated output is committed, so end users still need **zero** Ruby/gem
tooling; only maintainers regenerating the script do.
## Proposed approach
1. Add Bashly as a dev/build dependency and a `bashly.yml` defining the CLI
(commands, flags like `--yolo`, args).
2. Port the existing logic into `src/` command and library partials, factoring
the duplicated helpers into a single shared library.
3. Commit the generated `install.sh` and wire regeneration into CI so the
committed script can't drift from `src/`.
4. Keep the public entry point identical:
`bash <(curl -fsSL .../install.sh)` — no change for end users.
## Risks / open questions
- **Build-time dependency.** Maintainers need Ruby + the Bashly gem to
regenerate. End users are unaffected (they run the committed output).
- **Generated diffs.** The committed generated script produces large diffs;
CI regeneration + a check that the committed file matches `src/` mitigates
drift and review noise.
- **Scope.** This is a structural refactor and should be staged so behaviour is
preserved at each step; it may be split into child issues (scaffold CLI,
port macOS, port Linux, port github-push, CI regeneration).
- Confirm Bashly's license and maintenance status are acceptable for a Drupal
contrib project before committing.
---
*Proposed after a code read of branch `1.0.x`. Issue drafted with AI (Claude) assistance.*
issue