Files
elizaos--eliza/.github/actions/setup-bun-workspace/action.yml
T
wehub-resource-sync 426e9eeabd
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Waiting to run
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Waiting to run
Build Agent Image / build-and-push (push) Waiting to run
Chat shell gestures / Chat shell gesture + parity e2e (push) Waiting to run
ci / test (push) Waiting to run
ci / lint-and-format (push) Waiting to run
ci / build (push) Waiting to run
ci / dev-startup (push) Waiting to run
Cloud Gateway Discord / Test (push) Waiting to run
Cloud Gateway Webhook / Test (push) Waiting to run
Cloud Tests / lint-and-types (push) Waiting to run
Cloud Tests / unit-tests (push) Waiting to run
Cloud Tests / integration-tests (push) Waiting to run
Cloud Tests / e2e-tests (push) Blocked by required conditions
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Deploy Apps Worker (Product 2) / Determine environment (push) Waiting to run
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Blocked by required conditions
Deploy Eliza Provisioning Worker / Determine environment (push) Waiting to run
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Blocked by required conditions
Dev Smoke / Classify changed paths (push) Waiting to run
Dev Smoke / bun run dev onboarding chat (push) Blocked by required conditions
Dev Smoke / Vite HMR dependency-level smoke (push) Blocked by required conditions
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Waiting to run
gitleaks / gitleaks (push) Waiting to run
Markdown Links / Relative Markdown Links (push) Waiting to run
Publish @elizaos/example-code / check_npm (push) Waiting to run
Publish @elizaos/example-code / publish_npm (push) Blocked by required conditions
Publish @elizaos/plugin-elizacloud / verify_version (push) Waiting to run
Publish @elizaos/plugin-elizacloud / publish_npm (push) Blocked by required conditions
Quality (Extended) / Homepage Build (PR smoke) (push) Waiting to run
Quality (Extended) / Comment-only diff guard (push) Waiting to run
Quality (Extended) / Format + Type Safety Ratchet (push) Waiting to run
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Waiting to run
Quality (Extended) / Develop Gate (lint) (push) Waiting to run
Sandbox Live Smoke / Sandbox live smoke (push) Waiting to run
Snap Build & Test / Build Snap (amd64) (push) Waiting to run
Snap Build & Test / Build Snap (arm64) (push) Waiting to run
supply-chain / sbom (push) Waiting to run
supply-chain / vulnerability-scan (push) Waiting to run
Build, Push & Deploy to Phala Cloud / build-and-push (push) Waiting to run
Test Packaging / Validate Packaging Configs (push) Waiting to run
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Waiting to run
Test Packaging / Pack & Test JS Tarballs (push) Waiting to run
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Waiting to run
UI Fixture E2E / ui-fixture-e2e (push) Waiting to run
UI Fixture E2E / fixture-e2e (push) Waiting to run
UI Story Gate / story-gate (push) Waiting to run
vault-ci / test (macos-latest) (push) Waiting to run
vault-ci / test (ubuntu-latest) (push) Waiting to run
vault-ci / test (windows-latest) (push) Waiting to run
vault-ci / app-core wiring tests (push) Waiting to run
verify-patches / verify patches/CHECKSUMS.sha256 (push) Waiting to run
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Waiting to run
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Waiting to run
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Waiting to run
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Waiting to run
Voice Benchmark Smoke / voice bench smoke summary (push) Blocked by required conditions
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Waiting to run
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Waiting to run
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Waiting to run
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Waiting to run
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Waiting to run
Test Packaging / Build & Test PyPI Package (push) Waiting to run
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:43:05 +08:00

405 lines
19 KiB
YAML

name: "Setup Bun Workspace"
description: "Install Bun workspace dependencies and required native deps for eliza CI jobs."
inputs:
bun-version:
description: "Bun version to install"
required: false
default: "canary"
python-version:
description: "Python version for node-gyp fallbacks"
required: false
default: "3.12"
setup-python:
description: "Whether to set up Python for node-gyp fallbacks"
required: false
default: "true"
install-protoc:
description: "Whether to install protoc for Rust plugin builds"
required: false
default: "true"
force-protoc-github-fallback:
description: "Force the pinned GitHub protoc fallback path for setup action self-tests"
required: false
default: "false"
install-command:
description: "Dependency install command"
required: false
default: "bun install --frozen-lockfile --ignore-scripts"
install-native-deps:
description: "Whether to install native apt dependencies"
required: false
default: "true"
run-postinstall:
description: "Whether to run the repo postinstall step explicitly"
required: false
default: "true"
skip-avatar-clone:
description: "Set SKIP_AVATAR_CLONE=1 for postinstall"
required: false
default: "false"
no-vision-deps:
description: "Set ELIZA_NO_VISION_DEPS=1 for postinstall"
required: false
default: "false"
init-submodules:
description: "Run `git submodule update --init --recursive` before install"
required: false
default: "false"
setup-node:
description: >-
Whether to set up Node.js. Self-hosted runners are not guaranteed to have
`node` on PATH, and this action's very first step (turbo-cache-key) shells
out to `node`, so leave this on unless the caller already ran
actions/setup-node in the same job.
required: false
default: "true"
node-version:
description: "Node.js version to set up when setup-node is true"
required: false
default: "24"
runs:
using: "composite"
steps:
# Self-hosted (hetzner-robot) runners are not guaranteed to expose `node`
# on the step PATH, and the very next step (turbo-cache-github ->
# turbo-cache-key.mjs) invokes `node` directly. Without this, jobs that
# rely solely on this composite for their toolchain (coverage-gate,
# feed-env-audit, scenario-matrix, voice-workbench) fail with
# "node: command not found" on runners missing a system Node. setup-node is
# idempotent, so callers that already ran it (keyless-harness, the audits)
# just re-select the same version. Toggle off via setup-node: "false".
- name: Setup Node.js
if: ${{ inputs.setup-node == 'true' }}
# actions/setup-node@v4 (48b55a0)
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
with:
node-version: ${{ inputs.node-version }}
# Persist Turbo's local filesystem cache (.turbo) through the pinned
# GitHub-native shim (#12341) — the single Turbo cache regime, replacing the
# Vercel SaaS remote cache. The shim keys on the deterministic
# turbo-cache-key hash with no github.job fragment, so one entry is shared
# across every job instead of the ~30-way fragmentation the old per-job key
# caused; Turbo re-validates task hashes on restore, so a cross-job hit is
# always safe. This keeps the full 14-way typecheck off the runner on warm
# caches (cold ran it OOM/SIGTERM). No SaaS, no secrets; a miss is non-fatal.
- name: Restore Turbo cache (GitHub-native)
uses: ./.github/actions/turbo-cache-github
# actions/setup-python@v6 downloads a CPython build requiring the
# x86-64-v3 CPU baseline; some [self-hosted, hetzner-robot] runners expose
# a lower ISA and the download aborts with "CPU ISA level is lower than
# required" (exit 127), killing the whole step even though Python here is
# only a best-effort node-gyp fallback (#14188). GitHub-hosted runners are
# never affected, so this probe (and the fallback it enables) only runs on
# self-hosted runners via runner.environment — hosted CI takes the
# untouched actions/setup-python@v6 path below.
- name: Detect usable system Python (self-hosted ISA guard)
id: system-python
if: ${{ inputs.setup-python == 'true' && runner.environment == 'self-hosted' }}
shell: bash
run: |
if command -v python3 >/dev/null 2>&1 && python3 -c "import sys" >/dev/null 2>&1; then
echo "python-bin=$(command -v python3)" >> "$GITHUB_OUTPUT"
fi
- name: Setup Python
if: ${{ inputs.setup-python == 'true' && steps.system-python.outputs.python-bin == '' }}
# Skipped outright when the probe above found a usable system Python.
# If it didn't (a self-hosted runner with no system python3 either),
# don't let an ISA-incompatible download turn into a hard job failure —
# hosted runners are never in this branch and keep fail-fast behavior.
continue-on-error: ${{ runner.environment == 'self-hosted' }}
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python-version }}
- name: Install setuptools for distutils (Python 3.12+)
if: ${{ inputs.setup-python == 'true' }}
shell: bash
env:
RUNNER_ENVIRONMENT: ${{ runner.environment }}
run: |
python_bin="${{ steps.system-python.outputs.python-bin }}"
if [ -z "$python_bin" ]; then
python_bin="python3"
fi
if ! command -v "$python_bin" >/dev/null 2>&1; then
echo "::warning::No usable python3 found; skipping setuptools install (node-gyp fallback unavailable)"
exit 0
fi
if "$python_bin" -c 'import setuptools' >/dev/null 2>&1; then
echo "setuptools already present: $("$python_bin" -c 'import setuptools; print(setuptools.__version__)')"
exit 0
fi
if "$python_bin" - <<'PY'
import sys
raise SystemExit(0 if sys.version_info < (3, 12) else 1)
PY
then
echo "Python <3.12; setuptools distutils shim is not required"
exit 0
fi
if ! "$python_bin" -m pip --version >/dev/null 2>&1; then
if [ "$RUNNER_ENVIRONMENT" = "self-hosted" ]; then
echo "::warning::No usable pip found; skipping setuptools install (node-gyp fallback unavailable)"
exit 0
fi
echo "::error::No usable pip found for setuptools install"
exit 1
fi
"$python_bin" -m pip install setuptools \
|| "$python_bin" -m pip install --user setuptools \
|| "$python_bin" -m pip install --break-system-packages setuptools
- name: Install protoc
# protoc is required even when install-native-deps=false because Rust
# plugins (plugin-{discord,groq,openai,sql,whatsapp}) depend on the
# crates.io `elizaos` crate whose build script invokes prost-build.
# Without protoc, `cargo test` fails before any test runs.
if: ${{ inputs.install-protoc == 'true' && runner.os == 'Linux' }}
shell: bash
env:
FORCE_PROTOC_GITHUB_FALLBACK: ${{ inputs.force-protoc-github-fallback }}
PROTOC_RUNNER_ARCH: ${{ runner.arch }}
run: |
if [ "${FORCE_PROTOC_GITHUB_FALLBACK}" != "true" ] && command -v protoc >/dev/null 2>&1; then
echo "protoc already installed: $(protoc --version)"
exit 0
fi
apt_ok=""
if [ "${FORCE_PROTOC_GITHUB_FALLBACK}" = "true" ]; then
echo "forcing protoc GitHub fallback for setup action self-test"
elif command -v sudo >/dev/null 2>&1 && sudo -n true 2>/dev/null; then
# GitHub-hosted Ubuntu images include Microsoft feeds that are unrelated
# to our protoc install. If those feeds return 403, apt-get update fails
# before it can use the Ubuntu repositories we actually need.
while IFS= read -r source_file; do
if [[ "${source_file}" == *.disabled ]]; then
echo "Microsoft apt source already disabled: ${source_file}"
continue
fi
echo "Disabling unrelated Microsoft apt source: ${source_file}"
sudo mv "${source_file}" "${source_file}.disabled"
done < <(grep -RIl "packages.microsoft.com" /etc/apt/sources.list.d 2>/dev/null || true)
APT_ARGS=(-o Acquire::ForceIPv4=true -o Acquire::Retries=5 -o Acquire::http::Timeout=30)
for attempt in 1 2 3; do
if sudo apt-get "${APT_ARGS[@]}" update && \
sudo apt-get "${APT_ARGS[@]}" install -y --fix-missing protobuf-compiler; then
apt_ok=1
break
fi
sleep $((attempt * 10))
done
fi
if [ -z "$apt_ok" ]; then
# Sudo-free fallback: fetch the pinned protoc release into a user-writable
# dir and put it on PATH. Works on self-hosted runners without passwordless
# sudo or when Ubuntu mirrors fail. The checksum keeps the privileged
# build dependency from trusting an unauthenticated release asset.
echo "apt unavailable/failed; installing protoc from GitHub release (sudo-free)"
protoc_version=27.3
case "${PROTOC_RUNNER_ARCH}" in
X64)
protoc_asset_arch=x86_64
protoc_sha256=6dab2adab83f915126cab53540d48957c40e9e9023969c3e84d44bfb936c7741
;;
ARM64)
protoc_asset_arch=aarch_64
protoc_sha256=bdad36f3ad7472281d90568c4956ea2e203c216e0de005c6bd486f1920f2751c
;;
*)
echo "unsupported Linux runner architecture for protoc fallback: ${PROTOC_RUNNER_ARCH}"
exit 1
;;
esac
dest="${RUNNER_TEMP:-/tmp}/protoc"
rm -rf "$dest"
mkdir -p "$dest"
for attempt in 1 2 3; do
if curl -fsSL --retry 5 --retry-all-errors -o "$dest/protoc.zip" \
"https://github.com/protocolbuffers/protobuf/releases/download/v${protoc_version}/protoc-${protoc_version}-linux-${protoc_asset_arch}.zip"; then
break
fi
if [ "$attempt" -eq 3 ]; then
echo "protoc GitHub release download failed after ${attempt} attempts"
exit 1
fi
sleep $((attempt * 10))
done
echo "${protoc_sha256} $dest/protoc.zip" | sha256sum -c -
unzip -o "$dest/protoc.zip" -d "$dest" 'bin/protoc' 'include/*'
chmod +x "$dest/bin/protoc"
echo "$dest/bin" >> "$GITHUB_PATH"
export PATH="$dest/bin:$PATH"
fi
protoc --version
- name: Install native dependencies
if: ${{ inputs.install-native-deps == 'true' && runner.os == 'Linux' }}
shell: bash
run: |
if ! command -v sudo >/dev/null 2>&1 || ! sudo -n true 2>/dev/null; then
echo "::warning::passwordless sudo unavailable; skipping native apt deps (assuming runner image is pre-provisioned)"
else
# Some CI runners intermittently fail reaching Ubuntu mirrors over IPv6.
# Force IPv4 and retry apt commands so CI setup is resilient. Also
# disable unrelated Microsoft feeds because their 403s make apt-get
# update fail before Ubuntu packages can install.
while IFS= read -r source_file; do
if [[ "${source_file}" == *.disabled ]]; then
echo "Microsoft apt source already disabled: ${source_file}"
continue
fi
echo "Disabling unrelated Microsoft apt source: ${source_file}"
sudo mv "${source_file}" "${source_file}.disabled"
done < <(grep -RIl "packages.microsoft.com" /etc/apt/sources.list.d 2>/dev/null || true)
APT_ARGS=(-o Acquire::ForceIPv4=true -o Acquire::Retries=5 -o Acquire::http::Timeout=30)
for attempt in 1 2 3; do
if sudo apt-get "${APT_ARGS[@]}" update && \
sudo apt-get "${APT_ARGS[@]}" install -y --fix-missing \
build-essential libcairo2-dev libpango1.0-dev libjpeg-dev \
libgif-dev libpixman-1-dev librsvg2-dev; then
break
fi
if [ "$attempt" -eq 3 ]; then
echo "apt install failed after ${attempt} attempts"
exit 1
fi
sleep $((attempt * 10))
done
fi
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ inputs.bun-version }}
- name: Cache Bun install
# Cache the Bun install store on every OS, Linux included (#12338). A miss
# just means no speedup and a cache write failure is non-fatal, so this is
# additive: worst case a cold Linux runner behaves exactly as before while
# warm runners skip re-downloading the dependency tarballs. Pinned by SHA
# (no floating @v5) to avoid the Bun/cache-action drift #12338 targets.
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
with:
path: ~/.bun/install/cache
key: bun-${{ runner.os }}-${{ inputs.bun-version }}-${{ hashFiles('bun.lock') }}
restore-keys: |
bun-${{ runner.os }}-${{ inputs.bun-version }}-
bun-${{ runner.os }}-
- name: Initialize submodules
if: ${{ inputs.init-submodules == 'true' }}
shell: bash
run: git submodule update --init --recursive
- name: Install dependencies
shell: bash
run: |
install_command="${{ inputs.install-command }}"
for attempt in 1 2; do
install_log="$(mktemp)"
if bash -lc "$install_command" 2>&1 | tee "$install_log"; then
rm -f "$install_log"
exit 0
fi
install_status=${PIPESTATUS[0]}
if grep -q "lockfile had changes, but lockfile is frozen" "$install_log" && \
[[ "$install_command" == *"--frozen-lockfile"* ]]; then
fallback_command="${install_command/--frozen-lockfile/--no-frozen-lockfile}"
echo "::warning::Bun reported frozen lockfile drift; falling back to a non-frozen install."
if bash -lc "$fallback_command"; then
# The frozen install above is the canonical lockfile-drift signal.
# When it fails we fall back to a non-frozen install — but Bun
# canary (this repo tracks `bun-version: canary`) reserializes
# bun.lock byte-for-byte between versions even when the resolved
# dependency graph is identical, so a `git diff` here fails on
# serialization noise rather than real drift. Restore the committed
# lockfile after installing; the dedicated lockfile-drift gates
# (cloud-gateway-*, cache-key-stability) still catch genuine drift.
if ! git diff --quiet -- bun.lock; then
echo "::warning::bun.lock changed during install (bun-canary reserialization); restoring the committed lockfile."
git checkout -- bun.lock
fi
rm -f "$install_log"
exit 0
fi
fi
rm -f "$install_log"
if [ "$attempt" -eq 2 ]; then
exit "$install_status"
fi
echo "Dependency install failed; retrying once after cleaning Bun's install cache."
node packages/scripts/rm-path-recursive.mjs ~/.bun/install/cache
sleep 5
done
env:
# When the system-Python guard above found a usable interpreter,
# `pythonLocation` is never set (actions/setup-python@v6 was skipped),
# so prefer the probed binary and only fall back to the downloaded
# toolcache path.
npm_config_python: ${{ steps.system-python.outputs.python-bin || format('{0}/bin/python3', env.pythonLocation) }}
- name: Materialize bun npm package binary
if: ${{ runner.os != 'Windows' }}
# The default install command uses --ignore-scripts so trustedDependencies
# postinstalls don't run silently. The `bun` npm package ships a placeholder
# binary that errors with "Bun's postinstall script was not run" until its
# own install.js downloads the real binary; run it explicitly so any
# subsequent `bun ...` invocation (including the repo postinstall) works.
#
# Bun's hoisted install layout puts the package at
# node_modules/.bun/bun@<ver>/node_modules/bun/install.js rather
# than the legacy node_modules/bun/install.js, so check the
# hoisted path too. Recent bun versions nest deeper (the cache
# uses content-addressable hash dirs); raised maxdepth from 4 to
# 8 and added a final unbounded fallback so every layout we've
# seen in the wild gets caught.
shell: bash
run: |
install_js=""
if [ -f node_modules/bun/install.js ]; then
install_js="node_modules/bun/install.js"
elif [ -d node_modules/.bun ]; then
install_js="$(find node_modules/.bun -maxdepth 8 -path '*/bun@*/node_modules/bun/install.js' -print -quit 2>/dev/null)"
fi
# Final fallback: scan the whole node_modules tree (slower but
# exhaustive). Skip if we already found one above.
if { [ -z "$install_js" ] || [ ! -f "$install_js" ]; } && [ -d node_modules ]; then
install_js="$(find node_modules -path '*/bun/install.js' -print -quit 2>/dev/null)"
fi
if [ -n "$install_js" ] && [ -f "$install_js" ]; then
echo "Running bun postinstall: $install_js"
(cd "$(dirname "$install_js")" && node install.js)
else
echo "::warning::No bun/install.js found; downstream bun run calls may fail"
fi
- name: Run repository postinstall
if: ${{ inputs.run-postinstall == 'true' }}
shell: bash
run: bun run postinstall
env:
SKIP_AVATAR_CLONE: ${{ inputs.skip-avatar-clone == 'true' && '1' || '' }}
ELIZA_NO_VISION_DEPS: ${{ inputs.no-vision-deps == 'true' && '1' || '' }}
- name: Ensure workspace + native-plugin symlinks (postinstall safety net)
# `bun run postinstall` chains `bun scripts/patch-*` steps before
# `ensure-workspace-symlinks.mjs` / `ensure-native-plugins-linked.mjs`;
# if a `bun` invocation fails partway (e.g. the placeholder binary),
# those symlinks never get created and downstream vitest jobs hit
# "Failed to resolve entry for @elizaos/<workspace-pkg>". Run them
# explicitly with `node` (always available) so the symlinks exist
# regardless of how postinstall fared. Both scripts are idempotent.
shell: bash
run: |
node packages/scripts/ensure-workspace-symlinks.mjs || echo "::warning::ensure-workspace-symlinks failed"
node packages/scripts/ensure-native-plugins-linked.mjs || echo "::warning::ensure-native-plugins-linked failed"
- name: Ensure generated shared i18n data
shell: bash
run: node packages/app-core/scripts/ensure-shared-i18n-data.mjs