template.sh 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. #!/usr/bin/env bash
  2. #
  3. # A wizard — walks a human through a manual procedure step by step.
  4. # Generated by the /wizard skill.
  5. #
  6. # Everything above the "STAGES" marker is the wizard library: do not hand-edit
  7. # it. Author the per-step stages below the marker.
  8. set -euo pipefail
  9. # ──────────────────────────────────────────────────────────────────────────
  10. # Wizard library — delightful, consistent UX. Identical across every wizard.
  11. # ──────────────────────────────────────────────────────────────────────────
  12. if [[ -t 1 ]] && command -v tput >/dev/null 2>&1 && [[ "$(tput colors 2>/dev/null || echo 0)" -ge 8 ]]; then
  13. BOLD=$(tput bold); DIM=$(tput dim); RESET=$(tput sgr0)
  14. BLUE=$(tput setaf 4); GREEN=$(tput setaf 2); YELLOW=$(tput setaf 3); RED=$(tput setaf 1)
  15. else
  16. BOLD=""; DIM=""; RESET=""; BLUE=""; GREEN=""; YELLOW=""; RED=""
  17. fi
  18. # Author sets this at the top of the stages section.
  19. TOTAL_STAGES=0
  20. _STAGE_INDEX=0
  21. ENV_FILE="${ENV_FILE:-.env}"
  22. WRITTEN_ENV=() # KEYs written to ENV_FILE this run
  23. WRITTEN_SECRET=() # secret NAMEs set this run
  24. SKIPPED=() # things we couldn't do (e.g. gh missing)
  25. # _clear — wipe the terminal so only the current step is on screen. No-op when
  26. # output isn't a terminal, so piped logs stay readable.
  27. _clear() {
  28. [[ -t 1 ]] || return 0
  29. if command -v tput >/dev/null 2>&1; then tput clear; else printf '\033[2J\033[3J\033[H'; fi
  30. }
  31. # banner "Title" — opening frame: what this wizard does.
  32. banner() {
  33. _clear
  34. printf '\n%s%s %s%s\n' "$BOLD" "$BLUE" "$1" "$RESET"
  35. printf '%s %s stages%s\n\n' "$DIM" "$TOTAL_STAGES" "$RESET"
  36. printf '%s You drive the browser; this wizard tells you exactly what to do and\n' "$DIM"
  37. printf ' captures the values you copy back. Stop any time with Ctrl-C and re-run\n'
  38. printf ' later — it remembers values already saved.%s\n' "$RESET"
  39. pause "Ready to start?"
  40. }
  41. # stage "Name" — clear the screen, then announce a stage and show progress.
  42. # Clearing keeps only the current step on screen.
  43. stage() {
  44. _clear
  45. _STAGE_INDEX=$((_STAGE_INDEX + 1))
  46. printf '\n%s%s▸ Stage %s/%s · %s%s\n' \
  47. "$BOLD" "$BLUE" "$_STAGE_INDEX" "$TOTAL_STAGES" "$1" "$RESET"
  48. }
  49. # say "..." — a plain instruction line.
  50. say() { printf ' %s\n' "$1"; }
  51. # step "..." — a numbered-feeling action the human takes in the browser.
  52. step() { printf ' %s•%s %s\n' "$BLUE" "$RESET" "$1"; }
  53. note() { printf ' %s%s%s\n' "$DIM" "$1" "$RESET"; }
  54. warn() { printf ' %s⚠ %s%s\n' "$YELLOW" "$1" "$RESET"; }
  55. # open_url URL — open in the human's browser, cross-platform incl. WSL.
  56. open_url() {
  57. local url="$1"
  58. printf ' %s↗ opening%s %s\n' "$GREEN" "$RESET" "$url"
  59. { if command -v wslview >/dev/null 2>&1; then wslview "$url"
  60. elif command -v explorer.exe >/dev/null 2>&1; then explorer.exe "$url"
  61. elif command -v xdg-open >/dev/null 2>&1; then xdg-open "$url"
  62. elif command -v open >/dev/null 2>&1; then open "$url"
  63. else warn "couldn't open a browser — visit it manually: $url"; fi
  64. } >/dev/null 2>&1 || warn "couldn't open a browser — visit it manually: $url"
  65. }
  66. # pause "msg" — wait for the human to confirm they've done the manual part.
  67. pause() {
  68. printf ' %s%s%s ' "$DIM" "${1:-Press Enter to continue}" "$RESET"
  69. read -r _ || true
  70. }
  71. # confirm "question" — y/N gate; returns success on yes.
  72. confirm() {
  73. local reply=""
  74. printf ' %s? %s [y/N] ' "$YELLOW" "$1"
  75. read -r reply || true
  76. [[ "$reply" =~ ^[Yy] ]]
  77. }
  78. # _existing KEY — current value of KEY in ENV_FILE, if any.
  79. _existing() {
  80. [[ -f "$ENV_FILE" ]] || return 1
  81. local line; line=$(grep -E "^${1}=" "$ENV_FILE" | tail -n1) || return 1
  82. printf '%s' "${line#*=}"
  83. }
  84. # ask KEY "Prompt" — read a value into $KEY. Offers the existing .env value as
  85. # a default on re-runs (Enter keeps it). Visible input (non-secret).
  86. ask() {
  87. local key="$1" prompt="$2" current input
  88. current=$(_existing "$key" || true)
  89. if [[ -n "$current" ]]; then
  90. printf ' %s%s%s %s[Enter keeps current]%s ' "$BOLD" "$prompt" "$RESET" "$DIM" "$RESET"
  91. else
  92. printf ' %s%s%s ' "$BOLD" "$prompt" "$RESET"
  93. fi
  94. read -r input || true
  95. [[ -z "$input" && -n "$current" ]] && input="$current"
  96. printf -v "$key" '%s' "$input"
  97. }
  98. # ask_secret KEY "Prompt" — like ask, but input is hidden.
  99. ask_secret() {
  100. local key="$1" prompt="$2" current input
  101. current=$(_existing "$key" || true)
  102. if [[ -n "$current" ]]; then
  103. printf ' %s%s%s %s[Enter keeps current]%s ' "$BOLD" "$prompt" "$RESET" "$DIM" "$RESET"
  104. else
  105. printf ' %s%s%s ' "$BOLD" "$prompt" "$RESET"
  106. fi
  107. read -rs input || true
  108. printf '\n'
  109. [[ -z "$input" && -n "$current" ]] && input="$current"
  110. printf -v "$key" '%s' "$input"
  111. }
  112. # write_env KEY VALUE — upsert KEY=VALUE into ENV_FILE (creates it; replaces
  113. # any existing line). Idempotent.
  114. write_env() {
  115. local key="$1" value="$2" tmp
  116. touch "$ENV_FILE"
  117. tmp=$(mktemp)
  118. grep -vE "^${key}=" "$ENV_FILE" > "$tmp" || true
  119. printf '%s=%s\n' "$key" "$value" >> "$tmp"
  120. mv "$tmp" "$ENV_FILE"
  121. WRITTEN_ENV+=("$key")
  122. printf ' %s✓ wrote%s %s → %s\n' "$GREEN" "$RESET" "$key" "$ENV_FILE"
  123. }
  124. # set_secret NAME VALUE — set a GitHub Actions repo secret via gh. Falls back
  125. # to a warning (and records it) if gh is unavailable or unauthenticated.
  126. set_secret() {
  127. local name="$1" value="$2"
  128. if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then
  129. if printf '%s' "$value" | gh secret set "$name" >/dev/null 2>&1; then
  130. WRITTEN_SECRET+=("$name")
  131. printf ' %s✓ set%s GitHub secret %s\n' "$GREEN" "$RESET" "$name"
  132. return
  133. fi
  134. fi
  135. SKIPPED+=("GitHub secret $name (set it manually: gh secret set $name)")
  136. warn "skipped GitHub secret $name — gh not ready; set it later"
  137. }
  138. # set_var NAME VALUE — set a GitHub Actions repo variable (non-secret).
  139. set_var() {
  140. local name="$1" value="$2"
  141. if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then
  142. if gh variable set "$name" --body "$value" >/dev/null 2>&1; then
  143. printf ' %s✓ set%s GitHub variable %s\n' "$GREEN" "$RESET" "$name"
  144. return
  145. fi
  146. fi
  147. SKIPPED+=("GitHub variable $name")
  148. warn "skipped GitHub variable $name — gh not ready; set it later"
  149. }
  150. # finish — clear, then a closing summary of everything configured.
  151. finish() {
  152. _clear
  153. printf '\n%s%s ✓ Setup complete%s\n' "$BOLD" "$GREEN" "$RESET"
  154. (( ${#WRITTEN_ENV[@]} )) && note "wrote ${#WRITTEN_ENV[@]} value(s) to $ENV_FILE: ${WRITTEN_ENV[*]}"
  155. (( ${#WRITTEN_SECRET[@]} )) && note "set ${#WRITTEN_SECRET[@]} GitHub secret(s): ${WRITTEN_SECRET[*]}"
  156. if (( ${#SKIPPED[@]} )); then
  157. printf '\n'; warn "still to do by hand:"
  158. for s in "${SKIPPED[@]}"; do note " - $s"; done
  159. fi
  160. printf '\n'
  161. }
  162. # ──────────────────────────────────────────────────────────────────────────
  163. # STAGES — author this section. One stage() per step the human takes.
  164. # Replace the example below. Set TOTAL_STAGES to match the stages you write.
  165. # ──────────────────────────────────────────────────────────────────────────
  166. TOTAL_STAGES=1
  167. banner "Stripe setup"
  168. # ── Example stage: replace with your real steps ───────────────────────────
  169. stage "Stripe — API keys"
  170. say "We'll grab your Stripe test keys and store them for local dev + CI."
  171. open_url "https://dashboard.stripe.com/test/apikeys"
  172. step "On the API keys page, copy the Publishable key (starts pk_test_)."
  173. ask STRIPE_PUBLISHABLE_KEY "Paste the publishable key:"
  174. step "Click 'Reveal test key' on the Secret key row, then copy it."
  175. ask_secret STRIPE_SECRET_KEY "Paste the secret key:"
  176. write_env STRIPE_PUBLISHABLE_KEY "$STRIPE_PUBLISHABLE_KEY"
  177. write_env STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY"
  178. set_secret STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY" # CI needs this one
  179. # ──────────────────────────────────────────────────────────────────────────
  180. finish