Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
65
.github/actions/detect-docs-changes/action.yml
vendored
Normal file
65
.github/actions/detect-docs-changes/action.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
name: Detect docs-only changes
|
||||
description: >
|
||||
Outputs docs_only=true when all changed files are under docs/ or are
|
||||
markdown (.md/.mdx). Fail-safe: if detection fails, outputs false (run
|
||||
everything). Uses git diff — no API calls, no extra permissions needed.
|
||||
|
||||
outputs:
|
||||
docs_only:
|
||||
description: "'true' if all changes are docs/markdown, 'false' otherwise"
|
||||
value: ${{ steps.check.outputs.docs_only }}
|
||||
docs_changed:
|
||||
description: "'true' if any changed file is under docs/ or is markdown"
|
||||
value: ${{ steps.check.outputs.docs_changed }}
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Detect docs-only changes
|
||||
id: check
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "push" ]; then
|
||||
BASE="${{ github.event.before }}"
|
||||
else
|
||||
# Use the exact base SHA from the event payload — stable regardless
|
||||
# of base branch movement (avoids origin/<ref> drift).
|
||||
BASE="${{ github.event.pull_request.base.sha }}"
|
||||
fi
|
||||
|
||||
# Fail-safe: if we can't diff, assume non-docs (run everything)
|
||||
CHANGED=$(git diff --name-only "$BASE" HEAD 2>/dev/null || echo "UNKNOWN")
|
||||
if [ "$CHANGED" = "UNKNOWN" ] || [ -z "$CHANGED" ]; then
|
||||
echo "docs_only=false" >> "$GITHUB_OUTPUT"
|
||||
echo "docs_changed=false" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
docs_changed=false
|
||||
non_docs=false
|
||||
while IFS= read -r changed_path; do
|
||||
case "$changed_path" in
|
||||
test/fixtures/*)
|
||||
non_docs=true
|
||||
;;
|
||||
docs/* | *.md | *.mdx)
|
||||
docs_changed=true
|
||||
;;
|
||||
*)
|
||||
non_docs=true
|
||||
;;
|
||||
esac
|
||||
done <<< "$CHANGED"
|
||||
|
||||
if [ "$docs_changed" = "true" ]; then
|
||||
echo "docs_changed=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "docs_changed=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
if [ "$non_docs" = "false" ]; then
|
||||
echo "docs_only=true" >> "$GITHUB_OUTPUT"
|
||||
echo "Docs-only change detected — skipping heavy jobs"
|
||||
else
|
||||
echo "docs_only=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
172
.github/actions/docker-e2e-plan/action.yml
vendored
Normal file
172
.github/actions/docker-e2e-plan/action.yml
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
name: Docker E2E plan and hydrate
|
||||
description: >
|
||||
Create a Docker E2E lane plan, expose GitHub outputs, and optionally hydrate
|
||||
the prebuilt package artifact plus shared Docker images needed by the plan.
|
||||
inputs:
|
||||
mode:
|
||||
description: prepare, chunk, or targeted.
|
||||
required: true
|
||||
chunk:
|
||||
description: Release-path chunk for mode=chunk.
|
||||
required: false
|
||||
default: ""
|
||||
lanes:
|
||||
description: Comma/space separated lane names for targeted or prepare mode.
|
||||
required: false
|
||||
default: ""
|
||||
include-openwebui:
|
||||
description: Whether Open WebUI is included when planning release/prepare coverage.
|
||||
required: false
|
||||
default: "true"
|
||||
include-release-path-suites:
|
||||
description: Whether prepare mode should plan all release-path suites.
|
||||
required: false
|
||||
default: "false"
|
||||
hydrate-artifacts:
|
||||
description: Whether to download/pull artifacts required by the plan.
|
||||
required: false
|
||||
default: "true"
|
||||
package-artifact-name:
|
||||
description: Workflow artifact name containing openclaw-current.tgz.
|
||||
required: false
|
||||
default: docker-e2e-package
|
||||
outputs:
|
||||
credentials:
|
||||
description: Comma-separated credential groups required by selected lanes.
|
||||
value: ${{ steps.plan.outputs.credentials }}
|
||||
needs_bare_image:
|
||||
description: "1 when selected lanes require the bare Docker E2E image."
|
||||
value: ${{ steps.plan.outputs.needs_bare_image }}
|
||||
needs_e2e_image:
|
||||
description: "1 when selected lanes require any Docker E2E image."
|
||||
value: ${{ steps.plan.outputs.needs_e2e_image }}
|
||||
needs_functional_image:
|
||||
description: "1 when selected lanes require the functional Docker E2E image."
|
||||
value: ${{ steps.plan.outputs.needs_functional_image }}
|
||||
needs_live_image:
|
||||
description: "1 when selected lanes require building the live Docker image."
|
||||
value: ${{ steps.plan.outputs.needs_live_image }}
|
||||
needs_package:
|
||||
description: "1 when selected lanes require the OpenClaw package tarball."
|
||||
value: ${{ steps.plan.outputs.needs_package }}
|
||||
plan_json:
|
||||
description: Path to the generated plan JSON.
|
||||
value: ${{ steps.plan.outputs.plan_json }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Plan Docker E2E lanes
|
||||
id: plan
|
||||
shell: bash
|
||||
env:
|
||||
MODE: ${{ inputs.mode }}
|
||||
CHUNK: ${{ inputs.chunk }}
|
||||
LANES: ${{ inputs.lanes }}
|
||||
INCLUDE_OPENWEBUI: ${{ inputs.include-openwebui }}
|
||||
INCLUDE_RELEASE_PATH_SUITES: ${{ inputs.include-release-path-suites }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p .artifacts/docker-tests
|
||||
|
||||
case "$MODE" in
|
||||
prepare)
|
||||
plan_path=".artifacts/docker-tests/plan.json"
|
||||
if [[ "$INCLUDE_RELEASE_PATH_SUITES" == "true" ]]; then
|
||||
export OPENCLAW_DOCKER_ALL_PROFILE=release-path
|
||||
export OPENCLAW_DOCKER_ALL_PLAN_RELEASE_ALL=1
|
||||
elif [[ -n "$LANES" ]]; then
|
||||
export OPENCLAW_DOCKER_ALL_LANES="$LANES"
|
||||
elif [[ "$INCLUDE_OPENWEBUI" == "true" ]]; then
|
||||
export OPENCLAW_DOCKER_ALL_LANES=openwebui
|
||||
fi
|
||||
;;
|
||||
chunk)
|
||||
if [[ -z "$CHUNK" ]]; then
|
||||
echo "chunk input is required for Docker E2E chunk planning." >&2
|
||||
exit 1
|
||||
fi
|
||||
export OPENCLAW_DOCKER_ALL_PROFILE=release-path
|
||||
export OPENCLAW_DOCKER_ALL_CHUNK="$CHUNK"
|
||||
plan_path=".artifacts/docker-tests/release-${CHUNK}-plan.json"
|
||||
;;
|
||||
targeted)
|
||||
if [[ -z "$LANES" ]]; then
|
||||
echo "lanes input is required for Docker E2E targeted planning." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$INCLUDE_RELEASE_PATH_SUITES" == "true" ]]; then
|
||||
export OPENCLAW_DOCKER_ALL_PROFILE=release-path
|
||||
fi
|
||||
export OPENCLAW_DOCKER_ALL_LANES="$LANES"
|
||||
plan_path=".artifacts/docker-tests/targeted-plan.json"
|
||||
;;
|
||||
*)
|
||||
echo "mode must be prepare, chunk, or targeted. Got: $MODE" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI="$INCLUDE_OPENWEBUI"
|
||||
node scripts/test-docker-all.mjs --plan-json > "$plan_path"
|
||||
node scripts/docker-e2e.mjs github-outputs "$plan_path" >> "$GITHUB_OUTPUT"
|
||||
echo "plan_json=$plan_path" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Download OpenClaw Docker E2E package
|
||||
if: inputs.hydrate-artifacts == 'true' && steps.plan.outputs.needs_package == '1'
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: ${{ inputs.package-artifact-name }}
|
||||
path: .artifacts/docker-e2e-package
|
||||
|
||||
- name: Pull shared bare Docker E2E image
|
||||
if: inputs.hydrate-artifacts == 'true' && steps.plan.outputs.needs_bare_image == '1'
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
bash scripts/ci-docker-pull-retry.sh "${OPENCLAW_DOCKER_E2E_BARE_IMAGE}"
|
||||
|
||||
- name: Pull shared functional Docker E2E image
|
||||
if: inputs.hydrate-artifacts == 'true' && steps.plan.outputs.needs_functional_image == '1'
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
bash scripts/ci-docker-pull-retry.sh "${OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE}"
|
||||
|
||||
- name: Validate Docker E2E credentials
|
||||
if: inputs.hydrate-artifacts == 'true'
|
||||
shell: bash
|
||||
env:
|
||||
CREDENTIALS: ${{ steps.plan.outputs.credentials }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
credentials=",$CREDENTIALS,"
|
||||
require_any() {
|
||||
local label="$1"
|
||||
shift
|
||||
local key
|
||||
for key in "$@"; do
|
||||
if [[ -n "${!key:-}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
echo "Missing credential for ${label}: expected one of $*" >&2
|
||||
exit 1
|
||||
}
|
||||
if [[ "$credentials" == *",openai,"* ]]; then
|
||||
require_any OpenAI OPENAI_API_KEY
|
||||
fi
|
||||
if [[ "$credentials" == *",codex,"* ]]; then
|
||||
require_any Codex OPENCLAW_CODEX_AUTH_JSON
|
||||
fi
|
||||
if [[ "$credentials" == *",anthropic,"* ]]; then
|
||||
require_any Anthropic ANTHROPIC_API_TOKEN ANTHROPIC_API_KEY OPENCLAW_CLAUDE_CREDENTIALS_JSON OPENCLAW_CLAUDE_JSON
|
||||
fi
|
||||
if [[ "$credentials" == *",factory,"* ]]; then
|
||||
require_any Factory FACTORY_API_KEY
|
||||
fi
|
||||
if [[ "$credentials" == *",gemini,"* ]]; then
|
||||
require_any Gemini GEMINI_API_KEY GOOGLE_API_KEY OPENCLAW_GEMINI_SETTINGS_JSON
|
||||
fi
|
||||
if [[ "$credentials" == *",opencode,"* ]]; then
|
||||
require_any OpenCode OPENCODE_API_KEY OPENCODE_ZEN_API_KEY
|
||||
fi
|
||||
67
.github/actions/ensure-base-commit/action.yml
vendored
Normal file
67
.github/actions/ensure-base-commit/action.yml
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
name: Ensure base commit
|
||||
description: Ensure a shallow checkout has enough history to diff against a base SHA.
|
||||
inputs:
|
||||
base-sha:
|
||||
description: Base commit SHA to diff against.
|
||||
required: true
|
||||
fetch-ref:
|
||||
description: Branch or ref to deepen/fetch from origin when base-sha is missing.
|
||||
required: true
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Ensure base commit is available
|
||||
shell: bash
|
||||
env:
|
||||
BASE_SHA: ${{ inputs.base-sha }}
|
||||
FETCH_REF: ${{ inputs.fetch-ref }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if [ -z "$BASE_SHA" ] || [[ "$BASE_SHA" =~ ^0+$ ]]; then
|
||||
echo "No concrete base SHA available; skipping targeted fetch."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! [[ "$BASE_SHA" =~ ^[0-9a-fA-F]{7,40}$ ]]; then
|
||||
echo "::error title=ensure-base-commit invalid base sha::Refusing invalid base SHA: $BASE_SHA"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if ! git check-ref-format --branch "$FETCH_REF" >/dev/null 2>&1; then
|
||||
echo "::error title=ensure-base-commit invalid fetch ref::Refusing invalid fetch ref: $FETCH_REF"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if git rev-parse --verify "$BASE_SHA^{commit}" >/dev/null 2>&1; then
|
||||
echo "Base commit already present: $BASE_SHA"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
fetch_base_ref() {
|
||||
timeout --signal=TERM --kill-after=10s 30s git \
|
||||
-c protocol.version=2 \
|
||||
fetch "$@"
|
||||
}
|
||||
|
||||
for deepen_by in 25 100 300; do
|
||||
echo "Base commit missing; deepening $FETCH_REF by $deepen_by."
|
||||
if ! fetch_base_ref --no-tags --deepen="$deepen_by" origin -- "$FETCH_REF"; then
|
||||
echo "::warning title=ensure-base-commit fetch failed::Failed to deepen $FETCH_REF by $deepen_by while looking for $BASE_SHA"
|
||||
fi
|
||||
if git rev-parse --verify "$BASE_SHA^{commit}" >/dev/null 2>&1; then
|
||||
echo "Resolved base commit after deepening: $BASE_SHA"
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Base commit still missing; fetching full history for $FETCH_REF."
|
||||
if ! fetch_base_ref --no-tags origin -- "$FETCH_REF"; then
|
||||
echo "::warning title=ensure-base-commit fetch failed::Failed to fetch full history for $FETCH_REF while looking for $BASE_SHA"
|
||||
fi
|
||||
if git rev-parse --verify "$BASE_SHA^{commit}" >/dev/null 2>&1; then
|
||||
echo "Resolved base commit after full ref fetch: $BASE_SHA"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Base commit still unavailable after fetch attempts: $BASE_SHA"
|
||||
145
.github/actions/setup-node-env/action.yml
vendored
Normal file
145
.github/actions/setup-node-env/action.yml
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
name: Setup Node environment
|
||||
description: >
|
||||
Install Node 24 by default, pnpm, optionally Bun, and optionally run pnpm
|
||||
install. Requires actions/checkout to run first.
|
||||
inputs:
|
||||
node-version:
|
||||
description: Node.js version to install.
|
||||
required: false
|
||||
default: "24.x"
|
||||
install-bun:
|
||||
description: Whether to install Bun alongside Node.
|
||||
required: false
|
||||
default: "true"
|
||||
install-deps:
|
||||
description: Whether to run pnpm install after environment setup.
|
||||
required: false
|
||||
default: "true"
|
||||
frozen-lockfile:
|
||||
description: Whether to use --frozen-lockfile for install.
|
||||
required: false
|
||||
default: "true"
|
||||
use-actions-cache:
|
||||
description: Whether to restore the pnpm store with actions/cache.
|
||||
required: false
|
||||
default: "true"
|
||||
save-actions-cache:
|
||||
description: Whether to save the pnpm store with actions/cache after install when no exact cache restored.
|
||||
required: false
|
||||
default: "false"
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Normalize container toolcache
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [[ -d /__t && ! -e /opt/hostedtoolcache ]]; then
|
||||
mkdir -p /opt
|
||||
ln -s /__t /opt/hostedtoolcache
|
||||
fi
|
||||
|
||||
- name: Setup Node.js
|
||||
shell: bash
|
||||
env:
|
||||
REQUESTED_NODE_VERSION: ${{ inputs.node-version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source "$GITHUB_ACTION_PATH/../setup-pnpm-store-cache/ensure-node.sh"
|
||||
openclaw_ensure_node "$REQUESTED_NODE_VERSION"
|
||||
|
||||
- name: Setup pnpm
|
||||
id: setup-pnpm
|
||||
uses: ./.github/actions/setup-pnpm-store-cache
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
use-actions-cache: ${{ inputs.use-actions-cache }}
|
||||
|
||||
- name: Setup Bun
|
||||
if: inputs.install-bun == 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
npm install -g bun@1.3.14
|
||||
|
||||
- name: Runtime versions
|
||||
shell: bash
|
||||
run: |
|
||||
node -v
|
||||
npm -v
|
||||
pnpm -v
|
||||
if command -v bun &>/dev/null; then bun -v; fi
|
||||
|
||||
- name: Capture node path
|
||||
shell: bash
|
||||
run: |
|
||||
node_bin="$(dirname "$(node -p 'process.execPath')")"
|
||||
if command -v cygpath >/dev/null 2>&1; then
|
||||
node_bin="$(cygpath -u "$node_bin")"
|
||||
fi
|
||||
# zizmor: ignore[github-env] node_bin comes from trusted actions/setup-node output in this composite action.
|
||||
echo "NODE_BIN=$node_bin" >> "$GITHUB_ENV"
|
||||
echo "$node_bin" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Install dependencies
|
||||
if: inputs.install-deps == 'true'
|
||||
shell: bash
|
||||
env:
|
||||
CI: "true"
|
||||
FROZEN_LOCKFILE: ${{ inputs.frozen-lockfile }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
export PATH="$NODE_BIN:$PATH"
|
||||
which node
|
||||
node -v
|
||||
pnpm -v
|
||||
case "$FROZEN_LOCKFILE" in
|
||||
true) LOCKFILE_FLAG="--frozen-lockfile" ;;
|
||||
false) LOCKFILE_FLAG="" ;;
|
||||
*)
|
||||
echo "::error::Invalid frozen-lockfile input: '$FROZEN_LOCKFILE' (expected true or false)"
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
install_args=(
|
||||
install
|
||||
--prefer-offline
|
||||
--ignore-scripts=false
|
||||
--config.engine-strict=false
|
||||
--config.enable-pre-post-scripts=true
|
||||
--config.side-effects-cache=true
|
||||
)
|
||||
if [ -n "$LOCKFILE_FLAG" ]; then
|
||||
install_args+=("$LOCKFILE_FLAG")
|
||||
fi
|
||||
append_pnpm_option_arg() {
|
||||
local env_name="$1"
|
||||
local option_name="$2"
|
||||
local value="${!env_name-}"
|
||||
if [ -n "$value" ]; then
|
||||
install_args+=("--${option_name}=${value}")
|
||||
fi
|
||||
}
|
||||
append_pnpm_option_arg PNPM_CONFIG_CHILD_CONCURRENCY child-concurrency
|
||||
append_pnpm_option_arg PNPM_CONFIG_MODULES_DIR modules-dir
|
||||
append_pnpm_option_arg PNPM_CONFIG_NETWORK_CONCURRENCY network-concurrency
|
||||
append_pnpm_option_arg PNPM_CONFIG_VIRTUAL_STORE_DIR virtual-store-dir
|
||||
if [ -n "${PNPM_CONFIG_MODULES_DIR:-}" ]; then
|
||||
mkdir -p "$PNPM_CONFIG_MODULES_DIR"
|
||||
ln -sfn . "$PNPM_CONFIG_MODULES_DIR/node_modules"
|
||||
export NODE_PATH="$PNPM_CONFIG_MODULES_DIR${NODE_PATH:+:$NODE_PATH}"
|
||||
fi
|
||||
pnpm "${install_args[@]}" || pnpm "${install_args[@]}"
|
||||
if [ -n "${PNPM_CONFIG_MODULES_DIR:-}" ]; then
|
||||
rm -rf node_modules
|
||||
ln -sfn "$PNPM_CONFIG_MODULES_DIR" node_modules
|
||||
ln -sfn . "$PNPM_CONFIG_MODULES_DIR/node_modules"
|
||||
fi
|
||||
|
||||
- name: Save pnpm store cache
|
||||
if: ${{ inputs.install-deps == 'true' && inputs.use-actions-cache == 'true' && inputs.save-actions-cache == 'true' && runner.os != 'Windows' && steps.setup-pnpm.outputs.store-cache-hit != 'true' }}
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: ${{ steps.setup-pnpm.outputs.store-path }}
|
||||
key: ${{ steps.setup-pnpm.outputs.store-cache-primary-key }}
|
||||
108
.github/actions/setup-pnpm-store-cache/action.yml
vendored
Normal file
108
.github/actions/setup-pnpm-store-cache/action.yml
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
name: Setup pnpm
|
||||
description: Prepare pnpm from the repository packageManager and restore its store cache.
|
||||
inputs:
|
||||
package-manager-file:
|
||||
description: package.json file that owns the packageManager pnpm pin.
|
||||
required: false
|
||||
default: "package.json"
|
||||
lockfile-path:
|
||||
description: pnpm lockfile used to key the store cache.
|
||||
required: false
|
||||
default: "pnpm-lock.yaml"
|
||||
node-version:
|
||||
description: Expected Node.js version already installed by actions/setup-node.
|
||||
required: false
|
||||
default: ""
|
||||
use-actions-cache:
|
||||
description: Whether actions/cache should restore the pnpm store.
|
||||
required: false
|
||||
default: "true"
|
||||
outputs:
|
||||
pnpm-version:
|
||||
description: Resolved pnpm version activated by the setup action.
|
||||
value: ${{ steps.pnpm-version.outputs.pnpm-version }}
|
||||
project-dir:
|
||||
description: Directory containing the packageManager file used for pnpm resolution.
|
||||
value: ${{ steps.setup-pnpm.outputs.project-dir }}
|
||||
store-cache-hit:
|
||||
description: Whether the pnpm store cache restored an exact key.
|
||||
value: ${{ steps.pnpm-store-cache.outputs.cache-hit }}
|
||||
store-cache-primary-key:
|
||||
description: Exact pnpm store cache key used for restore/save.
|
||||
value: ${{ steps.pnpm-store-cache.outputs.cache-primary-key }}
|
||||
store-path:
|
||||
description: Resolved pnpm store path.
|
||||
value: ${{ steps.pnpm-store.outputs.path }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Validate pnpm setup inputs
|
||||
id: setup-pnpm
|
||||
shell: bash
|
||||
env:
|
||||
PACKAGE_MANAGER_FILE: ${{ inputs.package-manager-file }}
|
||||
REQUESTED_NODE_VERSION: ${{ inputs.node-version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
project_dir="$(dirname "$PACKAGE_MANAGER_FILE")"
|
||||
if [[ ! -f "$PACKAGE_MANAGER_FILE" ]]; then
|
||||
echo "::error::package manager file not found: $PACKAGE_MANAGER_FILE"
|
||||
exit 1
|
||||
fi
|
||||
echo "project-dir=$project_dir" >> "$GITHUB_OUTPUT"
|
||||
|
||||
requested_node="${REQUESTED_NODE_VERSION:-${NODE_VERSION:-}}"
|
||||
source "$GITHUB_ACTION_PATH/ensure-node.sh"
|
||||
openclaw_ensure_node "$requested_node"
|
||||
|
||||
- name: Setup pnpm from packageManager
|
||||
shell: bash
|
||||
env:
|
||||
COREPACK_ENABLE_DOWNLOAD_PROMPT: "0"
|
||||
PACKAGE_MANAGER_FILE: ${{ inputs.package-manager-file }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
package_manager="$(node -e "const fs = require('node:fs'); const path = require('node:path'); const pkg = JSON.parse(fs.readFileSync(path.resolve(process.argv[1]), 'utf8')); process.stdout.write(pkg.packageManager || '')" "$PACKAGE_MANAGER_FILE")"
|
||||
case "$package_manager" in
|
||||
pnpm@*) ;;
|
||||
*)
|
||||
echo "::error::Expected packageManager to pin pnpm, got '${package_manager:-<empty>}'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
corepack enable
|
||||
for attempt in 1 2 3; do
|
||||
if corepack prepare "$package_manager" --activate; then
|
||||
exit 0
|
||||
fi
|
||||
sleep $((attempt * 5))
|
||||
done
|
||||
corepack prepare "$package_manager" --activate
|
||||
|
||||
- name: Resolve pnpm store path
|
||||
id: pnpm-store
|
||||
if: ${{ inputs.use-actions-cache == 'true' && runner.os != 'Windows' }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
store_path="$(pnpm store path --silent)"
|
||||
node -e "require('node:fs').mkdirSync(process.argv[1], { recursive: true })" "$store_path"
|
||||
echo "path=$store_path" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Restore pnpm store cache
|
||||
id: pnpm-store-cache
|
||||
if: ${{ inputs.use-actions-cache == 'true' && runner.os != 'Windows' }}
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: ${{ steps.pnpm-store.outputs.path }}
|
||||
key: pnpm-store-${{ runner.os }}-${{ runner.arch }}-${{ inputs.node-version }}-${{ hashFiles(inputs.package-manager-file) }}-${{ hashFiles(inputs.lockfile-path) }}
|
||||
restore-keys: |
|
||||
pnpm-store-${{ runner.os }}-${{ runner.arch }}-${{ inputs.node-version }}-${{ hashFiles(inputs.package-manager-file) }}-
|
||||
pnpm-store-${{ runner.os }}-${{ runner.arch }}-${{ inputs.node-version }}-
|
||||
|
||||
- name: Record pnpm version
|
||||
id: pnpm-version
|
||||
shell: bash
|
||||
env:
|
||||
PROJECT_DIR: ${{ steps.setup-pnpm.outputs.project-dir }}
|
||||
run: echo "pnpm-version=$(cd "$PROJECT_DIR" && pnpm -v)" >> "$GITHUB_OUTPUT"
|
||||
223
.github/actions/setup-pnpm-store-cache/ensure-node.sh
vendored
Normal file
223
.github/actions/setup-pnpm-store-cache/ensure-node.sh
vendored
Normal file
@@ -0,0 +1,223 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
openclaw_node_version_matches() {
|
||||
local actual="$1"
|
||||
local requested="$2"
|
||||
if [[ -z "$requested" ]]; then
|
||||
return 0
|
||||
fi
|
||||
case "$requested" in
|
||||
*x)
|
||||
[[ "${actual%%.*}" == "${requested%%.*}" ]] || return 1
|
||||
if [[ "${requested%%.*}" == "22" ]]; then
|
||||
openclaw_node_version_at_least "$actual" "22.19.0"
|
||||
fi
|
||||
;;
|
||||
*.*.*)
|
||||
[[ "$actual" == "$requested" ]]
|
||||
;;
|
||||
*.*)
|
||||
[[ "$actual" == "$requested".* ]]
|
||||
;;
|
||||
*)
|
||||
[[ "${actual%%.*}" == "$requested" ]]
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
openclaw_node_version_at_least() {
|
||||
local actual="$1"
|
||||
local minimum="$2"
|
||||
local actual_major actual_minor actual_patch minimum_major minimum_minor minimum_patch
|
||||
IFS=. read -r actual_major actual_minor actual_patch <<< "$actual"
|
||||
IFS=. read -r minimum_major minimum_minor minimum_patch <<< "$minimum"
|
||||
actual_minor="${actual_minor:-0}"
|
||||
actual_patch="${actual_patch:-0}"
|
||||
minimum_minor="${minimum_minor:-0}"
|
||||
minimum_patch="${minimum_patch:-0}"
|
||||
|
||||
if (( actual_major != minimum_major )); then
|
||||
(( actual_major > minimum_major ))
|
||||
return
|
||||
fi
|
||||
if (( actual_minor != minimum_minor )); then
|
||||
(( actual_minor > minimum_minor ))
|
||||
return
|
||||
fi
|
||||
(( actual_patch >= minimum_patch ))
|
||||
}
|
||||
|
||||
openclaw_active_node_version() {
|
||||
node -p 'process.versions.node' 2>/dev/null || true
|
||||
}
|
||||
|
||||
openclaw_prepend_node_bin() {
|
||||
local node_bin_dir="$1"
|
||||
local github_path_dir="${2:-$node_bin_dir}"
|
||||
local shell_node_bin_dir="$node_bin_dir"
|
||||
if command -v cygpath >/dev/null 2>&1; then
|
||||
shell_node_bin_dir="$(cygpath -u "$node_bin_dir" 2>/dev/null || printf '%s' "$node_bin_dir")"
|
||||
fi
|
||||
export PATH="$shell_node_bin_dir:$PATH"
|
||||
if [[ -n "${GITHUB_PATH:-}" ]]; then
|
||||
local github_node_bin_dir="$github_path_dir"
|
||||
if [[ $# -lt 2 ]] && command -v cygpath >/dev/null 2>&1; then
|
||||
github_node_bin_dir="$shell_node_bin_dir"
|
||||
github_node_bin_dir="$(cygpath -w "$shell_node_bin_dir" 2>/dev/null || printf '%s' "$shell_node_bin_dir")"
|
||||
fi
|
||||
echo "$github_node_bin_dir" >> "$GITHUB_PATH"
|
||||
fi
|
||||
hash -r
|
||||
}
|
||||
|
||||
openclaw_find_toolcache_node() {
|
||||
local requested_node="$1"
|
||||
local roots=()
|
||||
local root
|
||||
for root in \
|
||||
"${RUNNER_TOOL_CACHE:-}" \
|
||||
"${AGENT_TOOLSDIRECTORY:-}" \
|
||||
"${ACTIONS_RUNNER_TOOL_CACHE:-}" \
|
||||
"${OPENCLAW_CONTAINER_TOOL_CACHE:-/__t}" \
|
||||
"/opt/hostedtoolcache" \
|
||||
"/home/runner/_work/_tool" \
|
||||
"/Users/runner/hostedtoolcache" \
|
||||
"/c/hostedtoolcache/windows"
|
||||
do
|
||||
if [[ ! -d "$root" && "$root" == *\\* ]] && command -v cygpath >/dev/null 2>&1; then
|
||||
root="$(cygpath -u "$root" 2>/dev/null || printf '%s' "$root")"
|
||||
fi
|
||||
if [[ -d "$root/node" ]]; then
|
||||
roots+=("$root/node")
|
||||
elif [[ "$(basename "$root")" == "node" && -d "$root" ]]; then
|
||||
roots+=("$root")
|
||||
fi
|
||||
done
|
||||
|
||||
local node_root candidate candidate_version
|
||||
for node_root in ${roots[@]+"${roots[@]}"}; do
|
||||
while IFS= read -r candidate; do
|
||||
candidate_version="$("$candidate" -p 'process.versions.node' 2>/dev/null || true)"
|
||||
if openclaw_node_version_matches "$candidate_version" "$requested_node"; then
|
||||
printf '%s\n' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
done < <(find "$node_root" \( -name node -o -name node.exe \) -type f 2>/dev/null | sort -r)
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
openclaw_resolve_node_download_version() {
|
||||
local requested_node="$1"
|
||||
if [[ "$requested_node" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
[[ "$requested_node" == v* ]] && printf '%s\n' "$requested_node" || printf 'v%s\n' "$requested_node"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local prefix="${requested_node#v}"
|
||||
prefix="${prefix%%[xX]*}"
|
||||
prefix="v${prefix}"
|
||||
[[ "$prefix" == *. ]] || prefix="${prefix}."
|
||||
curl -fsSL https://nodejs.org/dist/index.json |
|
||||
OPENCLAW_NODE_PREFIX="$prefix" python3 -c 'import json, os, sys
|
||||
prefix = os.environ["OPENCLAW_NODE_PREFIX"]
|
||||
for item in json.load(sys.stdin):
|
||||
version = item.get("version", "")
|
||||
if version.startswith(prefix):
|
||||
print(version)
|
||||
break
|
||||
'
|
||||
}
|
||||
|
||||
openclaw_node_download_platform() {
|
||||
local os_name arch_name
|
||||
os_name="$(uname -s)"
|
||||
arch_name="$(uname -m)"
|
||||
case "$os_name:$arch_name" in
|
||||
Linux:x86_64) printf 'linux-x64\n' ;;
|
||||
Linux:aarch64 | Linux:arm64) printf 'linux-arm64\n' ;;
|
||||
Darwin:x86_64) printf 'darwin-x64\n' ;;
|
||||
Darwin:arm64) printf 'darwin-arm64\n' ;;
|
||||
MINGW*:x86_64 | MSYS*:x86_64 | CYGWIN*:x86_64 | MINGW*:AMD64 | MSYS*:AMD64 | CYGWIN*:AMD64)
|
||||
printf 'win-x64\n'
|
||||
;;
|
||||
MINGW*:aarch64 | MINGW*:arm64 | MSYS*:aarch64 | MSYS*:arm64 | CYGWIN*:aarch64 | CYGWIN*:arm64) printf 'win-arm64\n' ;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
openclaw_download_node() {
|
||||
local requested_node="$1"
|
||||
local version platform archive_url install_root temp_root
|
||||
version="$(openclaw_resolve_node_download_version "$requested_node")"
|
||||
platform="$(openclaw_node_download_platform)" || return 1
|
||||
temp_root="${RUNNER_TEMP:-/tmp}"
|
||||
if command -v cygpath >/dev/null 2>&1; then
|
||||
temp_root="$(cygpath -u "$temp_root" 2>/dev/null || printf '%s\n' "$temp_root")"
|
||||
fi
|
||||
install_root="${temp_root}/openclaw-node-${version}-${platform}"
|
||||
if [[ "$platform" == win-* ]]; then
|
||||
local archive_path ps_archive_path ps_install_root ps_bin_dir node_bin_dir
|
||||
archive_path="${temp_root}/node-${version}-${platform}.zip"
|
||||
archive_url="https://nodejs.org/dist/${version}/node-${version}-${platform}.zip"
|
||||
rm -rf "$install_root"
|
||||
mkdir -p "$install_root"
|
||||
echo "Downloading Node ${version} from ${archive_url}"
|
||||
curl -fsSL -o "$archive_path" "$archive_url"
|
||||
ps_archive_path="$archive_path"
|
||||
ps_install_root="$install_root"
|
||||
if command -v cygpath >/dev/null 2>&1; then
|
||||
ps_archive_path="$(cygpath -w "$archive_path")"
|
||||
ps_install_root="$(cygpath -w "$install_root")"
|
||||
fi
|
||||
ps_bin_dir="$ps_install_root\\node-${version}-${platform}"
|
||||
node_bin_dir="$install_root/node-${version}-${platform}"
|
||||
if command -v pwsh >/dev/null 2>&1; then
|
||||
pwsh -NoLogo -NoProfile -Command "Expand-Archive -LiteralPath '${ps_archive_path}' -DestinationPath '${ps_install_root}' -Force"
|
||||
openclaw_prepend_node_bin "$node_bin_dir" "$ps_bin_dir"
|
||||
elif command -v powershell.exe >/dev/null 2>&1; then
|
||||
powershell.exe -NoLogo -NoProfile -Command "Expand-Archive -LiteralPath '${ps_archive_path}' -DestinationPath '${ps_install_root}' -Force"
|
||||
openclaw_prepend_node_bin "$node_bin_dir" "$ps_bin_dir"
|
||||
else
|
||||
unzip -q "$archive_path" -d "$install_root"
|
||||
openclaw_prepend_node_bin "$node_bin_dir"
|
||||
fi
|
||||
else
|
||||
archive_url="https://nodejs.org/dist/${version}/node-${version}-${platform}.tar.xz"
|
||||
mkdir -p "$install_root"
|
||||
echo "Downloading Node ${version} from ${archive_url}"
|
||||
curl -fsSL "$archive_url" | tar -xJ -C "$install_root" --strip-components=1
|
||||
openclaw_prepend_node_bin "$install_root/bin"
|
||||
fi
|
||||
}
|
||||
|
||||
openclaw_ensure_node() {
|
||||
local requested_node="${1:-}"
|
||||
requested_node="${requested_node#v}"
|
||||
if [[ -z "$requested_node" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local active_node_version node_bin
|
||||
active_node_version="$(openclaw_active_node_version)"
|
||||
if openclaw_node_version_matches "$active_node_version" "$requested_node"; then
|
||||
echo "Using active Node ${active_node_version} at $(command -v node)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
node_bin="$(openclaw_find_toolcache_node "$requested_node" || true)"
|
||||
if [[ -n "$node_bin" ]]; then
|
||||
echo "Using Node $("$node_bin" -p 'process.versions.node') from $node_bin"
|
||||
openclaw_prepend_node_bin "$(dirname "$node_bin")"
|
||||
else
|
||||
openclaw_download_node "$requested_node" || true
|
||||
fi
|
||||
|
||||
active_node_version="$(openclaw_active_node_version)"
|
||||
if ! openclaw_node_version_matches "$active_node_version" "$requested_node"; then
|
||||
echo "::error::Expected Node '${requested_node}', but active node is '${active_node_version:-missing}' at $(command -v node || true)"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
Reference in New Issue
Block a user