Files
openclaw/scripts/update-gateway.sh
Peter Steinberger b02d3e4707 feat(scripts): reference update script for source-checkout gateway servers (#112557)
* feat(scripts): reference update script for source-checkout gateway servers

Teams running a gateway from a git checkout kept re-deriving the same
update ritual by hand: restore the tracked bundle pnpm build rewrites,
fail closed on real local changes, fast-forward main or rebase a local
server branch, install deps, build clean (incremental builds have
shipped stale hashed chunks), restart the service. update-gateway.sh
encodes that as the documented reference; simple installs keep using
openclaw update --channel dev.

* fix(scripts): harden update-gateway guards and refresh docs map

Review findings: never run over a pre-existing rebase/merge/cherry-pick
(the abort path could discard operator progress), and treat untracked
files honestly — fail closed only where the clean build would delete
them, warn-list elsewhere. Regenerate docs_map for the new updating.md
section.

* fix(scripts): pipefail-safe warnings and honest disposable-dir policy

Review findings: head-truncating a long warning list through a pipe
dies under pipefail (SIGPIPE) in the warning path, so truncate via
here-strings; the untracked-under-build-dirs guard was theater since
ignored files bypassed it — declare dist/dist-runtime/.artifacts as
wholly disposable instead and say so; note the rm targets are
symlink-safe (no trailing slashes).

* fix(scripts): order guards before mutations and refuse symlinked build dirs

Review round: the in-progress-operation guard now precedes the bundle
restore (the script's first mutation), symlinked build dirs are refused
before the recursive clean (intermediate symlink resolution would
redirect the delete outside the checkout), and the untracked-files note
no longer overpromises — kept files can affect the build and the
message says so.

* fix(scripts): preserve merge commits when rebasing the server branch

Plain git rebase flattens merges, silently dropping merge-only conflict
resolutions from a local server branch; --rebase-merges keeps them.
2026-07-22 07:25:14 +00:00

113 lines
4.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# 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,
# - tolerate tracked build outputs that `pnpm build` rewrites,
# - 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
log() { echo "[update-gateway] $*"; }
on_exit() {
local code=$?
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"
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
# `pnpm build` rewrites this tracked bundle, which would make the tree look
# dirty and block the rebase below. Restoring it loses nothing: the build
# regenerates it from source every run.
git checkout -- extensions/browser/chrome-extension/modules/copilot-runtime.js 2>/dev/null || true
# 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: every update deletes and 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
branch="$(git rev-parse --abbrev-ref HEAD)"
if [ "$branch" = "main" ]; then
log "fast-forwarding main"
git merge --ff-only "${remote}/main"
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 "${remote}/main"; then
git rebase --abort
log "rebase of '$branch' conflicts with ${remote}/main; resolve manually"
exit 1
fi
fi
log "installing dependencies"
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
rm -rf dist dist-runtime .artifacts/tsgo-cache
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)"