diff --git a/common/install.sh b/common/install.sh index b4b2d3c..61e0512 100755 --- a/common/install.sh +++ b/common/install.sh @@ -65,10 +65,10 @@ fetch_release_asset() { fi id="$(curl -fsSL \ - -H "Authorization: Bearer ${GITHUB_TOKEN}" \ - -H 'Accept: application/vnd.github+json' \ - "https://api.github.com/repos/${RepoSlug}/releases/latest" \ - | release_asset_id "$name")" || return 1 + -H "Authorization: Bearer ${GITHUB_TOKEN}" \ + -H 'Accept: application/vnd.github+json' \ + "https://api.github.com/repos/${RepoSlug}/releases/latest" | + release_asset_id "$name")" || return 1 curl -fsSL \ -H "Authorization: Bearer ${GITHUB_TOKEN}" \ @@ -186,11 +186,11 @@ start_stack() { # package refuses an anonymous pull with `unauthorized`. # --password-stdin, not an argument: argv is visible to every other user on the host. if [ -n "${GITHUB_TOKEN:-}" ]; then - printf '%s' "${GITHUB_TOKEN}" \ - | docker login ghcr.io -u "${RepoSlug%%/*}" --password-stdin >/dev/null || { - echo 'could not sign in to ghcr.io; the token needs read:packages' >&2 - return 1 - } + printf '%s' "${GITHUB_TOKEN}" | + docker login ghcr.io -u "${RepoSlug%%/*}" --password-stdin >/dev/null || { + echo 'could not sign in to ghcr.io; the token needs read:packages' >&2 + return 1 + } fi docker compose up --remove-orphans -d || return 1 } @@ -200,7 +200,8 @@ start_stack() { # pipeline as failed. Measured at roughly one run in seven — a stack that # refused to migrate, at random, with a message about a service that was there. compose_has_service() { - local -r service="$1"; shift + local -r service="$1" + shift local services services="$(docker compose "$@" config --services)" || return 1 @@ -225,15 +226,33 @@ run_migrations() { } main() { - command -v curl >/dev/null || { echo 'curl is required'; return 1; } - docker compose version >/dev/null 2>&1 || { echo 'docker compose is required'; return 1; } + command -v curl >/dev/null || { + echo 'curl is required' + return 1 + } + docker compose version >/dev/null 2>&1 || { + echo 'docker compose is required' + return 1 + } require_private_tools || return 1 - create_directory || { echo 'could not create the target directory'; return 1; } - download_release_assets || { echo 'could not download the release assets'; return 1; } + create_directory || { + echo 'could not create the target directory' + return 1 + } + download_release_assets || { + echo 'could not download the release assets' + return 1 + } require_configured_image || return 1 - start_stack || { echo 'could not start the stack; check the output above'; return 1; } - run_migrations || { echo 'could not run migrations; check the output above'; return 1; } + start_stack || { + echo 'could not start the stack; check the output above' + return 1 + } + run_migrations || { + echo 'could not run migrations; check the output above' + return 1 + } # One line per application (ADR-0022), read out of .env so it reflects any # port the operator changed. diff --git a/docs/superpowers/plans/2026-09-16-clean-shell-and-comments.md b/docs/superpowers/plans/2026-09-16-clean-shell-and-comments.md new file mode 100644 index 0000000..ba670a7 --- /dev/null +++ b/docs/superpowers/plans/2026-09-16-clean-shell-and-comments.md @@ -0,0 +1,81 @@ +# Clean Shell and Comments Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** every tracked file meets `comment-code`; every shell file meets the Clean Bash rules; lint keeps it that way. + +**Architecture:** five independent pull requests by area, each merged before the next branches. No behaviour change anywhere; the existing bats suites and real-project checklists are the regression net. + +**Tech Stack:** bash 5, shellcheck 0.11.0, shfmt 3.14.1, bats 1.13.0, mise. + +**Spec:** `docs/superpowers/specs/2026-09-16-clean-shell-and-comments-design.md` + +## Global Constraints + +- Rules: the `comment-code` skill (`~/.claude/skills/comment-code/SKILL.md`) and `~/.dotfiles/agentic/rules/bash.md`. Read both before editing. +- No behaviour change: error messages, stdout/stderr lines, exit codes and generated-file contents stay byte-identical. Never edit a test assertion to make a code change pass. +- No file-level `readonly`; `local -r` inside functions is fine. +- No `set -euo pipefail` added to sourced files (`lib/*.sh`, `services/**/*.sh`, `tests/helpers/*.bash`, fixture drivers). Executables (`scaffold`, `scripts/*.sh`, `common/install.sh`) must have it. +- Bats `@test` bodies keep `[ … ]`; helpers and non-test shell use `[[ ]]` / `(( ))`. +- `[ ]` → `[[ ]]`: quote the RHS of `=`, `==`, `!=` unless a glob is intended. Numeric tests → `(( ))`. +- `echo` → `printf '%s\n'` only where a variable/expansion is printed. +- Never `local x="$(cmd)"`: declare, then assign. +- Split a function over 20 lines only at a nameable seam. Linear sequences (big `case`, heredoc, render loop) stay; record each kept function and why in the task report. +- Function names that appear in `docs/` or `README.md` keep their name and contract: `add_app_service apply_adapter apply_service_dockerfile apply_service_drivers assemble_compose assert_known_tiers cmd_add cmd_new cmd_wizard config_roots generate_service_passwords init_project lint_adapters lint_services load_adapter load_toolchain_env project_name_is_usable register_config_root register_image_target role_path service_compose_key service_driver_dockerfile service_healthy sync_ci_roots tui_name_is_usable tui_prompt_name wizard_actions wizard_command wizard_new_args wizard_options wizard_prompt_for wizard_questions write_env_lines splice_flask_probe init_flask_alembic assert_nest_probe_spliced`. +- Keep: `#!` lines, `# shellcheck …` pragmas (each carrying a same-line reason), `# noqa`, `eslint-disable`, `@phpstan-`, `ponytail:` markers, and splice anchors `# @SERVICE_SETUP@`, `# @DB_ENGINE@`, `# @DB_PROBE@` (column and spelling are matched by awk/sed). +- Do not touch: `verbatim` rows of `docs/PROVENANCE.md` (`common/.editorconfig`), `common/docs/.vitepress/theme/vendor/**`, `docs/**`, `*.md`, `LICENSE`, `mise.lock`, `UPSTREAM`. +- Script headers: purpose + usage only. No `Description :`/`Author`/banner blocks. +- Commits: conventional (`chore:`, `refactor:`, `style:`), no co-author noise beyond repo convention. Each PR from a fresh branch off up-to-date `main`. +- Verification per PR: `mise run lint` clean; `mise run test-runner` exits 0. +- Real-project check (Tasks 2, 3, 5): in the scratchpad, `./scaffold new