mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 20:35:39 -06:00
fix(install): --json preserves valid NDJSON for dynamic values (#128682)
* fix(install): preserve valid NDJSON for dynamic values * fix(install): satisfy installer shell checks
This commit is contained in:
+97
-49
@@ -161,10 +161,10 @@ cleanup_legacy_submodules() {
|
||||
local repo_dir="${1:-${OPENCLAW_GIT_DIR:-${OPENCLAW_EFFECTIVE_HOME}/openclaw}}"
|
||||
local legacy_dir="${repo_dir}/Peekaboo"
|
||||
if [[ -d "$legacy_dir" ]]; then
|
||||
emit_json "{\"event\":\"step\",\"name\":\"legacy-submodule\",\"status\":\"start\",\"path\":\"${legacy_dir//\"/\\\"}\"}"
|
||||
emit_json step name legacy-submodule status start path "$legacy_dir"
|
||||
log "Removing legacy submodule checkout: ${legacy_dir}"
|
||||
rm -rf "$legacy_dir"
|
||||
emit_json "{\"event\":\"step\",\"name\":\"legacy-submodule\",\"status\":\"ok\",\"path\":\"${legacy_dir//\"/\\\"}\"}"
|
||||
emit_json step name legacy-submodule status ok path "$legacy_dir"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -185,15 +185,63 @@ sha256_file() {
|
||||
fail "Missing sha256 tool (need sha256sum, shasum, or openssl)"
|
||||
}
|
||||
|
||||
JSON_STRING=""
|
||||
quote_json_string() {
|
||||
local value="${1:-}"
|
||||
local char code escaped index
|
||||
# Byte iteration escapes every C0 control without rewriting valid UTF-8 bytes.
|
||||
# The final emitter can therefore guarantee one physical line per event.
|
||||
local LC_ALL=C
|
||||
|
||||
JSON_STRING='"'
|
||||
for ((index = 0; index < ${#value}; index++)); do
|
||||
char="${value:index:1}"
|
||||
case "$char" in
|
||||
'"') JSON_STRING+='\"' ;;
|
||||
\\) JSON_STRING+="\\\\" ;;
|
||||
*)
|
||||
printf -v code '%d' "'$char"
|
||||
if ((code < 32)); then
|
||||
printf -v escaped '\\u%04x' "$code"
|
||||
JSON_STRING+="$escaped"
|
||||
else
|
||||
JSON_STRING+="$char"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
JSON_STRING+='"'
|
||||
}
|
||||
|
||||
emit_json() {
|
||||
if [[ "$JSON" -eq 1 ]]; then
|
||||
printf '%s\n' "$1"
|
||||
if [[ "$JSON" -ne 1 ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local event="$1"
|
||||
local key value output
|
||||
shift
|
||||
quote_json_string "$event"
|
||||
output="{\"event\":${JSON_STRING}"
|
||||
# `ok` is the installer's only non-string event field and belongs to done.
|
||||
if [[ "$event" == "done" ]]; then
|
||||
output+=',"ok":true'
|
||||
fi
|
||||
while [[ $# -gt 0 ]]; do
|
||||
key="$1"
|
||||
value="$2"
|
||||
shift 2
|
||||
quote_json_string "$key"
|
||||
output+=",${JSON_STRING}:"
|
||||
quote_json_string "$value"
|
||||
output+="$JSON_STRING"
|
||||
done
|
||||
printf '%s}\n' "$output"
|
||||
}
|
||||
|
||||
fail() {
|
||||
local msg="$1"
|
||||
emit_json "{\"event\":\"error\",\"message\":\"${msg//\"/\\\"}\"}"
|
||||
emit_json error message "$msg"
|
||||
log "ERROR: $msg"
|
||||
exit 1
|
||||
}
|
||||
@@ -220,7 +268,7 @@ preflight_fresh_git_disk_space() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
emit_json "{\"event\":\"step\",\"name\":\"disk-space\",\"status\":\"start\"}"
|
||||
emit_json step name disk-space status start
|
||||
ancestor="$repo_dir"
|
||||
while [[ ! -e "$ancestor" ]]; do
|
||||
local parent
|
||||
@@ -236,14 +284,14 @@ preflight_fresh_git_disk_space() {
|
||||
|
||||
available_kib="$(available_disk_kib "$ancestor")"
|
||||
if [[ ! "$available_kib" =~ ^[0-9]+$ ]]; then
|
||||
emit_json "{\"event\":\"step\",\"name\":\"disk-space\",\"status\":\"warn\",\"reason\":\"unreadable\"}"
|
||||
emit_json step name disk-space status warn reason unreadable
|
||||
return 0
|
||||
fi
|
||||
if ((available_kib < FRESH_GIT_MIN_FREE_KIB)); then
|
||||
available_gib="$(awk -v kib="$available_kib" 'BEGIN { printf "%.1f", kib / 1048576 }')"
|
||||
fail "Fresh Git installs require at least 6 GiB of free disk space; only ${available_gib} GiB is available. Free disk space and retry."
|
||||
fi
|
||||
emit_json "{\"event\":\"step\",\"name\":\"disk-space\",\"status\":\"ok\"}"
|
||||
emit_json step name disk-space status ok
|
||||
}
|
||||
|
||||
has_sudo() {
|
||||
@@ -256,11 +304,11 @@ is_root() {
|
||||
|
||||
ensure_git() {
|
||||
if command -v git >/dev/null 2>&1; then
|
||||
emit_json '{"event":"step","name":"git","status":"ok"}'
|
||||
emit_json step name git status ok
|
||||
return
|
||||
fi
|
||||
|
||||
emit_json '{"event":"step","name":"git","status":"start"}'
|
||||
emit_json step name git status start
|
||||
log "Installing Git (required for npm installs)..."
|
||||
|
||||
case "$(os_detect)" in
|
||||
@@ -316,7 +364,7 @@ ensure_git() {
|
||||
fail "Git install failed. Install git manually and retry."
|
||||
fi
|
||||
|
||||
emit_json '{"event":"step","name":"git","status":"ok"}'
|
||||
emit_json step name git status ok
|
||||
}
|
||||
|
||||
parse_args() {
|
||||
@@ -688,10 +736,10 @@ install_alpine_node() {
|
||||
local required_version
|
||||
local sqlite_version
|
||||
|
||||
emit_json "{\"event\":\"step\",\"name\":\"node\",\"status\":\"start\",\"method\":\"apk\"}"
|
||||
emit_json step name node status start method apk
|
||||
if try_link_usable_node_runtime_from_path; then
|
||||
installed_version="$("$(node_bin)" -v 2>/dev/null || echo unknown)"
|
||||
emit_json "{\"event\":\"step\",\"name\":\"node\",\"status\":\"ok\",\"method\":\"system\",\"version\":\"${installed_version}\"}"
|
||||
emit_json step name node status ok method system version "$installed_version"
|
||||
return
|
||||
fi
|
||||
|
||||
@@ -718,7 +766,7 @@ install_alpine_node() {
|
||||
fi
|
||||
|
||||
installed_version="$("$(node_bin)" -v 2>/dev/null || echo unknown)"
|
||||
emit_json "{\"event\":\"step\",\"name\":\"node\",\"status\":\"ok\",\"method\":\"apk\",\"version\":\"${installed_version}\"}"
|
||||
emit_json step name node status ok method apk version "$installed_version"
|
||||
}
|
||||
|
||||
set_pnpm_cmd() {
|
||||
@@ -1090,11 +1138,11 @@ install_node() {
|
||||
fi
|
||||
|
||||
if linked_node_is_usable; then
|
||||
emit_json "{\"event\":\"step\",\"name\":\"node\",\"status\":\"skip\",\"path\":\"${dir//\"/\\\\\\\"}\"}"
|
||||
emit_json step name node status skip path "$dir"
|
||||
return
|
||||
fi
|
||||
|
||||
emit_json "{\"event\":\"step\",\"name\":\"node\",\"status\":\"start\",\"version\":\"${NODE_VERSION}\"}"
|
||||
emit_json step name node status start version "$NODE_VERSION"
|
||||
log "Installing Node ${NODE_VERSION} (user-space)..."
|
||||
|
||||
mkdir -p "${PREFIX}/tools"
|
||||
@@ -1135,7 +1183,7 @@ install_node() {
|
||||
sqlite_version="$(linked_node_sqlite_version)"
|
||||
fail "Installed Node ${NODE_VERSION} must provide Node >= ${required_version} with WAL-reset-safe SQLite; found Node ${installed_version}, SQLite ${sqlite_version}. Re-run with --node-version 24.19.0 (or newer)"
|
||||
fi
|
||||
emit_json "{\"event\":\"step\",\"name\":\"node\",\"status\":\"ok\",\"version\":\"${NODE_VERSION}\"}"
|
||||
emit_json step name node status ok version "$NODE_VERSION"
|
||||
}
|
||||
|
||||
ensure_pnpm() {
|
||||
@@ -1149,27 +1197,27 @@ ensure_pnpm() {
|
||||
fi
|
||||
|
||||
if [[ -x "$(node_dir)/bin/corepack" ]]; then
|
||||
emit_json "{\"event\":\"step\",\"name\":\"pnpm\",\"status\":\"start\",\"method\":\"corepack\"}"
|
||||
emit_json step name pnpm status start method corepack
|
||||
log "Installing pnpm via Corepack..."
|
||||
"$(node_dir)/bin/corepack" enable >/dev/null 2>&1 || true
|
||||
# Corepack downloads fail hard on npm registry key rotation (its bundled
|
||||
# signature set goes stale); the npm fallback below must stay reachable.
|
||||
if "$(node_dir)/bin/corepack" prepare pnpm@11 --activate; then
|
||||
if detect_pnpm_cmd && pnpm_cmd_is_ready && [[ "$("${PNPM_CMD[@]}" --version 2>/dev/null || true)" =~ ^11\. ]]; then
|
||||
emit_json "{\"event\":\"step\",\"name\":\"pnpm\",\"status\":\"ok\"}"
|
||||
emit_json step name pnpm status ok
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
emit_json "{\"event\":\"step\",\"name\":\"pnpm\",\"status\":\"warn\",\"reason\":\"corepack-failed\"}"
|
||||
emit_json step name pnpm status warn reason corepack-failed
|
||||
log "Corepack could not provision pnpm; falling back to npm."
|
||||
fi
|
||||
fi
|
||||
|
||||
emit_json "{\"event\":\"step\",\"name\":\"pnpm\",\"status\":\"start\",\"method\":\"npm\"}"
|
||||
emit_json step name pnpm status start method npm
|
||||
log "Installing pnpm via npm..."
|
||||
"$(npm_bin)" install -g --prefix "$PREFIX" pnpm@11
|
||||
detect_pnpm_cmd || true
|
||||
emit_json "{\"event\":\"step\",\"name\":\"pnpm\",\"status\":\"ok\"}"
|
||||
emit_json step name pnpm status ok
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -1201,7 +1249,7 @@ fix_npm_prefix_if_needed() {
|
||||
done
|
||||
|
||||
export PATH="${target}/bin:${PATH}"
|
||||
emit_json "{\"event\":\"step\",\"name\":\"npm-prefix\",\"status\":\"ok\",\"prefix\":\"${target//\"/\\\"}\"}"
|
||||
emit_json step name npm-prefix status ok prefix "$target"
|
||||
log "Configured npm prefix to ${target}"
|
||||
}
|
||||
|
||||
@@ -1376,7 +1424,7 @@ install_openclaw() {
|
||||
npm_cmd="$(npm_bin)"
|
||||
local npm_cwd="$PWD"
|
||||
lifecycle_arg="$(npm_lifecycle_allow_arg "$npm_cmd" "$install_spec" "$npm_cwd")" || return 1
|
||||
emit_json "{\"event\":\"step\",\"name\":\"openclaw\",\"status\":\"start\",\"version\":\"${requested}\"}"
|
||||
emit_json step name openclaw status start version "$requested"
|
||||
log "Installing OpenClaw (${requested})..."
|
||||
if [[ "$SET_NPM_PREFIX" -eq 1 ]]; then
|
||||
fix_npm_prefix_if_needed
|
||||
@@ -1391,7 +1439,7 @@ install_openclaw() {
|
||||
if ! env -u NPM_CONFIG_BEFORE -u npm_config_before -u NPM_CONFIG_MIN_RELEASE_AGE -u npm_config_min_release_age -u npm_config_min-release-age "$npm_cmd" "${npm_install_args[@]}" || [[ ! -f "$installed_entry" || -e "$install_guard" ]]; then
|
||||
log "npm install openclaw@${resolved_requested} did not produce a usable package; retrying once"
|
||||
if ! env -u NPM_CONFIG_BEFORE -u npm_config_before -u NPM_CONFIG_MIN_RELEASE_AGE -u npm_config_min_release_age -u npm_config_min-release-age "$npm_cmd" "${npm_install_args[@]}" || [[ ! -f "$installed_entry" || -e "$install_guard" ]]; then
|
||||
emit_json '{"event":"error","message":"npm install did not produce a usable OpenClaw package"}'
|
||||
emit_json error message "npm install did not produce a usable OpenClaw package"
|
||||
log "ERROR: npm install did not produce a usable OpenClaw package"
|
||||
return 1
|
||||
fi
|
||||
@@ -1403,7 +1451,7 @@ install_openclaw() {
|
||||
set -euo pipefail
|
||||
exec "${PREFIX}/tools/node/bin/node" "$(node_dir)/lib/node_modules/openclaw/dist/entry.js" "\$@"
|
||||
EOF
|
||||
emit_json "{\"event\":\"step\",\"name\":\"openclaw\",\"status\":\"ok\",\"version\":\"${requested}\"}"
|
||||
emit_json step name openclaw status ok version "$requested"
|
||||
}
|
||||
|
||||
ensure_pnpm_git_prepare_allowlist() {
|
||||
@@ -1534,18 +1582,18 @@ install_openclaw_from_git() {
|
||||
repo_dir="$(cd "$(dirname "$repo_dir")" && pwd -P)/$(basename "$repo_dir")"
|
||||
fi
|
||||
|
||||
emit_json "{\"event\":\"step\",\"name\":\"openclaw\",\"status\":\"start\",\"method\":\"git\",\"repo\":\"${repo_url//\"/\\\"}\"}"
|
||||
emit_json step name openclaw status start method git repo "$repo_url"
|
||||
if [[ -d "$repo_dir/.git" ]]; then
|
||||
log "Installing Openclaw from git checkout: ${repo_dir}"
|
||||
else
|
||||
log "Installing Openclaw from GitHub (${repo_url})..."
|
||||
fi
|
||||
|
||||
emit_json '{"event":"step","name":"git-tools","status":"start"}'
|
||||
emit_json step name git-tools status start
|
||||
ensure_git
|
||||
ensure_pnpm
|
||||
ensure_pnpm_binary_for_scripts
|
||||
emit_json '{"event":"step","name":"git-tools","status":"ok"}'
|
||||
emit_json step name git-tools status ok
|
||||
|
||||
if [[ -d "$repo_dir/.git" ]] &&
|
||||
! git --git-dir="$repo_dir/.git" --work-tree="$repo_dir" rev-parse --verify --quiet 'HEAD^{commit}' >/dev/null 2>&1; then
|
||||
@@ -1556,17 +1604,17 @@ install_openclaw_from_git() {
|
||||
:
|
||||
elif [[ -d "$repo_dir" ]]; then
|
||||
if [[ -z "$(ls -A "$repo_dir" 2>/dev/null || true)" ]]; then
|
||||
emit_json '{"event":"step","name":"git-clone","status":"start"}'
|
||||
emit_json step name git-clone status start
|
||||
clone_git_checkout_transactionally "$repo_url" "$repo_dir"
|
||||
emit_json '{"event":"step","name":"git-clone","status":"ok"}'
|
||||
emit_json step name git-clone status ok
|
||||
fresh_checkout=1
|
||||
else
|
||||
fail "Git install dir exists but is not a git repo: ${repo_dir}"
|
||||
fi
|
||||
else
|
||||
emit_json '{"event":"step","name":"git-clone","status":"start"}'
|
||||
emit_json step name git-clone status start
|
||||
clone_git_checkout_transactionally "$repo_url" "$repo_dir"
|
||||
emit_json '{"event":"step","name":"git-clone","status":"ok"}'
|
||||
emit_json step name git-clone status ok
|
||||
fresh_checkout=1
|
||||
fi
|
||||
|
||||
@@ -1575,15 +1623,15 @@ install_openclaw_from_git() {
|
||||
if [[ -z "$(git -C "$repo_dir" status --porcelain 2>/dev/null || true)" ]]; then
|
||||
log "Using git ref: ${git_ref}"
|
||||
if [[ "$fresh_checkout" -eq 0 ]]; then
|
||||
emit_json '{"event":"step","name":"git-update","status":"start"}'
|
||||
emit_json step name git-update status start
|
||||
fi
|
||||
checkout_git_openclaw_ref "$repo_dir" "$git_ref"
|
||||
if [[ "$fresh_checkout" -eq 0 ]]; then
|
||||
emit_json '{"event":"step","name":"git-update","status":"ok"}'
|
||||
emit_json step name git-update status ok
|
||||
fi
|
||||
else
|
||||
log "Repo is dirty; skipping git checkout/update"
|
||||
emit_json '{"event":"step","name":"git-update","status":"warn","reason":"dirty"}'
|
||||
emit_json step name git-update status warn reason dirty
|
||||
fi
|
||||
|
||||
if [[ -n "${REQUIRED_COMPATIBLE_VERSION:-}" ]]; then
|
||||
@@ -1601,20 +1649,20 @@ install_openclaw_from_git() {
|
||||
|
||||
local install_lockfile_flag
|
||||
install_lockfile_flag="$(git_install_lockfile_flag "$repo_dir" "$git_ref")"
|
||||
emit_json '{"event":"step","name":"dependencies","status":"start"}'
|
||||
emit_json step name dependencies status start
|
||||
CI="${CI:-true}" run_pnpm -C "$repo_dir" install "$install_lockfile_flag"
|
||||
emit_json '{"event":"step","name":"dependencies","status":"ok"}'
|
||||
emit_json step name dependencies status ok
|
||||
|
||||
emit_json '{"event":"step","name":"control-ui","status":"start"}'
|
||||
emit_json step name control-ui status start
|
||||
if ! run_pnpm -C "$repo_dir" ui:build; then
|
||||
log "UI build failed; continuing (CLI may still work)"
|
||||
emit_json '{"event":"step","name":"control-ui","status":"warn"}'
|
||||
emit_json step name control-ui status warn
|
||||
else
|
||||
emit_json '{"event":"step","name":"control-ui","status":"ok"}'
|
||||
emit_json step name control-ui status ok
|
||||
fi
|
||||
emit_json '{"event":"step","name":"cli-build","status":"start"}'
|
||||
emit_json step name cli-build status start
|
||||
run_pnpm -C "$repo_dir" build
|
||||
emit_json '{"event":"step","name":"cli-build","status":"ok"}'
|
||||
emit_json step name cli-build status ok
|
||||
|
||||
mkdir -p "${PREFIX}/bin"
|
||||
publish_executable_wrapper "${PREFIX}/bin/openclaw" <<EOF
|
||||
@@ -1622,7 +1670,7 @@ install_openclaw_from_git() {
|
||||
set -euo pipefail
|
||||
exec "${PREFIX}/tools/node/bin/node" "${repo_dir}/dist/entry.js" "\$@"
|
||||
EOF
|
||||
emit_json "{\"event\":\"step\",\"name\":\"openclaw\",\"status\":\"ok\",\"method\":\"git\"}"
|
||||
emit_json step name openclaw status ok method git
|
||||
}
|
||||
|
||||
is_gateway_daemon_loaded() {
|
||||
@@ -1670,15 +1718,15 @@ refresh_gateway_service_if_loaded() {
|
||||
fi
|
||||
|
||||
if ! is_gateway_daemon_loaded "$claw"; then
|
||||
emit_json '{"event":"step","name":"gateway-service","status":"skip","reason":"not-loaded"}'
|
||||
emit_json step name gateway-service status skip reason not-loaded
|
||||
return 0
|
||||
fi
|
||||
|
||||
emit_json '{"event":"step","name":"gateway-service","status":"start"}'
|
||||
emit_json step name gateway-service status start
|
||||
log "Refreshing loaded gateway service..."
|
||||
|
||||
if ! "$claw" gateway install --force >/dev/null 2>&1; then
|
||||
emit_json '{"event":"step","name":"gateway-service","status":"warn","reason":"install-failed"}'
|
||||
emit_json step name gateway-service status warn reason install-failed
|
||||
log "Warning: gateway service refresh failed; continuing."
|
||||
return 0
|
||||
fi
|
||||
@@ -1686,7 +1734,7 @@ refresh_gateway_service_if_loaded() {
|
||||
# `gateway install --force` activates the replacement service. A second
|
||||
# restart can kill startup migrations and strand their lock until expiry.
|
||||
"$claw" gateway status --probe --json >/dev/null 2>&1 || true
|
||||
emit_json '{"event":"step","name":"gateway-service","status":"ok"}'
|
||||
emit_json step name gateway-service status ok
|
||||
}
|
||||
|
||||
main() {
|
||||
@@ -1727,7 +1775,7 @@ main() {
|
||||
commit_wrapper_backup
|
||||
|
||||
refresh_gateway_service_if_loaded
|
||||
emit_json "{\"event\":\"done\",\"ok\":true,\"version\":\"${installed_version//\"/\\\"}\"}"
|
||||
emit_json "done" version "$installed_version"
|
||||
log "OpenClaw installed (${installed_version})."
|
||||
|
||||
if [[ "$RUN_ONBOARD" -eq 1 ]]; then
|
||||
|
||||
@@ -207,6 +207,62 @@ describe("install-cli.sh", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("round-trips dynamic installer values through independent NDJSON records", () => {
|
||||
const root = tempDirs.make("openclaw-install-cli-json-events-");
|
||||
const dynamicValue = `quote"\\lobster🦞${String.fromCharCode(
|
||||
...Array.from({ length: 31 }, (_, index) => index + 1),
|
||||
)}end`;
|
||||
const repo = join(root, dynamicValue);
|
||||
const legacyDir = join(repo, "Peekaboo");
|
||||
const fakeNode = join(root, "node");
|
||||
mkdirSync(legacyDir, { recursive: true });
|
||||
writeFileSync(fakeNode, '#!/bin/bash\nprintf "%s" "$EVENT_VALUE"\n');
|
||||
chmodSync(fakeNode, 0o755);
|
||||
|
||||
const success = runInstallCliShell(
|
||||
[
|
||||
"set -euo pipefail",
|
||||
`cd ${JSON.stringify(process.cwd())}`,
|
||||
`source ${JSON.stringify(SCRIPT_PATH)}`,
|
||||
"JSON=1",
|
||||
'cleanup_legacy_submodules "$REPO"',
|
||||
"try_link_usable_node_runtime_from_path() { return 0; }",
|
||||
`node_bin() { printf '%s\\n' ${JSON.stringify(fakeNode)}; }`,
|
||||
"install_alpine_node",
|
||||
'emit_json done version "$EVENT_VALUE"',
|
||||
].join("\n"),
|
||||
{ EVENT_VALUE: dynamicValue, REPO: repo },
|
||||
);
|
||||
|
||||
expect(success.status, success.stderr || success.stdout).toBe(0);
|
||||
expect(existsSync(legacyDir)).toBe(false);
|
||||
const successLines = success.stdout.trimEnd().split("\n");
|
||||
expect(successLines).toHaveLength(5);
|
||||
const successEvents = successLines.map((line) => JSON.parse(line) as Record<string, unknown>);
|
||||
expect(successEvents).toEqual([
|
||||
{ event: "step", name: "legacy-submodule", status: "start", path: legacyDir },
|
||||
{ event: "step", name: "legacy-submodule", status: "ok", path: legacyDir },
|
||||
{ event: "step", name: "node", status: "start", method: "apk" },
|
||||
{ event: "step", name: "node", status: "ok", method: "system", version: dynamicValue },
|
||||
{ event: "done", ok: true, version: dynamicValue },
|
||||
]);
|
||||
|
||||
const failure = runInstallCliShell(
|
||||
[
|
||||
"set -euo pipefail",
|
||||
`cd ${JSON.stringify(process.cwd())}`,
|
||||
`source ${JSON.stringify(SCRIPT_PATH)}`,
|
||||
"JSON=1",
|
||||
'fail "$EVENT_VALUE"',
|
||||
].join("\n"),
|
||||
{ EVENT_VALUE: dynamicValue },
|
||||
);
|
||||
|
||||
expect(failure.status).toBe(1);
|
||||
expect(failure.stdout.trimEnd().split("\n")).toHaveLength(1);
|
||||
expect(JSON.parse(failure.stdout)).toEqual({ event: "error", message: dynamicValue });
|
||||
});
|
||||
|
||||
it("rejects a git checkout without a commit before updating it", () => {
|
||||
const result = runInstallCliShell(`
|
||||
set -euo pipefail
|
||||
|
||||
Reference in New Issue
Block a user