From d7b6035c463025d722cf8a9ea5133e527e39c372 Mon Sep 17 00:00:00 2001 From: Carlos Villela Date: Sun, 22 Mar 2026 22:42:01 -0700 Subject: [PATCH 1/2] chore: apply shfmt formatting and fix SC2206 in shell scripts Auto-formatted with `shfmt -i 2 -ci -bn` to match the project's pre-commit configuration. Also fix SC2206 shellcheck warning in version_gte() by using `read -ra` instead of unquoted word splitting. Co-Authored-By: Claude Opus 4.6 (1M context) --- install.sh | 75 ++++++++++------- scripts/backup-workspace.sh | 155 ++++++++++++++++++----------------- scripts/install-openshell.sh | 4 +- 3 files changed, 127 insertions(+), 107 deletions(-) diff --git a/install.sh b/install.sh index 88c767980..40a0d6e2a 100755 --- a/install.sh +++ b/install.sh @@ -27,9 +27,9 @@ NEMOCLAW_VERSION="$(resolve_installer_version)" # --------------------------------------------------------------------------- if [[ -z "${NO_COLOR:-}" && -t 1 ]]; then if [[ "${COLORTERM:-}" == "truecolor" || "${COLORTERM:-}" == "24bit" ]]; then - C_GREEN=$'\033[38;2;118;185;0m' # #76B900 — exact NVIDIA green + C_GREEN=$'\033[38;2;118;185;0m' # #76B900 — exact NVIDIA green else - C_GREEN=$'\033[38;5;148m' # closest 256-color on dark backgrounds + C_GREEN=$'\033[38;5;148m' # closest 256-color on dark backgrounds fi C_BOLD=$'\033[1m' C_DIM=$'\033[2m' @@ -44,10 +44,13 @@ fi # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- -info() { printf "${C_CYAN}[INFO]${C_RESET} %s\n" "$*"; } -warn() { printf "${C_YELLOW}[WARN]${C_RESET} %s\n" "$*"; } -error() { printf "${C_RED}[ERROR]${C_RESET} %s\n" "$*" >&2; exit 1; } -ok() { printf " ${C_GREEN}✓${C_RESET} %s\n" "$*"; } +info() { printf "${C_CYAN}[INFO]${C_RESET} %s\n" "$*"; } +warn() { printf "${C_YELLOW}[WARN]${C_RESET} %s\n" "$*"; } +error() { + printf "${C_RED}[ERROR]${C_RESET} %s\n" "$*" >&2 + exit 1 +} +ok() { printf " ${C_GREEN}✓${C_RESET} %s\n" "$*"; } resolve_default_sandbox_name() { local registry_file="${HOME}/.nemoclaw/sandboxes.json" @@ -95,7 +98,7 @@ print_banner() { } print_done() { - local elapsed=$(( SECONDS - _INSTALL_START )) + local elapsed=$((SECONDS - _INSTALL_START)) local sandbox_name sandbox_name="$(resolve_default_sandbox_name)" info "=== Installation complete ===" @@ -146,7 +149,8 @@ usage() { # Stdout/stderr are captured; dumped only on failure. # Falls back to plain output when stdout is not a TTY (CI / piped installs). spin() { - local msg="$1"; shift + local msg="$1" + shift if [[ ! -t 1 ]]; then info "$msg" @@ -154,7 +158,8 @@ spin() { return fi - local log; log=$(mktemp) + local log + log=$(mktemp) "$@" >"$log" 2>&1 & local pid=$! i=0 local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏') @@ -191,12 +196,13 @@ ORIGINAL_PATH="${PATH:-}" # Compare two semver strings (major.minor.patch). Returns 0 if $1 >= $2. version_gte() { - local IFS=. - local -a a=($1) b=($2) + local -a a b + IFS=. read -ra a <<<"$1" + IFS=. read -ra b <<<"$2" for i in 0 1 2; do local ai=${a[$i]:-0} bi=${b[$i]:-0} - if (( ai > bi )); then return 0; fi - if (( ai < bi )); then return 1; fi + if ((ai > bi)); then return 0; fi + if ((ai < bi)); then return 1; fi done return 0 } @@ -265,7 +271,7 @@ ensure_supported_runtime() { [[ "$node_major" =~ ^[0-9]+$ ]] || error "Could not determine Node.js version from '${node_version}'. ${RUNTIME_REQUIREMENT_MSG}" [[ "$npm_major" =~ ^[0-9]+$ ]] || error "Could not determine npm version from '${npm_version}'. ${RUNTIME_REQUIREMENT_MSG}" - if (( node_major < MIN_NODE_MAJOR || npm_major < MIN_NPM_MAJOR )); then + if ((node_major < MIN_NODE_MAJOR || npm_major < MIN_NPM_MAJOR)); then error "Unsupported runtime detected: Node.js ${node_version:-unknown}, npm ${npm_version:-unknown}. ${RUNTIME_REQUIREMENT_MSG} Upgrade Node.js and rerun the installer." fi @@ -288,7 +294,10 @@ install_nodejs() { local nvm_tmp nvm_tmp="$(mktemp)" curl -fsSL "https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VERSION}/install.sh" -o "$nvm_tmp" \ - || { rm -f "$nvm_tmp"; error "Failed to download nvm installer"; } + || { + rm -f "$nvm_tmp" + error "Failed to download nvm installer" + } local actual_hash if command_exists sha256sum; then actual_hash="$(sha256sum "$nvm_tmp" | awk '{print $1}')" @@ -296,7 +305,7 @@ install_nodejs() { actual_hash="$(shasum -a 256 "$nvm_tmp" | awk '{print $1}')" else warn "No SHA-256 tool found — skipping nvm integrity check" - actual_hash="$NVM_SHA256" # allow execution + actual_hash="$NVM_SHA256" # allow execution fi if [[ "$actual_hash" != "$NVM_SHA256" ]]; then rm -f "$nvm_tmp" @@ -341,7 +350,7 @@ get_vram_mb() { if [[ "$(uname -s)" == "Darwin" ]] && command_exists sysctl; then local bytes bytes=$(sysctl -n hw.memsize 2>/dev/null || echo 0) - echo $(( bytes / 1024 / 1024 )) + echo $((bytes / 1024 / 1024)) return fi echo 0 @@ -373,10 +382,10 @@ install_or_upgrade_ollama() { # Pull the appropriate model based on VRAM local vram_mb vram_mb=$(get_vram_mb) - local vram_gb=$(( vram_mb / 1024 )) + local vram_gb=$((vram_mb / 1024)) info "Detected ${vram_gb} GB VRAM" - if (( vram_gb >= 120 )); then + if ((vram_gb >= 120)); then info "Pulling nemotron-3-super:120b…" ollama pull nemotron-3-super:120b else @@ -406,13 +415,12 @@ pre_extract_openclaw() { info "Pre-extracting openclaw@${openclaw_version} with system tar (GH-503 workaround)…" local tmpdir tmpdir="$(mktemp -d)" - if npm pack "openclaw@${openclaw_version}" --pack-destination "$tmpdir" > /dev/null 2>&1; then + if npm pack "openclaw@${openclaw_version}" --pack-destination "$tmpdir" >/dev/null 2>&1; then local tgz tgz="$(find "$tmpdir" -maxdepth 1 -name 'openclaw-*.tgz' -print -quit)" if [[ -n "$tgz" && -f "$tgz" ]]; then if mkdir -p "${install_dir}/node_modules/openclaw" \ - && tar xzf "$tgz" -C "${install_dir}/node_modules/openclaw" --strip-components=1 - then + && tar xzf "$tgz" -C "${install_dir}/node_modules/openclaw" --strip-components=1; then info "openclaw pre-extracted successfully" else warn "Failed to extract openclaw tarball" @@ -435,8 +443,8 @@ pre_extract_openclaw() { install_nemoclaw() { if [[ -f "./package.json" ]] && grep -q '"name": "nemoclaw"' ./package.json 2>/dev/null; then info "NemoClaw package.json found in current directory — installing from source…" - spin "Preparing OpenClaw package" bash -lc "$(declare -f pre_extract_openclaw); pre_extract_openclaw \"\$1\"" _ "$(pwd)" || \ - warn "Pre-extraction failed — npm install may fail if openclaw tarball is broken" + spin "Preparing OpenClaw package" bash -lc "$(declare -f pre_extract_openclaw); pre_extract_openclaw \"\$1\"" _ "$(pwd)" \ + || warn "Pre-extraction failed — npm install may fail if openclaw tarball is broken" spin "Installing NemoClaw dependencies" npm install --ignore-scripts spin "Building NemoClaw plugin" bash -lc 'cd nemoclaw && npm install --ignore-scripts && npm run build' spin "Linking NemoClaw CLI" npm link @@ -449,8 +457,8 @@ install_nemoclaw() { rm -rf "$nemoclaw_src" mkdir -p "$(dirname "$nemoclaw_src")" spin "Cloning NemoClaw source" git clone --depth 1 https://github.com/NVIDIA/NemoClaw.git "$nemoclaw_src" - spin "Preparing OpenClaw package" bash -lc "$(declare -f pre_extract_openclaw); pre_extract_openclaw \"\$1\"" _ "$nemoclaw_src" || \ - warn "Pre-extraction failed — npm install may fail if openclaw tarball is broken" + spin "Preparing OpenClaw package" bash -lc "$(declare -f pre_extract_openclaw); pre_extract_openclaw \"\$1\"" _ "$nemoclaw_src" \ + || warn "Pre-extraction failed — npm install may fail if openclaw tarball is broken" spin "Installing NemoClaw dependencies" bash -lc "cd \"$nemoclaw_src\" && npm install --ignore-scripts" spin "Building NemoClaw plugin" bash -lc "cd \"$nemoclaw_src\"/nemoclaw && npm install --ignore-scripts && npm run build" spin "Linking NemoClaw CLI" bash -lc "cd \"$nemoclaw_src\" && npm link" @@ -558,9 +566,18 @@ main() { for arg in "$@"; do case "$arg" in --non-interactive) NON_INTERACTIVE=1 ;; - --version|-v) printf "nemoclaw-installer v%s\n" "$NEMOCLAW_VERSION"; exit 0 ;; - --help|-h) usage; exit 0 ;; - *) usage; error "Unknown option: $arg" ;; + --version | -v) + printf "nemoclaw-installer v%s\n" "$NEMOCLAW_VERSION" + exit 0 + ;; + --help | -h) + usage + exit 0 + ;; + *) + usage + error "Unknown option: $arg" + ;; esac done # Also honor env var diff --git a/scripts/backup-workspace.sh b/scripts/backup-workspace.sh index 0e83ca3fe..573823443 100755 --- a/scripts/backup-workspace.sh +++ b/scripts/backup-workspace.sh @@ -16,10 +16,13 @@ NC='\033[0m' info() { echo -e "${GREEN}[backup]${NC} $1"; } warn() { echo -e "${YELLOW}[backup]${NC} $1"; } -fail() { echo -e "${RED}[backup]${NC} $1" >&2; exit 1; } +fail() { + echo -e "${RED}[backup]${NC} $1" >&2 + exit 1 +} usage() { - cat < $(basename "$0") restore [timestamp] @@ -31,88 +34,88 @@ Commands: Backup location: ${BACKUP_BASE}// EOF - exit 1 + exit 1 } do_backup() { - local sandbox="$1" - local ts - ts="$(date +%Y%m%d-%H%M%S)" - local dest="${BACKUP_BASE}/${ts}" - - mkdir -p "$BACKUP_BASE" - chmod 0700 "${HOME}/.nemoclaw" "$BACKUP_BASE" || \ - fail "Failed to set secure permissions on ${HOME}/.nemoclaw — check directory ownership." - mkdir -p "$dest" - chmod 0700 "$dest" - - info "Backing up workspace from sandbox '${sandbox}'..." - - local count=0 - for f in "${FILES[@]}"; do - if openshell sandbox download "$sandbox" "${WORKSPACE_PATH}/${f}" "${dest}/"; then - count=$((count + 1)) - else - warn "Skipped ${f} (not found or download failed)" - fi - done - - for d in "${DIRS[@]}"; do - if openshell sandbox download "$sandbox" "${WORKSPACE_PATH}/${d}/" "${dest}/${d}/"; then - count=$((count + 1)) - else - warn "Skipped ${d}/ (not found or download failed)" - fi - done - - if [ "$count" -eq 0 ]; then - fail "No files were backed up. Check that the sandbox '${sandbox}' exists and has workspace files." + local sandbox="$1" + local ts + ts="$(date +%Y%m%d-%H%M%S)" + local dest="${BACKUP_BASE}/${ts}" + + mkdir -p "$BACKUP_BASE" + chmod 0700 "${HOME}/.nemoclaw" "$BACKUP_BASE" \ + || fail "Failed to set secure permissions on ${HOME}/.nemoclaw — check directory ownership." + mkdir -p "$dest" + chmod 0700 "$dest" + + info "Backing up workspace from sandbox '${sandbox}'..." + + local count=0 + for f in "${FILES[@]}"; do + if openshell sandbox download "$sandbox" "${WORKSPACE_PATH}/${f}" "${dest}/"; then + count=$((count + 1)) + else + warn "Skipped ${f} (not found or download failed)" + fi + done + + for d in "${DIRS[@]}"; do + if openshell sandbox download "$sandbox" "${WORKSPACE_PATH}/${d}/" "${dest}/${d}/"; then + count=$((count + 1)) + else + warn "Skipped ${d}/ (not found or download failed)" fi + done + + if [ "$count" -eq 0 ]; then + fail "No files were backed up. Check that the sandbox '${sandbox}' exists and has workspace files." + fi - info "Backup saved to ${dest}/ (${count} items)" + info "Backup saved to ${dest}/ (${count} items)" } do_restore() { - local sandbox="$1" - local ts="${2:-}" - - if [ -z "$ts" ]; then - ts="$(ls -1 "$BACKUP_BASE" 2>/dev/null | sort -r | head -n1)" - [ -n "$ts" ] || fail "No backups found in ${BACKUP_BASE}/" - info "Using most recent backup: ${ts}" + local sandbox="$1" + local ts="${2:-}" + + if [ -z "$ts" ]; then + ts="$(ls -1 "$BACKUP_BASE" 2>/dev/null | sort -r | head -n1)" + [ -n "$ts" ] || fail "No backups found in ${BACKUP_BASE}/" + info "Using most recent backup: ${ts}" + fi + + local src="${BACKUP_BASE}/${ts}" + [ -d "$src" ] || fail "Backup directory not found: ${src}" + + info "Restoring workspace to sandbox '${sandbox}' from ${src}..." + + local count=0 + for f in "${FILES[@]}"; do + if [ -f "${src}/${f}" ]; then + if openshell sandbox upload "$sandbox" "${src}/${f}" "${WORKSPACE_PATH}/"; then + count=$((count + 1)) + else + warn "Failed to restore ${f}" + fi fi - - local src="${BACKUP_BASE}/${ts}" - [ -d "$src" ] || fail "Backup directory not found: ${src}" - - info "Restoring workspace to sandbox '${sandbox}' from ${src}..." - - local count=0 - for f in "${FILES[@]}"; do - if [ -f "${src}/${f}" ]; then - if openshell sandbox upload "$sandbox" "${src}/${f}" "${WORKSPACE_PATH}/"; then - count=$((count + 1)) - else - warn "Failed to restore ${f}" - fi - fi - done - - for d in "${DIRS[@]}"; do - if [ -d "${src}/${d}" ]; then - if openshell sandbox upload "$sandbox" "${src}/${d}/" "${WORKSPACE_PATH}/${d}/"; then - count=$((count + 1)) - else - warn "Failed to restore ${d}/" - fi - fi - done - - if [ "$count" -eq 0 ]; then - fail "No files were restored. Check that the sandbox '${sandbox}' is running." + done + + for d in "${DIRS[@]}"; do + if [ -d "${src}/${d}" ]; then + if openshell sandbox upload "$sandbox" "${src}/${d}/" "${WORKSPACE_PATH}/${d}/"; then + count=$((count + 1)) + else + warn "Failed to restore ${d}/" + fi fi + done + + if [ "$count" -eq 0 ]; then + fail "No files were restored. Check that the sandbox '${sandbox}' is running." + fi - info "Restored ${count} items to sandbox '${sandbox}'." + info "Restored ${count} items to sandbox '${sandbox}'." } # --- Main --- @@ -125,7 +128,7 @@ sandbox="$2" shift 2 case "$action" in - backup) do_backup "$sandbox" ;; - restore) do_restore "$sandbox" "$@" ;; - *) usage ;; + backup) do_backup "$sandbox" ;; + restore) do_restore "$sandbox" "$@" ;; + *) usage ;; esac diff --git a/scripts/install-openshell.sh b/scripts/install-openshell.sh index 1eeec7d2b..dbbeed409 100755 --- a/scripts/install-openshell.sh +++ b/scripts/install-openshell.sh @@ -40,8 +40,8 @@ version_gte() { # Returns 0 (true) if $1 >= $2 — portable, no sort -V (BSD compat) local IFS=. local -a a b - read -r -a a <<< "$1" - read -r -a b <<< "$2" + read -r -a a <<<"$1" + read -r -a b <<<"$2" for i in 0 1 2; do local ai=${a[$i]:-0} bi=${b[$i]:-0} if ((ai > bi)); then return 0; fi From de7f5ef71aba631f870d3fbb88322710df09f4b5 Mon Sep 17 00:00:00 2001 From: Carlos Villela Date: Sun, 22 Mar 2026 22:42:52 -0700 Subject: [PATCH 2/2] fix(ci): fix pyright scanning .venv and test files - Drop stray '.' arg from pyright in Makefile and pre-commit hook (overrides pyproject.toml include/exclude, causing it to scan .venv) - Exclude test_*.py from pyright strict checking (pytest is a dev dep) Co-Authored-By: Claude Opus 4.6 (1M context) --- .pre-commit-config.yaml | 2 +- nemoclaw-blueprint/Makefile | 2 +- nemoclaw-blueprint/pyproject.toml | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cac42595d..f6a147121 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -181,7 +181,7 @@ repos: - id: pyright-check name: Pyright (nemoclaw-blueprint) - entry: bash -c 'cd nemoclaw-blueprint && uv run --with pyright pyright .' + entry: bash -c 'cd nemoclaw-blueprint && uv run --with pyright pyright' language: system pass_filenames: false always_run: true diff --git a/nemoclaw-blueprint/Makefile b/nemoclaw-blueprint/Makefile index 448e7981c..e00180cce 100644 --- a/nemoclaw-blueprint/Makefile +++ b/nemoclaw-blueprint/Makefile @@ -10,4 +10,4 @@ format: check: ruff check . ruff format --check . - uv run --extra dev --with pyright pyright . + uv run --extra dev --with pyright pyright diff --git a/nemoclaw-blueprint/pyproject.toml b/nemoclaw-blueprint/pyproject.toml index 47c6481c7..111be4ee3 100644 --- a/nemoclaw-blueprint/pyproject.toml +++ b/nemoclaw-blueprint/pyproject.toml @@ -44,6 +44,7 @@ ignore = [ pythonVersion = "3.11" typeCheckingMode = "strict" include = ["orchestrator", "migrations"] +exclude = ["**/test_*.py"] [tool.ruff.format] quote-style = "double"