mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 17:53:39 +00:00
* fix(scripts): use system Bash for macOS tooling and owned Mach-O fixtures Pin native entrypoints and package commands to /bin/bash, guard portable heredoc callers on Darwin, and preserve Bash 3.2 boolean parsing. Streamed installers explain how to use system Bash when their input cannot be replayed. Generate deterministic x86_64, arm64, and arm64e framework fixtures instead of borrowing /bin/ls. Preserve the existing framework pipeline repair from #141056 and verify merged slice bytes. * fix(scripts): keep guarded portable scripts bash 3.2 compatible * fix(scripts): keep macOS Bash CI coverage green Distinguish sourced installer returns from stdin exits without ShellCheck unreachable-code warnings. Retain the shebang regression suite in changed-target routing, and repartition hosted tooling tails toward 50-second groups within the existing 150-second budget and 80-job cap. Validation: 635 interpreter and routing tests plus 53 planner tests passed; ShellCheck, targeted lint, formatting, and fresh Codex review passed. The broader local changed-file check hit an unrelated existing dependency graph crossing through extensions/reef/node_modules/@noble/hashes; exact-head hosted CI remains required. * docs(install): use system Bash in install and recovery commands Align macOS-facing copy-and-paste commands and emitted installer guidance with the supported streamed interpreter. This addresses the remaining installer-command review finding without changing the PR body. Validation: streamed help for both installers, install.sh dry-run, 16 selected fresh-install and upgrade lifecycle tests, formatting, diff check, and fresh Codex review passed. Landing remains blocked by unrelated provider-transport integration CI failure caused by an unchanged incomplete plugin-registry mock. * fix(scripts): preserve streamed installs and CI packing Keep public installer commands portable while replaying Darwin Bash 5.3+ stdin under system Bash through an immediately unlinked private temp file. Retain actionable sourced-install rejection and the SC2317-safe check. Restore the original CI packing policy and move the Bash policy scan into its existing macOS tooling owner without adding a routed test file. Validation: real Homebrew Bash streamed help and cleanup; 642 scan/routing tests; 23 selected installer tests under both PATH orders; planner cap and coverage tests; 139 Bash syntax checks; ShellCheck; 1,135 changed-gate tests; focused lint/changed-check repair; fresh Codex review with no P0/P1 findings.
156 lines
6.8 KiB
Bash
Executable file
156 lines
6.8 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Bash 5.3+ can deadlock writing heredoc pipes on macOS before the reader starts.
|
|
if [[ ${OSTYPE:-} == darwin* && $BASH != /bin/bash ]] && ((BASH_VERSINFO[0] > 5 || (BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 3))); then
|
|
exec /bin/bash "$0" "$@"
|
|
fi
|
|
# Updates a self-hosted OpenClaw gateway that runs from this source checkout.
|
|
#
|
|
# Reference workflow for team-operated servers (see docs/install/updating.md).
|
|
# Simple installs should prefer `openclaw update` / `openclaw update --channel
|
|
# dev`; this script exists for checkouts that additionally need to:
|
|
# - preserve a local branch by rebasing it onto origin/main,
|
|
# - refuse all tracked local changes, including build outputs,
|
|
# - build clean (incremental builds have shipped stale hashed chunks),
|
|
# - restart a custom service unit.
|
|
#
|
|
# Environment:
|
|
# OPENCLAW_UPDATE_RESTART_CMD restart command (default: openclaw gateway restart)
|
|
# set to "" to skip the restart step
|
|
# OPENCLAW_UPDATE_REMOTE git remote to update from (default: origin)
|
|
set -euo pipefail
|
|
|
|
pnpm_dir=""
|
|
log() { echo "[update-gateway] $*"; }
|
|
on_exit() {
|
|
local code=$?
|
|
if [ -n "$pnpm_dir" ]; then rm -rf "$pnpm_dir"; fi
|
|
if [ "$code" -ne 0 ]; then
|
|
echo "[update-gateway] FAILED (exit $code)" >&2
|
|
fi
|
|
}
|
|
trap on_exit EXIT
|
|
|
|
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "$repo_root"
|
|
|
|
# Create private shims before fetching; target compatibility needs an invocation
|
|
# after fetch, before checkout mutation. Never activate a global version.
|
|
if ! command -v corepack >/dev/null 2>&1; then
|
|
log "Corepack is required. Install a Corepack version compatible with the target pnpm pin, then retry."
|
|
exit 1
|
|
fi
|
|
pnpm_dir="$(mktemp -d "${TMPDIR:-/tmp}/openclaw-pnpm.XXXXXX")"
|
|
if ! corepack enable --install-directory "$pnpm_dir" pnpm || [ ! -x "$pnpm_dir/pnpm" ]; then
|
|
log "Corepack could not create scoped pnpm shims. Repair Corepack before retrying; no Git update was attempted."
|
|
exit 1
|
|
fi
|
|
run_pnpm() (
|
|
# Source updates own downloads; do not wait for a Corepack terminal prompt.
|
|
export COREPACK_ENABLE_DOWNLOAD_PROMPT=0
|
|
export PATH="$pnpm_dir:$PATH"
|
|
export NPM_CONFIG_WORKSPACE_DIR="$PWD" npm_config_workspace_dir="$PWD"
|
|
export PNPM_CONFIG_LOCKFILE_DIR="$PWD" pnpm_config_lockfile_dir="$PWD"
|
|
"$pnpm_dir/pnpm" "$@"
|
|
)
|
|
|
|
remote="${OPENCLAW_UPDATE_REMOTE:-origin}"
|
|
|
|
# Never update over an in-progress git operation: aborting or rebasing on top
|
|
# of an operator's paused rebase/merge would discard their progress.
|
|
git_dir="$(git rev-parse --git-dir)"
|
|
if [ -d "$git_dir/rebase-merge" ] || [ -d "$git_dir/rebase-apply" ] || \
|
|
[ -f "$git_dir/MERGE_HEAD" ] || [ -f "$git_dir/CHERRY_PICK_HEAD" ]; then
|
|
log "a git rebase/merge/cherry-pick is in progress; finish or abort it first"
|
|
exit 1
|
|
fi
|
|
|
|
# Fail closed on any other local changes: an agent or operator may have
|
|
# uncommitted work in this checkout, and an update must never eat it.
|
|
if ! git diff --quiet || ! git diff --cached --quiet; then
|
|
log "working tree has local changes; commit, stash, or restore them first:"
|
|
status_lines="$(git status --short)"
|
|
head -20 <<<"$status_lines"
|
|
exit 1
|
|
fi
|
|
|
|
# dist, dist-runtime, and .artifacts/tsgo-cache are wholly disposable build
|
|
# outputs: build and check tooling regenerates them, tracked or not — never
|
|
# store anything there. Untracked files elsewhere are kept and only warned
|
|
# about (servers accumulate harmless scratch files). Accepted tradeoff: an
|
|
# untracked file a build tool happens to read stays in effect, same as before
|
|
# the update; operators own what they leave in the checkout.
|
|
untracked="$(git ls-files --others --exclude-standard)"
|
|
if [ -n "$untracked" ]; then
|
|
log "warning: untracked files present; they are kept and a build tool that reads them can affect the deployed output:"
|
|
head -10 <<<"$untracked"
|
|
fi
|
|
|
|
log "fetching ${remote}/main"
|
|
git fetch "$remote" main
|
|
|
|
# Probe only package-manager metadata, not target dependencies or hooks. Freeze
|
|
# the fetched commit so the probe and later checkout update cannot drift apart.
|
|
probe_dir="$pnpm_dir/probe"
|
|
if ! target_sha="$(git rev-parse --verify 'FETCH_HEAD^{commit}')" ||
|
|
! target_version="$(git show "$target_sha:package.json" | node -e '
|
|
const fs = require("node:fs");
|
|
const pin = JSON.parse(fs.readFileSync(0, "utf8")).packageManager;
|
|
const version = typeof pin === "string" && /^pnpm@(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)(?:\+[0-9A-Za-z.-]+)?$/.exec(pin);
|
|
if (!version) throw new Error("Fetched package.json must declare an exact pnpm packageManager pin");
|
|
const dir = process.argv[1];
|
|
fs.mkdirSync(dir);
|
|
fs.writeFileSync(dir + "/package.json", JSON.stringify({private: true, packageManager: pin}));
|
|
fs.writeFileSync(dir + "/pnpm-workspace.yaml", "packages: []\n");
|
|
process.stdout.write(version[1]);
|
|
' "$probe_dir")" ||
|
|
! probe_version="$(cd "$probe_dir" && run_pnpm --version)" ||
|
|
[ "$probe_version" != "$target_version" ]; then
|
|
log "Target pnpm preflight failed; no checkout update or restart was attempted. Verify the fetched packageManager pin and install a compatible Corepack, then retry."
|
|
exit 1
|
|
fi
|
|
|
|
branch="$(git rev-parse --abbrev-ref HEAD)"
|
|
if [ "$branch" = "main" ]; then
|
|
log "fast-forwarding main"
|
|
git merge --ff-only "$target_sha"
|
|
else
|
|
# A server may carry a local branch (e.g. an agent's in-progress fix) on top
|
|
# of main. Rebase preserves that work while still deploying latest main;
|
|
# --rebase-merges keeps merge commits (and their conflict resolutions)
|
|
# instead of silently flattening them away.
|
|
log "rebasing local branch '$branch' onto ${remote}/main"
|
|
if ! git rebase --rebase-merges "$target_sha"; then
|
|
git rebase --abort
|
|
log "rebase of '$branch' conflicts with ${remote}/main; resolve manually"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
log "installing dependencies"
|
|
run_pnpm install --frozen-lockfile
|
|
|
|
# Incremental builds have left stale hashed chunks and config validators from
|
|
# the previous revision in dist; a clean build is the reliable path.
|
|
log "clean building"
|
|
# These deletes must stay inside the checkout: a symlinked build dir would
|
|
# redirect the recursion into its target, so refuse symlinks outright.
|
|
for build_path in dist dist-runtime .artifacts; do
|
|
if [ -L "$build_path" ]; then
|
|
log "$build_path is a symlink; refusing to clean through it"
|
|
exit 1
|
|
fi
|
|
done
|
|
# The build owns cleanup under its checkout-local artifact lock. Deleting here
|
|
# would race declaration writers and readers before that ownership is acquired.
|
|
# Match CLI updates: build runtime artifacts unless declarations were explicitly requested.
|
|
OPENCLAW_UPDATE_IN_PROGRESS=1 run_pnpm build
|
|
|
|
restart_cmd="${OPENCLAW_UPDATE_RESTART_CMD-openclaw gateway restart}"
|
|
if [ -n "$restart_cmd" ]; then
|
|
log "restarting gateway: $restart_cmd"
|
|
bash -c "$restart_cmd"
|
|
else
|
|
log "restart skipped (OPENCLAW_UPDATE_RESTART_CMD is empty)"
|
|
fi
|
|
|
|
log "OK $(git rev-parse --short HEAD) ($branch)"
|