update skills
This commit is contained in:
1 parent
9fb18f57ec
commit
ff86cf48b4
82 files changed
+2156
-650
No files matched your search
@@ -1,13 +1,15 @@
|
||||
---
|
||||
name: wizard
|
||||
description: Generate an interactive bash wizard that walks a human through steps only they can perform. Use when provisioning infrastructure, setting up credentials or CI secrets, walking an unfamiliar third-party dashboard, or running a one-off migration or cutover. Don't invoke this for steps the agent can perform itself.
|
||||
description: Generate an interactive Bash wizard for steps only a human can perform, such as dashboard setup or credential capture. Use for a requested manual procedure, not work the agent can complete directly.
|
||||
---
|
||||
|
||||
# Wizard
|
||||
|
||||
A **wizard** is a bash script that walks a human, step by step, through a manual procedure that's tedious to do by hand and tedious to re-explain to an AI every time. It opens each URL, says exactly what to click and copy, captures the values, writes them where they belong (`.env`, GitHub secrets), confirms at every stage, and shows how many stages are left. It might configure third-party services, run a one-off migration, or move the project from one state to another.
|
||||
A **wizard** walks a human through the manual parts of the requested procedure, captures values, and writes only to the selected destinations. Reuse the user's chosen service and CI platform; generating a wizard does not authorize running provisioning or publishing credentials.
|
||||
|
||||
The delightful UX is already solved by [template.sh](template.sh) — stage-by-stage progress, confirmation gates, cross-platform URL opening (including WSL), hidden secret entry, idempotent `.env` upserts, `gh secret`/`gh variable` writes, and a closing summary. **Your job is only to scope the procedure and author its stages.** The library above the `STAGES` marker is identical in every wizard; that consistency is the point — never hand-edit it.
|
||||
Start from [template.sh](template.sh) for progress, confirmation gates, URL opening, hidden input, and persistence helpers. Adapt helpers when the selected destination requires it; verify changes with dummy data before handling real credentials.
|
||||
|
||||
The bundled dotenv helpers accept uppercase variable names and required, single-line values without apostrophes. They write private, single-quoted values so hashes, spaces, dollars, and backslashes survive; unsupported values or existing formats stop with an error. Confirm that the actual consumer supports this format. If it needs another dialect or multiline values, adapt both reading and writing and test a round trip through that consumer. EOF cancels the wizard; empty input keeps an existing value or fails if none exists.
|
||||
|
||||
A wizard is ephemeral by default — built for one run, saved to a scratch or `scripts/` path, deleted when the job's done. Commit it only when the user wants a repeatable setup path that should live in the repo.
|
||||
|
||||
@@ -17,12 +19,12 @@ A wizard is ephemeral by default — built for one run, saved to a scratch or `s
|
||||
|
||||
Work out every manual step the human must take and every value that gets captured along the way. Read the repo first — don't ask cold:
|
||||
|
||||
- For setup: `.env`, `.env.example`, `.env.*`, `README`, `docker-compose*`, framework config, and `.github/workflows/*` (every `secrets.*` / `vars.*` reference is a value the wizard must produce).
|
||||
- For setup: configuration examples, README, compose/framework configuration, and the actual CI files (this repository uses `.gitlab-ci.yml`). Inspect secret names and references without displaying existing secret values. Include only values needed for the requested procedure, not every variable in the repository.
|
||||
- For a migration or transition: the current state, the target state, and the irreversible actions between them.
|
||||
|
||||
Then show the user the ordered list of stages and the values each produces, and confirm — they may add, drop, or reorder.
|
||||
|
||||
**Done when:** every stage is named in order, and for each captured value you know (a) where the human gets it, (b) where it's written (`.env`, a GitHub secret, both, or nowhere — some stages are pure actions), and (c) whether it's secret (hidden entry) or public.
|
||||
**Done when:** every stage is named in order, and each captured value has a known source, selected destination (local file, configured secret store, or neither), and secrecy level.
|
||||
|
||||
### 2. Map each stage's journey
|
||||
|
||||
@@ -32,13 +34,14 @@ For each stage, write the precise path a human follows: which URL to open, what
|
||||
|
||||
### 3. Author the wizard
|
||||
|
||||
Copy `template.sh` to the target path. Replace the example stage with one `stage` per step, in dependency order. Use the library helpers — `stage`, `say`/`step`, `open_url`, `ask`/`ask_secret`, `write_env`, `set_secret`/`set_var`, `pause`/`confirm` — and set `TOTAL_STAGES` to the number of stages you wrote.
|
||||
Copy `template.sh` to the target path. Replace the example stages in dependency order, using `stage`, `say`/`step`, `open_url`, `ask`/`ask_secret`, `write_env`, and `pause`/`confirm`. Set honest stage/time estimates. For CI secrets, use the selected platform's existing tooling or guide the human through its UI. Add a provider-specific writer only when needed and verify its local help, authentication, project scope, and secret-safe input mechanism first.
|
||||
|
||||
Hold the bar the template sets: open the URL before asking for its value, use `ask_secret` for anything secret, `write_env` every persisted value, `set_secret` only the values CI actually needs, and `confirm` before any irreversible action. Each `stage` clears the screen so only the current step is visible — keep a stage to one focused task so nothing the human needs scrolls away. Don't touch the library above the marker.
|
||||
Open the URL before asking for its value, use `ask_secret` for secrets, and persist only to the selected destinations. Require a successful `confirm` before an irreversible action. Do not expose values in logs or command arguments. On an ambiguous remote write, check its result before retrying; on missing authority or repeated failure, stop that stage and report what remains. Keep each stage focused so needed instructions remain visible.
|
||||
|
||||
### 4. Verify and hand off
|
||||
|
||||
- `bash -n <script>`; run `shellcheck` if available.
|
||||
- Run `python3 test_wizard.py` from this skill directory (requires Node for the dotenv consumer check). Source the generated script to test its helpers with dummy values in a temporary directory, without running the stages.
|
||||
- `chmod +x <script>`.
|
||||
- Don't run it end-to-end yourself — it opens browsers and blocks on human input. Trace it statically instead: every value from step 1 is captured and lands where step 1 said, and every `set_secret` name exactly matches a `secrets.*` reference in CI.
|
||||
- Tell the user how to run it. If it's a repeatable setup path, commit it and link it from the README so the next person runs the script instead of asking an AI.
|
||||
- Don't run it end-to-end yourself — it opens browsers and blocks on human input. Check that each value lands in the selected destination and any CI variable name matches an actual reference. Test remote writers with stubs, never real credentials.
|
||||
- Tell the user how to run it. For a requested repeatable setup path, link it from the relevant setup documentation. Commit only if requested, and do not execute the manual procedure merely because its script is complete.
|
||||
@@ -1,3 +1,5 @@
|
||||
interface:
|
||||
display_name: "Wizard"
|
||||
short_description: "Generate an interactive setup wizard"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -3,8 +3,8 @@
|
||||
# A wizard — walks a human through a manual procedure step by step.
|
||||
# Generated by the /wizard skill.
|
||||
#
|
||||
# Everything above the "STAGES" marker is the wizard library: do not hand-edit
|
||||
# it. Author the per-step stages below the marker.
|
||||
# Author the per-step stages below the "STAGES" marker. Adapt and test the
|
||||
# persistence helpers when the target consumer needs a different format.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -19,14 +19,15 @@ else
|
||||
BOLD=""; DIM=""; RESET=""; BLUE=""; GREEN=""; YELLOW=""; RED=""
|
||||
fi
|
||||
|
||||
# Author sets this at the top of the stages section.
|
||||
# Author sets these two at the top of the stages section.
|
||||
TOTAL_STAGES=0
|
||||
TOTAL_MINUTES=0
|
||||
|
||||
_STAGE_INDEX=0
|
||||
_MINUTES_ELAPSED=0
|
||||
ENV_FILE="${ENV_FILE:-.env}"
|
||||
WRITTEN_ENV=() # KEYs written to ENV_FILE this run
|
||||
WRITTEN_SECRET=() # secret NAMEs set this run
|
||||
SKIPPED=() # things we couldn't do (e.g. gh missing)
|
||||
SKIPPED=() # manual steps still outstanding
|
||||
|
||||
# _clear — wipe the terminal so only the current step is on screen. No-op when
|
||||
# output isn't a terminal, so piped logs stay readable.
|
||||
@@ -35,24 +36,28 @@ _clear() {
|
||||
if command -v tput >/dev/null 2>&1; then tput clear; else printf '\033[2J\033[3J\033[H'; fi
|
||||
}
|
||||
|
||||
# banner "Title" — opening frame: what this wizard does.
|
||||
# banner "Title" — opening frame: what this wizard does and how long it takes.
|
||||
banner() {
|
||||
_clear
|
||||
printf '\n%s%s %s%s\n' "$BOLD" "$BLUE" "$1" "$RESET"
|
||||
printf '%s %s stages%s\n\n' "$DIM" "$TOTAL_STAGES" "$RESET"
|
||||
printf '%s %s stages · about %s minutes%s\n\n' \
|
||||
"$DIM" "$TOTAL_STAGES" "$TOTAL_MINUTES" "$RESET"
|
||||
printf '%s You drive the browser; this wizard tells you exactly what to do and\n' "$DIM"
|
||||
printf ' captures the values you copy back. Stop any time with Ctrl-C and re-run\n'
|
||||
printf ' later — it remembers values already saved.%s\n' "$RESET"
|
||||
pause "Ready to start?"
|
||||
}
|
||||
|
||||
# stage "Name" — clear the screen, then announce a stage and show progress.
|
||||
# Clearing keeps only the current step on screen.
|
||||
# stage "Name" <minutes> — clear the screen, then announce a stage and show
|
||||
# progress + time remaining. Clearing keeps only the current step on screen.
|
||||
stage() {
|
||||
_clear
|
||||
_STAGE_INDEX=$((_STAGE_INDEX + 1))
|
||||
printf '\n%s%s▸ Stage %s/%s · %s%s\n' \
|
||||
"$BOLD" "$BLUE" "$_STAGE_INDEX" "$TOTAL_STAGES" "$1" "$RESET"
|
||||
local remaining=$((TOTAL_MINUTES - _MINUTES_ELAPSED))
|
||||
(( remaining < 0 )) && remaining=0
|
||||
_MINUTES_ELAPSED=$((_MINUTES_ELAPSED + ${2:-0}))
|
||||
printf '\n%s%s▸ Stage %s/%s · %s%s %s(~%s min left)%s\n' \
|
||||
"$BOLD" "$BLUE" "$_STAGE_INDEX" "$TOTAL_STAGES" "$1" "$RESET" "$DIM" "$remaining" "$RESET"
|
||||
}
|
||||
|
||||
# say "..." — a plain instruction line.
|
||||
@@ -77,101 +82,114 @@ open_url() {
|
||||
# pause "msg" — wait for the human to confirm they've done the manual part.
|
||||
pause() {
|
||||
printf ' %s%s%s ' "$DIM" "${1:-Press Enter to continue}" "$RESET"
|
||||
read -r _ || true
|
||||
read -r _ || _cancel
|
||||
}
|
||||
|
||||
_cancel() { printf '\nCancelled; no further steps were run.\n' >&2; exit 1; }
|
||||
|
||||
_valid_key() {
|
||||
[[ "$1" =~ ^[A-Z_][A-Z0-9_]*$ && "$1" != HOME && "$1" != CODEX_HOME && "$1" != PATH && "$1" != ENV_FILE ]] || {
|
||||
printf 'Invalid or reserved variable name.\n' >&2; return 1;
|
||||
}
|
||||
}
|
||||
|
||||
# confirm "question" — y/N gate; returns success on yes.
|
||||
confirm() {
|
||||
local reply=""
|
||||
printf ' %s? %s [y/N] ' "$YELLOW" "$1"
|
||||
read -r reply || true
|
||||
[[ "$reply" =~ ^[Yy] ]]
|
||||
read -r reply || _cancel
|
||||
[[ "$reply" == y || "$reply" == Y || "$reply" == yes || "$reply" == YES ]]
|
||||
}
|
||||
|
||||
# _existing KEY — current value of KEY in ENV_FILE, if any.
|
||||
_existing() {
|
||||
[[ -f "$ENV_FILE" ]] || return 1
|
||||
local line; line=$(grep -E "^${1}=" "$ENV_FILE" | tail -n1) || return 1
|
||||
printf '%s' "${line#*=}"
|
||||
[[ -e "$ENV_FILE" ]] || return 0
|
||||
local line value=""
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
[[ "$line" =~ ^[[:space:]]*(export[[:space:]]+)?${1}[[:space:]]*= ]] || continue
|
||||
value="${line#*=}"
|
||||
# ponytail: only reuse the single-line format we emit; adapt for another dotenv dialect.
|
||||
if [[ "$value" == \"* || "$value" == \`* || "$value" == *$'\r'* ]]; then
|
||||
printf 'Existing value uses an unsupported dotenv format; adapt the helper first.\n' >&2; return 1
|
||||
elif [[ "$value" == "'"* ]]; then
|
||||
[[ "$value" == *"'" && ${#value} -ge 2 ]] || return 1
|
||||
value="${value:1:${#value}-2}"
|
||||
[[ "$value" != *"'"* ]] || return 1
|
||||
else
|
||||
[[ "$value" != *[[:space:]]* && "$value" != *[\'\"\`\\]* ]] || return 1
|
||||
value="${value%%#*}"
|
||||
fi
|
||||
done < "$ENV_FILE"
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
# ask KEY "Prompt" — read a value into $KEY. Offers the existing .env value as
|
||||
# a default on re-runs (Enter keeps it). Visible input (non-secret).
|
||||
ask() {
|
||||
local key="$1" prompt="$2" current input
|
||||
current=$(_existing "$key" || true)
|
||||
_valid_key "$key" || return 1
|
||||
current=$(_existing "$key") || return 1
|
||||
if [[ -n "$current" ]]; then
|
||||
printf ' %s%s%s %s[Enter keeps current]%s ' "$BOLD" "$prompt" "$RESET" "$DIM" "$RESET"
|
||||
else
|
||||
printf ' %s%s%s ' "$BOLD" "$prompt" "$RESET"
|
||||
fi
|
||||
read -r input || true
|
||||
read -r input || _cancel
|
||||
[[ -z "$input" && -n "$current" ]] && input="$current"
|
||||
[[ -n "$input" ]] || { printf 'A value is required.\n' >&2; return 1; }
|
||||
printf -v "$key" '%s' "$input"
|
||||
}
|
||||
|
||||
# ask_secret KEY "Prompt" — like ask, but input is hidden.
|
||||
ask_secret() {
|
||||
local key="$1" prompt="$2" current input
|
||||
current=$(_existing "$key" || true)
|
||||
_valid_key "$key" || return 1
|
||||
current=$(_existing "$key") || return 1
|
||||
if [[ -n "$current" ]]; then
|
||||
printf ' %s%s%s %s[Enter keeps current]%s ' "$BOLD" "$prompt" "$RESET" "$DIM" "$RESET"
|
||||
else
|
||||
printf ' %s%s%s ' "$BOLD" "$prompt" "$RESET"
|
||||
fi
|
||||
read -rs input || true
|
||||
read -rs input || _cancel
|
||||
printf '\n'
|
||||
[[ -z "$input" && -n "$current" ]] && input="$current"
|
||||
[[ -n "$input" ]] || { printf 'A value is required.\n' >&2; return 1; }
|
||||
printf -v "$key" '%s' "$input"
|
||||
}
|
||||
|
||||
# write_env KEY VALUE — upsert KEY=VALUE into ENV_FILE (creates it; replaces
|
||||
# any existing line). Idempotent.
|
||||
# write_env KEY VALUE — atomic, private single-line dotenv upsert.
|
||||
# Single quotes preserve #, spaces, dollars and backslashes literally.
|
||||
write_env() {
|
||||
local key="$1" value="$2" tmp
|
||||
touch "$ENV_FILE"
|
||||
tmp=$(mktemp)
|
||||
grep -vE "^${key}=" "$ENV_FILE" > "$tmp" || true
|
||||
printf '%s=%s\n' "$key" "$value" >> "$tmp"
|
||||
mv "$tmp" "$ENV_FILE"
|
||||
local key="$1" value="$2" tmp line
|
||||
_valid_key "$key" || return 1
|
||||
[[ -n "$value" && "$value" != *"'"* && "$value" != *$'\n'* && "$value" != *$'\r'* ]] || {
|
||||
printf 'Cannot save an empty value, apostrophe, or newline in this dotenv format.\n' >&2; return 1;
|
||||
}
|
||||
_existing "$key" >/dev/null || return 1
|
||||
tmp=$(mktemp "${ENV_FILE}.XXXXXX") || return 1
|
||||
if [[ -e "$ENV_FILE" ]] && ! awk -v key="$key" '$0 !~ "^[[:space:]]*(export[[:space:]]+)?" key "[[:space:]]*="' "$ENV_FILE" > "$tmp"; then
|
||||
rm -- "$tmp"
|
||||
return 1
|
||||
fi
|
||||
if printf "%s='%s'\n" "$key" "$value" >> "$tmp" && mv -- "$tmp" "$ENV_FILE"; then
|
||||
:
|
||||
else
|
||||
rm -- "$tmp"
|
||||
printf 'Could not save dotenv value.\n' >&2; return 1
|
||||
fi
|
||||
WRITTEN_ENV+=("$key")
|
||||
printf ' %s✓ wrote%s %s → %s\n' "$GREEN" "$RESET" "$key" "$ENV_FILE"
|
||||
}
|
||||
|
||||
# set_secret NAME VALUE — set a GitHub Actions repo secret via gh. Falls back
|
||||
# to a warning (and records it) if gh is unavailable or unauthenticated.
|
||||
set_secret() {
|
||||
local name="$1" value="$2"
|
||||
if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then
|
||||
if printf '%s' "$value" | gh secret set "$name" >/dev/null 2>&1; then
|
||||
WRITTEN_SECRET+=("$name")
|
||||
printf ' %s✓ set%s GitHub secret %s\n' "$GREEN" "$RESET" "$name"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
SKIPPED+=("GitHub secret $name (set it manually: gh secret set $name)")
|
||||
warn "skipped GitHub secret $name — gh not ready; set it later"
|
||||
}
|
||||
|
||||
# set_var NAME VALUE — set a GitHub Actions repo variable (non-secret).
|
||||
set_var() {
|
||||
local name="$1" value="$2"
|
||||
if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then
|
||||
if gh variable set "$name" --body "$value" >/dev/null 2>&1; then
|
||||
printf ' %s✓ set%s GitHub variable %s\n' "$GREEN" "$RESET" "$name"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
SKIPPED+=("GitHub variable $name")
|
||||
warn "skipped GitHub variable $name — gh not ready; set it later"
|
||||
}
|
||||
|
||||
# finish — clear, then a closing summary of everything configured.
|
||||
finish() {
|
||||
_clear
|
||||
printf '\n%s%s ✓ Setup complete%s\n' "$BOLD" "$GREEN" "$RESET"
|
||||
if (( ${#SKIPPED[@]} )); then
|
||||
warn 'Setup incomplete'
|
||||
else
|
||||
printf '\n%s%s ✓ Setup complete%s\n' "$BOLD" "$GREEN" "$RESET"
|
||||
fi
|
||||
(( ${#WRITTEN_ENV[@]} )) && note "wrote ${#WRITTEN_ENV[@]} value(s) to $ENV_FILE: ${WRITTEN_ENV[*]}"
|
||||
(( ${#WRITTEN_SECRET[@]} )) && note "set ${#WRITTEN_SECRET[@]} GitHub secret(s): ${WRITTEN_SECRET[*]}"
|
||||
if (( ${#SKIPPED[@]} )); then
|
||||
printf '\n'; warn "still to do by hand:"
|
||||
for s in "${SKIPPED[@]}"; do note " - $s"; done
|
||||
@@ -181,16 +199,20 @@ finish() {
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
# STAGES — author this section. One stage() per step the human takes.
|
||||
# Replace the example below. Set TOTAL_STAGES to match the stages you write.
|
||||
# Replace the example below. Set the two totals to match the stages you write.
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
# Sourcing exposes helpers for isolated checks without opening browsers.
|
||||
if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then return 0; fi
|
||||
|
||||
TOTAL_STAGES=1
|
||||
TOTAL_MINUTES=5
|
||||
|
||||
banner "Stripe setup"
|
||||
|
||||
# ── Example stage: replace with your real steps ───────────────────────────
|
||||
stage "Stripe — API keys"
|
||||
say "We'll grab your Stripe test keys and store them for local dev + CI."
|
||||
stage "Stripe — API keys" 5
|
||||
say "We'll grab your Stripe test keys and store them for local development."
|
||||
open_url "https://dashboard.stripe.com/test/apikeys"
|
||||
step "On the API keys page, copy the Publishable key (starts pk_test_)."
|
||||
ask STRIPE_PUBLISHABLE_KEY "Paste the publishable key:"
|
||||
@@ -198,7 +220,6 @@ step "Click 'Reveal test key' on the Secret key row, then copy it."
|
||||
ask_secret STRIPE_SECRET_KEY "Paste the secret key:"
|
||||
write_env STRIPE_PUBLISHABLE_KEY "$STRIPE_PUBLISHABLE_KEY"
|
||||
write_env STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY"
|
||||
set_secret STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY" # CI needs this one
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
finish
|
||||
@@ -0,0 +1,58 @@
|
||||
"""Isolated Bash helper checks; only dummy values, no browsers or secret stores."""
|
||||
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
|
||||
template = Path(__file__).with_name("template.sh").resolve()
|
||||
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
env_file = Path(directory) / ".env"
|
||||
environment = {**os.environ, "ENV_FILE": str(env_file)}
|
||||
|
||||
def run(command, value="", input="", expected=0):
|
||||
result = subprocess.run(
|
||||
["bash", "-c", 'source "$1"; ' + command, "check", str(template)],
|
||||
env={**environment, "DUMMY_VALUE": value},
|
||||
input=input, text=True, capture_output=True,
|
||||
)
|
||||
assert (result.returncode == 0) == (expected == 0), result.stderr
|
||||
return result
|
||||
|
||||
for value in ("dummy#value", " spaces ", 'a"b', r"dollar$and\backslash", "replacement"):
|
||||
run('write_env DUMMY_KEY "$DUMMY_VALUE"', value)
|
||||
parsed = subprocess.run(
|
||||
["node", "-e", "process.stdout.write(JSON.stringify(require('node:util').parseEnv(require('node:fs').readFileSync(process.argv[1], 'utf8'))))", str(env_file)],
|
||||
text=True, capture_output=True, check=True,
|
||||
)
|
||||
assert json.loads(parsed.stdout)["DUMMY_KEY"] == value
|
||||
assert run("_existing DUMMY_KEY").stdout == value
|
||||
assert env_file.stat().st_mode & 0o777 == 0o600
|
||||
assert env_file.read_text().count("DUMMY_KEY=") == 1
|
||||
|
||||
run("write_env OTHER_KEY unchanged")
|
||||
run("write_env DUMMY_KEY replacement")
|
||||
assert run("_existing OTHER_KEY").stdout == "unchanged"
|
||||
saved = env_file.read_bytes()
|
||||
for value in ("", "can't", "two\nlines", "carriage\rreturn"):
|
||||
run('write_env DUMMY_KEY "$DUMMY_VALUE"', value, expected=1)
|
||||
assert env_file.read_bytes() == saved
|
||||
run('write_env "BAD-KEY" value', expected=1)
|
||||
run('write_env HOME value', expected=1)
|
||||
for helper in ("ask", "ask_secret"):
|
||||
run(f'{helper} NEW_KEY prompt', expected=1)
|
||||
run(f'{helper} NEW_KEY prompt', input="\n", expected=1)
|
||||
run(f'{helper} DUMMY_KEY prompt; [[ "$DUMMY_KEY" == replacement ]]', input="\n")
|
||||
run(f'{helper} NEW_KEY prompt; [[ "$NEW_KEY" == captured ]]', input="captured\n")
|
||||
run("pause", expected=1)
|
||||
run("confirm question", expected=1)
|
||||
run("confirm question", input="yesterday\n", expected=1)
|
||||
run("confirm question", input="yes\n")
|
||||
env_file.write_text('DUMMY_KEY="unsupported"\n')
|
||||
run('write_env DUMMY_KEY replacement', expected=1)
|
||||
assert env_file.read_text() == 'DUMMY_KEY="unsupported"\n'
|
||||
|
||||
print("Wizard helper checks passed")
|
||||
Reference in new issue
Block a user