mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 08:01:26 +00:00
add cli-sync workspace snapshot/load scripts (#9322)
* feat(fixtures): add cli-sync workspace snapshot/load scripts * fix(fixtures): address review nits (env var password, mktemp, dead refs) * fix(fixtures): address CI review (SIGPIPE, JSON escaping, doc/code drift)
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
name: Check fixture is empty
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "fixtures/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "fixtures/**"
|
||||
|
||||
jobs:
|
||||
check-empty-fixture:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Ensure fixtures/cli-sync/ has no committed snapshot
|
||||
run: bash fixtures/check-empty.sh
|
||||
@@ -0,0 +1,68 @@
|
||||
# Fixtures
|
||||
|
||||
Helpers for sharing a reproducible test workspace alongside a PR.
|
||||
|
||||
The workflow is:
|
||||
|
||||
1. While iterating on a PR, snapshot your local test workspace into
|
||||
`fixtures/cli-sync/` so a teammate (or CI) can replay it.
|
||||
2. Commit the snapshot on your branch so reviewers can load it locally.
|
||||
3. **Before merging**, clear `fixtures/cli-sync/` again. CI fails on `main` /
|
||||
PRs that try to merge a non-empty fixture (see `check-empty.sh`).
|
||||
|
||||
## Scripts
|
||||
|
||||
All scripts assume:
|
||||
|
||||
- A local Windmill backend running at `http://localhost:8000` (see top-level
|
||||
`AGENTS.md` for `cargo run` / `npm run dev`).
|
||||
- Default super-admin credentials `admin@windmill.dev` / `changeme`.
|
||||
- `bun` and `python3` are installed and on `PATH`. No `wmill` install
|
||||
required — the scripts invoke `cli/src/main.ts` via `bun run` directly.
|
||||
`python3` is used only to build correctly-escaped JSON request bodies.
|
||||
|
||||
The login token is passed to the CLI via `--token`, which means it appears in
|
||||
`/proc/<pid>/cmdline` for the duration of the `bun run` call. This is fine
|
||||
against a local dev instance; if you point the scripts at a real instance,
|
||||
the credentials are no more exposed than running `wmill` directly with
|
||||
explicit flags, but bear it in mind.
|
||||
|
||||
### `load.sh` — load the fixture into a fresh workspace
|
||||
|
||||
```bash
|
||||
./fixtures/load.sh
|
||||
```
|
||||
|
||||
Logs in as `admin@windmill.dev`, creates a new workspace with a random id
|
||||
(`fixture-<8 hex chars>`), and pushes `fixtures/cli-sync/` into it via
|
||||
`wmill sync push`. Prints the workspace id at the end so you can open it in
|
||||
the UI.
|
||||
|
||||
Flags:
|
||||
- `--base-url <url>` (default `http://localhost:8000`)
|
||||
- `--email <email>` (default `admin@windmill.dev`)
|
||||
- `--password <pwd>` (default `changeme`) — prefer `WMILL_PASSWORD=<pwd>` env
|
||||
var when using a real password, since `--password` ends up in `ps` /
|
||||
shell history.
|
||||
- `--workspace <id>` (default `fixture-<random>`)
|
||||
- `--dir <path>` (default `fixtures/cli-sync`)
|
||||
|
||||
### `snapshot.sh` — snapshot a workspace into the fixture folder
|
||||
|
||||
```bash
|
||||
./fixtures/snapshot.sh <workspace-id>
|
||||
```
|
||||
|
||||
Pulls the given workspace into `fixtures/cli-sync/` via `wmill sync pull`.
|
||||
The target directory is cleared first (everything except `wmill.yaml` and
|
||||
`.gitkeep`) so the snapshot reflects exactly what's in the workspace.
|
||||
|
||||
Flags: same as `load.sh` minus `--workspace` (passed as positional arg).
|
||||
|
||||
### `check-empty.sh` — fail if the fixture is non-empty
|
||||
|
||||
Used by CI to guard `main`. Run it locally before opening a PR for merge:
|
||||
|
||||
```bash
|
||||
./fixtures/check-empty.sh
|
||||
```
|
||||
Executable
+27
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
# Fail if fixtures/cli-sync/ contains anything beyond the fixture scaffold
|
||||
# (wmill.yaml, .gitkeep). Used by CI to guard `main` against accidentally
|
||||
# merging PRs with a test workspace snapshot still committed.
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
DIR="${1:-$SCRIPT_DIR/cli-sync}"
|
||||
|
||||
ALLOWED_RE='^fixtures/cli-sync/(\.gitkeep|wmill\.yaml)$'
|
||||
|
||||
# Use `git ls-files` so we only check tracked files. Untracked local snapshots
|
||||
# are fine — devs may keep them locally between sessions.
|
||||
EXTRA=$(cd "$REPO_ROOT" && git ls-files fixtures/cli-sync \
|
||||
| grep -vE "$ALLOWED_RE" || true)
|
||||
|
||||
if [[ -n "$EXTRA" ]]; then
|
||||
echo "✗ fixtures/cli-sync/ contains committed snapshot files:" >&2
|
||||
echo "$EXTRA" | sed 's/^/ /' >&2
|
||||
echo >&2
|
||||
echo " Run fixtures/snapshot.sh against an empty workspace or" >&2
|
||||
echo " remove the files before merging." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✓ fixtures/cli-sync/ is clean"
|
||||
@@ -0,0 +1,15 @@
|
||||
defaultTs: bun
|
||||
includes:
|
||||
- f/**
|
||||
excludes: []
|
||||
skipVariables: false
|
||||
skipResources: false
|
||||
skipResourceTypes: false
|
||||
skipSecrets: true
|
||||
includeSchedules: true
|
||||
includeTriggers: true
|
||||
includeUsers: false
|
||||
includeGroups: false
|
||||
includeSettings: false
|
||||
includeKey: false
|
||||
syncBehavior: v1
|
||||
Executable
+102
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env bash
|
||||
# Load fixtures/cli-sync/ into a fresh workspace on a local Windmill instance.
|
||||
#
|
||||
# Requires: bun on PATH. No `wmill` install needed — we invoke
|
||||
# cli/src/main.ts directly via `bun run`.
|
||||
#
|
||||
# Assumes a Windmill backend running at http://localhost:8000 with the
|
||||
# default super-admin (admin@windmill.dev / changeme). Override via flags.
|
||||
set -euo pipefail
|
||||
|
||||
BASE_URL="http://localhost:8000"
|
||||
EMAIL="admin@windmill.dev"
|
||||
# Prefer WMILL_PASSWORD env var over --password flag — flags leak into
|
||||
# /proc/<pid>/cmdline and shell history.
|
||||
PASSWORD="${WMILL_PASSWORD:-changeme}"
|
||||
WORKSPACE=""
|
||||
DIR=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--base-url) BASE_URL="$2"; shift 2 ;;
|
||||
--email) EMAIL="$2"; shift 2 ;;
|
||||
--password) PASSWORD="$2"; shift 2 ;;
|
||||
--workspace) WORKSPACE="$2"; shift 2 ;;
|
||||
--dir) DIR="$2"; shift 2 ;;
|
||||
-h|--help)
|
||||
sed -n '2,8p' "$0"; exit 0 ;;
|
||||
*) echo "Unknown flag: $1" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
DIR="${DIR:-$SCRIPT_DIR/cli-sync}"
|
||||
DIR="$(cd "$DIR" && pwd)"
|
||||
CLI_ENTRY="$REPO_ROOT/cli/src/main.ts"
|
||||
|
||||
if ! command -v bun >/dev/null 2>&1; then
|
||||
echo "✗ bun is required but not found on PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f "$CLI_ENTRY" ]]; then
|
||||
echo "✗ Cannot find CLI entry at $CLI_ENTRY" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f "$DIR/wmill.yaml" ]]; then
|
||||
echo "✗ No wmill.yaml in $DIR — is the fixture folder set up?" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$WORKSPACE" ]]; then
|
||||
# Use $RANDOM rather than piping /dev/urandom through head -c, which
|
||||
# SIGPIPEs `tr` and aborts the script under `set -o pipefail`.
|
||||
printf -v WORKSPACE 'fixture-%04x%04x' $RANDOM $RANDOM
|
||||
fi
|
||||
|
||||
# JSON body builder — interpolation via printf '%s' is unsafe for arbitrary
|
||||
# emails / passwords / workspace ids. Python is universal enough for a dev
|
||||
# script and produces correctly escaped JSON.
|
||||
json_object() {
|
||||
python3 -c '
|
||||
import json, sys
|
||||
print(json.dumps(dict(zip(sys.argv[1::2], sys.argv[2::2]))))
|
||||
' "$@"
|
||||
}
|
||||
|
||||
echo "→ Logging in as $EMAIL on $BASE_URL"
|
||||
TOKEN="$(curl -sS -f -X POST "$BASE_URL/api/auth/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$(json_object email "$EMAIL" password "$PASSWORD")")"
|
||||
if [[ -z "$TOKEN" ]]; then
|
||||
echo "✗ Login failed (empty token)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "→ Creating workspace '$WORKSPACE'"
|
||||
CREATE_OUT="$(mktemp)"
|
||||
trap 'rm -f "$CREATE_OUT"' EXIT
|
||||
HTTP_CODE="$(curl -sS -o "$CREATE_OUT" -w '%{http_code}' \
|
||||
-X POST "$BASE_URL/api/workspaces/create" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$(json_object id "$WORKSPACE" name "$WORKSPACE")")"
|
||||
if [[ "$HTTP_CODE" != "200" && "$HTTP_CODE" != "201" ]]; then
|
||||
echo "✗ Workspace creation failed (HTTP $HTTP_CODE):" >&2
|
||||
cat "$CREATE_OUT" >&2
|
||||
echo >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "→ Pushing $DIR to workspace '$WORKSPACE'"
|
||||
(
|
||||
cd "$DIR"
|
||||
bun run "$CLI_ENTRY" sync push --yes \
|
||||
--base-url "$BASE_URL" \
|
||||
--workspace "$WORKSPACE" \
|
||||
--token "$TOKEN"
|
||||
)
|
||||
|
||||
echo
|
||||
echo "✓ Fixture loaded into workspace '$WORKSPACE'"
|
||||
echo " Open: ${BASE_URL%/}/?workspace=$WORKSPACE"
|
||||
Executable
+100
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env bash
|
||||
# Snapshot a workspace into fixtures/cli-sync/ so it can be committed
|
||||
# alongside a PR.
|
||||
#
|
||||
# Requires: bun on PATH. No `wmill` install needed.
|
||||
#
|
||||
# Assumes a Windmill backend running at http://localhost:8000 with the
|
||||
# default super-admin (admin@windmill.dev / changeme). Override via flags.
|
||||
set -euo pipefail
|
||||
|
||||
BASE_URL="http://localhost:8000"
|
||||
EMAIL="admin@windmill.dev"
|
||||
# Prefer WMILL_PASSWORD env var over --password flag — flags leak into
|
||||
# /proc/<pid>/cmdline and shell history.
|
||||
PASSWORD="${WMILL_PASSWORD:-changeme}"
|
||||
DIR=""
|
||||
WORKSPACE=""
|
||||
|
||||
if [[ $# -lt 1 || "$1" == "-h" || "$1" == "--help" ]]; then
|
||||
sed -n '2,8p' "$0"
|
||||
echo
|
||||
echo "Usage: $(basename "$0") <workspace-id> [--base-url URL] [--email E] [--password P] [--dir PATH]"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
WORKSPACE="$1"; shift
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--base-url) BASE_URL="$2"; shift 2 ;;
|
||||
--email) EMAIL="$2"; shift 2 ;;
|
||||
--password) PASSWORD="$2"; shift 2 ;;
|
||||
--dir) DIR="$2"; shift 2 ;;
|
||||
*) echo "Unknown flag: $1" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
DIR="${DIR:-$SCRIPT_DIR/cli-sync}"
|
||||
DIR="$(cd "$DIR" && pwd)"
|
||||
CLI_ENTRY="$REPO_ROOT/cli/src/main.ts"
|
||||
|
||||
if ! command -v bun >/dev/null 2>&1; then
|
||||
echo "✗ bun is required but not found on PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f "$CLI_ENTRY" ]]; then
|
||||
echo "✗ Cannot find CLI entry at $CLI_ENTRY" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f "$DIR/wmill.yaml" ]]; then
|
||||
echo "✗ No wmill.yaml in $DIR — is the fixture folder set up?" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# JSON body builder — interpolation via printf '%s' is unsafe for arbitrary
|
||||
# emails / passwords. Python is universal enough for a dev script and
|
||||
# produces correctly escaped JSON.
|
||||
json_object() {
|
||||
python3 -c '
|
||||
import json, sys
|
||||
print(json.dumps(dict(zip(sys.argv[1::2], sys.argv[2::2]))))
|
||||
' "$@"
|
||||
}
|
||||
|
||||
echo "→ Logging in as $EMAIL on $BASE_URL"
|
||||
TOKEN="$(curl -sS -f -X POST "$BASE_URL/api/auth/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$(json_object email "$EMAIL" password "$PASSWORD")")"
|
||||
if [[ -z "$TOKEN" ]]; then
|
||||
echo "✗ Login failed (empty token)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Clear previous snapshot content while preserving the fixture scaffold
|
||||
# (wmill.yaml, .gitkeep). Anything else is removed so the snapshot reflects
|
||||
# exactly what is in the workspace.
|
||||
echo "→ Clearing previous snapshot in $DIR"
|
||||
(
|
||||
cd "$DIR"
|
||||
find . -mindepth 1 -maxdepth 1 \
|
||||
! -name 'wmill.yaml' \
|
||||
! -name '.gitkeep' \
|
||||
-exec rm -rf {} +
|
||||
)
|
||||
|
||||
echo "→ Pulling workspace '$WORKSPACE' into $DIR"
|
||||
(
|
||||
cd "$DIR"
|
||||
bun run "$CLI_ENTRY" sync pull --yes \
|
||||
--base-url "$BASE_URL" \
|
||||
--workspace "$WORKSPACE" \
|
||||
--token "$TOKEN"
|
||||
)
|
||||
|
||||
echo
|
||||
echo "✓ Snapshot of '$WORKSPACE' written to $DIR"
|
||||
echo " Commit the changes to share with reviewers. Run fixtures/check-empty.sh"
|
||||
echo " to verify the dir is empty again before merging."
|
||||
Reference in New Issue
Block a user