mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:20:43 +00:00
test: add update migration package gate
This commit is contained in:
@@ -34,17 +34,17 @@ on:
|
||||
default: 1
|
||||
type: number
|
||||
published_upgrade_survivor_baseline:
|
||||
description: Published OpenClaw package baseline for the published-upgrade-survivor Docker lane
|
||||
description: Published OpenClaw package baseline for the published-upgrade-survivor/update-migration Docker lane
|
||||
required: false
|
||||
default: openclaw@latest
|
||||
type: string
|
||||
published_upgrade_survivor_baselines:
|
||||
description: Optional exact baseline list for published-upgrade-survivor lane expansion
|
||||
description: Optional exact baseline list for published-upgrade-survivor/update-migration lane expansion
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
published_upgrade_survivor_scenarios:
|
||||
description: Optional scenario list for published-upgrade-survivor lane expansion
|
||||
description: Optional scenario list for published-upgrade-survivor/update-migration lane expansion
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
@@ -129,17 +129,17 @@ on:
|
||||
default: 1
|
||||
type: number
|
||||
published_upgrade_survivor_baseline:
|
||||
description: Published OpenClaw package baseline for the published-upgrade-survivor Docker lane
|
||||
description: Published OpenClaw package baseline for the published-upgrade-survivor/update-migration Docker lane
|
||||
required: false
|
||||
default: openclaw@latest
|
||||
type: string
|
||||
published_upgrade_survivor_baselines:
|
||||
description: Optional exact baseline list for published-upgrade-survivor lane expansion
|
||||
description: Optional exact baseline list for published-upgrade-survivor/update-migration lane expansion
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
published_upgrade_survivor_scenarios:
|
||||
description: Optional scenario list for published-upgrade-survivor lane expansion
|
||||
description: Optional scenario list for published-upgrade-survivor/update-migration lane expansion
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
|
||||
10
.github/workflows/package-acceptance.yml
vendored
10
.github/workflows/package-acceptance.yml
vendored
@@ -70,12 +70,12 @@ on:
|
||||
default: openclaw@latest
|
||||
type: string
|
||||
published_upgrade_survivor_baselines:
|
||||
description: Optional baseline list for published-upgrade-survivor; use release-history for last 6 plus key legacy releases
|
||||
description: Optional baseline list for published-upgrade-survivor/update-migration; use release-history or all-since-2026.4.23
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
published_upgrade_survivor_scenarios:
|
||||
description: Optional scenario list for published-upgrade-survivor; use reported-issues for known upgrade failure shapes
|
||||
description: Optional scenario list for published-upgrade-survivor/update-migration; use reported-issues for known upgrade failure shapes
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
@@ -150,12 +150,12 @@ on:
|
||||
default: openclaw@latest
|
||||
type: string
|
||||
published_upgrade_survivor_baselines:
|
||||
description: Optional baseline list for published-upgrade-survivor; use release-history for last 6 plus key legacy releases
|
||||
description: Optional baseline list for published-upgrade-survivor/update-migration; use release-history or all-since-2026.4.23
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
published_upgrade_survivor_scenarios:
|
||||
description: Optional scenario list for published-upgrade-survivor; use reported-issues for known upgrade failure shapes
|
||||
description: Optional scenario list for published-upgrade-survivor/update-migration; use reported-issues for known upgrade failure shapes
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
@@ -442,7 +442,7 @@ jobs:
|
||||
fi
|
||||
releases_json=""
|
||||
npm_versions_json=""
|
||||
if [[ "$REQUESTED_BASELINES" == *"release-history"* ]]; then
|
||||
if [[ "$REQUESTED_BASELINES" == *"release-history"* || "$REQUESTED_BASELINES" == *"all-since-"* ]]; then
|
||||
releases_json=".artifacts/package-candidate-input/openclaw-releases.json"
|
||||
npm_versions_json=".artifacts/package-candidate-input/openclaw-npm-versions.json"
|
||||
mkdir -p "$(dirname "$releases_json")"
|
||||
|
||||
46
.github/workflows/update-migration.yml
vendored
Normal file
46
.github/workflows/update-migration.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: Update Migration
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
workflow_ref:
|
||||
description: Trusted workflow/harness ref
|
||||
default: main
|
||||
required: true
|
||||
type: string
|
||||
package_ref:
|
||||
description: Branch, tag, or SHA to package as the update target
|
||||
default: main
|
||||
required: true
|
||||
type: string
|
||||
baselines:
|
||||
description: Published baselines to migrate; use all-since-2026.4.23 for full coverage
|
||||
default: all-since-2026.4.23
|
||||
required: true
|
||||
type: string
|
||||
scenarios:
|
||||
description: Update survivor scenarios
|
||||
default: plugin-deps-cleanup
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
packages: write
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
update_migration:
|
||||
name: Update migration matrix
|
||||
uses: ./.github/workflows/package-acceptance.yml
|
||||
with:
|
||||
workflow_ref: ${{ inputs.workflow_ref }}
|
||||
source: ref
|
||||
package_ref: ${{ inputs.package_ref }}
|
||||
suite_profile: custom
|
||||
docker_lanes: update-migration
|
||||
published_upgrade_survivor_baselines: ${{ inputs.baselines }}
|
||||
published_upgrade_survivor_scenarios: ${{ inputs.scenarios }}
|
||||
telegram_mode: none
|
||||
secrets: inherit
|
||||
@@ -192,7 +192,7 @@ For the dedicated update and plugin testing policy, including local commands,
|
||||
Docker lanes, Package Acceptance inputs, release defaults, and failure triage,
|
||||
see [Testing updates and plugins](/help/testing-updates-plugins).
|
||||
|
||||
Release checks call Package Acceptance with `source=artifact`, the prepared release package artifact, `suite_profile=custom`, `docker_lanes='doctor-switch update-channel-switch upgrade-survivor published-upgrade-survivor plugins-offline plugin-update'`, `published_upgrade_survivor_baselines=release-history`, `published_upgrade_survivor_scenarios=reported-issues`, and `telegram_mode=mock-openai`. This keeps package migration, update, stale-plugin-dependency cleanup, offline plugin, plugin-update, and Telegram proof on the same resolved package tarball. Cross-OS release checks still cover OS-specific onboarding, installer, and platform behavior; package/update product validation should start with Package Acceptance. The `published-upgrade-survivor` Docker lane validates one published package baseline per run. In Package Acceptance, the resolved `package-under-test` tarball is always the candidate and `published_upgrade_survivor_baseline` selects the fallback published baseline, defaulting to `openclaw@latest`; failed-lane rerun commands preserve that baseline. Set `published_upgrade_survivor_baselines=release-history` to expand the lane across a deduped history matrix: the latest six stable releases, `2026.4.23`, and the latest stable release before `2026-03-15`. Set `published_upgrade_survivor_scenarios=reported-issues` to expand the same baselines across issue-shaped fixtures for Feishu config, preserved bootstrap/persona files, tilde log paths, and stale legacy plugin dependency roots. Local aggregate runs can pass exact package specs with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPECS`, keep a single lane with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPEC` such as `openclaw@2026.4.15`, or set `OPENCLAW_UPGRADE_SURVIVOR_SCENARIOS` for the scenario matrix. The published lane configures the baseline with a baked `openclaw config set` command recipe, records recipe steps in `summary.json`, and probes `/healthz`, `/readyz`, plus RPC status after Gateway start. The Windows packaged and installer fresh lanes also verify that an installed package can import a browser-control override from a raw absolute Windows path. The OpenAI cross-OS agent-turn smoke defaults to `OPENCLAW_CROSS_OS_OPENAI_MODEL` when set, otherwise `openai/gpt-5.5`, so the install and gateway proof stays on the preferred GPT-5 test model.
|
||||
Release checks call Package Acceptance with `source=artifact`, the prepared release package artifact, `suite_profile=custom`, `docker_lanes='doctor-switch update-channel-switch upgrade-survivor published-upgrade-survivor plugins-offline plugin-update'`, `published_upgrade_survivor_baselines=release-history`, `published_upgrade_survivor_scenarios=reported-issues`, and `telegram_mode=mock-openai`. This keeps package migration, update, stale-plugin-dependency cleanup, offline plugin, plugin-update, and Telegram proof on the same resolved package tarball. Cross-OS release checks still cover OS-specific onboarding, installer, and platform behavior; package/update product validation should start with Package Acceptance. The `published-upgrade-survivor` Docker lane validates one published package baseline per run. In Package Acceptance, the resolved `package-under-test` tarball is always the candidate and `published_upgrade_survivor_baseline` selects the fallback published baseline, defaulting to `openclaw@latest`; failed-lane rerun commands preserve that baseline. Set `published_upgrade_survivor_baselines=release-history` to expand the lane across a deduped history matrix: the latest six stable releases, `2026.4.23`, and the latest stable release before `2026-03-15`. Set `published_upgrade_survivor_scenarios=reported-issues` to expand the same baselines across issue-shaped fixtures for Feishu config, preserved bootstrap/persona files, tilde log paths, and stale legacy plugin dependency roots. The separate `Update Migration` workflow uses the `update-migration` Docker lane with `all-since-2026.4.23` and `plugin-deps-cleanup` when the question is exhaustive published update cleanup, not normal Full Release CI breadth. Local aggregate runs can pass exact package specs with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPECS`, keep a single lane with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPEC` such as `openclaw@2026.4.15`, or set `OPENCLAW_UPGRADE_SURVIVOR_SCENARIOS` for the scenario matrix. The published lane configures the baseline with a baked `openclaw config set` command recipe, records recipe steps in `summary.json`, and probes `/healthz`, `/readyz`, plus RPC status after Gateway start. The Windows packaged and installer fresh lanes also verify that an installed package can import a browser-control override from a raw absolute Windows path. The OpenAI cross-OS agent-turn smoke defaults to `OPENCLAW_CROSS_OS_OPENAI_MODEL` when set, otherwise `openai/gpt-5.5`, so the install and gateway proof stays on the preferred GPT-5 test model.
|
||||
|
||||
### Legacy compatibility windows
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ pnpm test:docker:plugins
|
||||
pnpm test:docker:plugin-update
|
||||
pnpm test:docker:upgrade-survivor
|
||||
pnpm test:docker:published-upgrade-survivor
|
||||
pnpm test:docker:update-migration
|
||||
```
|
||||
|
||||
Important lanes:
|
||||
@@ -95,6 +96,12 @@ Important lanes:
|
||||
configures it through a baked `openclaw config set` recipe, updates it to the
|
||||
candidate tarball, runs doctor, checks legacy cleanup, starts the Gateway, and
|
||||
probes `/healthz`, `/readyz`, and RPC status.
|
||||
- `test:docker:update-migration` is the cleanup-heavy published-update lane. It
|
||||
starts from a configured Discord/Telegram-style user state, runs baseline
|
||||
doctor so configured plugin dependencies have a chance to materialize, seeds
|
||||
legacy plugin dependency debris for a configured packaged plugin, updates to
|
||||
the candidate tarball, and requires post-update doctor to remove the legacy
|
||||
dependency roots.
|
||||
|
||||
Useful published-upgrade survivor variants:
|
||||
|
||||
@@ -109,10 +116,24 @@ pnpm test:docker:published-upgrade-survivor
|
||||
```
|
||||
|
||||
Available scenarios are `base`, `feishu-channel`, `bootstrap-persona`,
|
||||
`tilde-log-path`, and `versioned-runtime-deps`. In aggregate runs,
|
||||
`plugin-deps-cleanup`, `tilde-log-path`, and `versioned-runtime-deps`. In aggregate runs,
|
||||
`OPENCLAW_UPGRADE_SURVIVOR_SCENARIOS=reported-issues` expands to all reported
|
||||
issue-shaped scenarios.
|
||||
|
||||
Full update migration is intentionally separate from Full Release CI. Use the
|
||||
manual `Update Migration` workflow when the release question is "can every
|
||||
published stable release from 2026.4.23 onward update to this candidate and
|
||||
clean up plugin dependency debris?":
|
||||
|
||||
```bash
|
||||
gh workflow run update-migration.yml \
|
||||
--ref main \
|
||||
-f workflow_ref=main \
|
||||
-f package_ref=main \
|
||||
-f baselines=all-since-2026.4.23 \
|
||||
-f scenarios=plugin-deps-cleanup
|
||||
```
|
||||
|
||||
## Package Acceptance
|
||||
|
||||
Package Acceptance is the GitHub-native package gate. It resolves one candidate
|
||||
@@ -148,6 +169,11 @@ This keeps package migration, update channel switching, stale plugin dependency
|
||||
cleanup, offline plugin coverage, plugin update behavior, and Telegram package
|
||||
QA on the same resolved artifact.
|
||||
|
||||
`release-history` is a bounded release-check sample: latest six stable releases,
|
||||
`2026.4.23`, and one older pre-date anchor. For exhaustive published update
|
||||
migration coverage, use `all-since-2026.4.23` in the separate Update Migration
|
||||
workflow instead of Full Release CI.
|
||||
|
||||
Run a package profile manually when validating a candidate before release:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -445,6 +445,8 @@ The canonical checklist for update and plugin validation is
|
||||
[Testing updates and plugins](/help/testing-updates-plugins). Use it when
|
||||
deciding which local, Docker, Package Acceptance, or release-check lane proves a
|
||||
plugin install/update, doctor cleanup, or published-package migration change.
|
||||
Exhaustive published update migration from every stable `2026.4.23+` package is
|
||||
a separate manual `Update Migration` workflow, not part of Full Release CI.
|
||||
|
||||
Legacy package-acceptance leniency is intentionally time boxed. Packages through
|
||||
`2026.4.25` may use the compatibility path for metadata gaps already published
|
||||
|
||||
@@ -45,6 +45,7 @@ title: "Tests"
|
||||
- `pnpm test:docker:mcp-channels`: Starts a seeded Gateway container and a second client container that spawns `openclaw mcp serve`, then verifies routed conversation discovery, transcript reads, attachment metadata, live event queue behavior, outbound send routing, and Claude-style channel + permission notifications over the real stdio bridge. The Claude notification assertion reads the raw stdio MCP frames directly so the smoke reflects what the bridge actually emits.
|
||||
- `pnpm test:docker:upgrade-survivor`: Installs the packed OpenClaw tarball over a dirty old-user fixture, runs package update plus non-interactive doctor without live provider or channel keys, then starts a loopback Gateway and checks that agents, channel config, plugin allowlists, workspace/session files, stale legacy plugin dependency state, startup, and RPC status survive.
|
||||
- `pnpm test:docker:published-upgrade-survivor`: Installs `openclaw@latest` by default, seeds realistic existing-user files without live provider or channel keys, configures that baseline with a baked `openclaw config set` command recipe, updates that published install to the packed OpenClaw tarball, runs non-interactive doctor, writes `.artifacts/upgrade-survivor/summary.json`, then starts a loopback Gateway and checks that configured intents, workspace/session files, stale plugin config and legacy dependency state, startup, `/healthz`, `/readyz`, and RPC status survive or repair cleanly. Override one baseline with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPEC`, expand an exact matrix with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPECS`, or add scenario fixtures with `OPENCLAW_UPGRADE_SURVIVOR_SCENARIOS=reported-issues`; Package Acceptance exposes those as `published_upgrade_survivor_baseline`, `published_upgrade_survivor_baselines`, and `published_upgrade_survivor_scenarios`.
|
||||
- `pnpm test:docker:update-migration`: Runs the published-upgrade survivor harness in the cleanup-heavy `plugin-deps-cleanup` scenario, starting at `openclaw@2026.4.23` by default. The separate `Update Migration` workflow expands this lane with `baselines=all-since-2026.4.23` so every stable published package from `.23` onward updates to the candidate and proves configured-plugin dependency cleanup outside Full Release CI.
|
||||
|
||||
## Local PR gate
|
||||
|
||||
|
||||
@@ -1518,6 +1518,7 @@
|
||||
"test:docker:session-runtime-context": "bash scripts/e2e/session-runtime-context-docker.sh",
|
||||
"test:docker:timings": "node scripts/docker-e2e-timings.mjs",
|
||||
"test:docker:update-channel-switch": "bash scripts/e2e/update-channel-switch-docker.sh",
|
||||
"test:docker:update-migration": "env OPENCLAW_UPGRADE_SURVIVOR_PUBLISHED_BASELINE=1 OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPEC=${OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPEC:-openclaw@2026.4.23} OPENCLAW_UPGRADE_SURVIVOR_SCENARIO=${OPENCLAW_UPGRADE_SURVIVOR_SCENARIO:-plugin-deps-cleanup} bash scripts/e2e/upgrade-survivor-docker.sh",
|
||||
"test:docker:upgrade-survivor": "bash scripts/e2e/upgrade-survivor-docker.sh",
|
||||
"test:e2e": "node scripts/run-vitest.mjs run --config test/vitest/vitest.e2e.config.ts",
|
||||
"test:e2e:openshell": "OPENCLAW_E2E_OPENSHELL=1 node scripts/run-vitest.mjs run --config test/vitest/vitest.e2e.config.ts extensions/openshell/src/backend.e2e.test.ts",
|
||||
|
||||
@@ -6,6 +6,7 @@ const SCENARIOS = new Set([
|
||||
"base",
|
||||
"feishu-channel",
|
||||
"bootstrap-persona",
|
||||
"plugin-deps-cleanup",
|
||||
"tilde-log-path",
|
||||
"versioned-runtime-deps",
|
||||
]);
|
||||
|
||||
@@ -53,6 +53,7 @@ BASELINE_INSTALL_LOG="$ARTIFACT_ROOT/baseline-install.log"
|
||||
UPDATE_JSON="$ARTIFACT_ROOT/update.json"
|
||||
UPDATE_ERR="$ARTIFACT_ROOT/update.err"
|
||||
DOCTOR_LOG="$ARTIFACT_ROOT/doctor.log"
|
||||
BASELINE_DOCTOR_LOG="$ARTIFACT_ROOT/baseline-doctor.log"
|
||||
GATEWAY_LOG="$ARTIFACT_ROOT/gateway.log"
|
||||
HEALTHZ_JSON="$ARTIFACT_ROOT/healthz.json"
|
||||
READYZ_JSON="$ARTIFACT_ROOT/readyz.json"
|
||||
@@ -260,6 +261,123 @@ legacy_runtime_deps_symlink_source() {
|
||||
"$plugin"
|
||||
}
|
||||
|
||||
plugin_deps_cleanup_enabled() {
|
||||
[ "$SCENARIO" = "plugin-deps-cleanup" ]
|
||||
}
|
||||
|
||||
plugin_deps_cleanup_plugins() {
|
||||
printf '%s\n' "${OPENCLAW_UPGRADE_SURVIVOR_PLUGIN_DEPS_CLEANUP_PLUGINS:-discord telegram}"
|
||||
}
|
||||
|
||||
legacy_plugin_dependency_probe_paths() {
|
||||
local plugin="$1"
|
||||
local plugin_dir
|
||||
plugin_dir="$(package_root)/dist/extensions/$plugin"
|
||||
printf '%s\n' \
|
||||
"$plugin_dir/node_modules" \
|
||||
"$plugin_dir/.openclaw-runtime-deps.json" \
|
||||
"$plugin_dir/.openclaw-runtime-deps-stamp.json" \
|
||||
"$plugin_dir/.openclaw-runtime-deps-copy-upgrade-survivor" \
|
||||
"$plugin_dir/.openclaw-install-stage-upgrade-survivor" \
|
||||
"$plugin_dir/.openclaw-pnpm-store" \
|
||||
"$(package_root)/.local/bundled-plugin-runtime-deps/$plugin-upgrade-survivor" \
|
||||
"$OPENCLAW_STATE_DIR/.local/bundled-plugin-runtime-deps/$plugin-upgrade-survivor" \
|
||||
"$OPENCLAW_STATE_DIR/plugin-runtime-deps/$plugin-upgrade-survivor"
|
||||
}
|
||||
|
||||
install_baseline_plugin_dependencies() {
|
||||
plugin_deps_cleanup_enabled || return 0
|
||||
echo "Running baseline doctor to install configured plugin dependencies before update."
|
||||
if ! openclaw doctor --fix --non-interactive >"$BASELINE_DOCTOR_LOG" 2>&1; then
|
||||
echo "baseline openclaw doctor failed while preparing plugin dependency cleanup scenario" >&2
|
||||
cat "$BASELINE_DOCTOR_LOG" >&2 || true
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
seed_legacy_plugin_dependency_debris() {
|
||||
plugin_deps_cleanup_enabled || return 0
|
||||
|
||||
local found=0
|
||||
local plugin
|
||||
for plugin in $(plugin_deps_cleanup_plugins); do
|
||||
local plugin_dir
|
||||
plugin_dir="$(package_root)/dist/extensions/$plugin"
|
||||
if [ ! -d "$plugin_dir" ]; then
|
||||
continue
|
||||
fi
|
||||
found=1
|
||||
mkdir -p \
|
||||
"$plugin_dir/node_modules/openclaw-upgrade-survivor-dep" \
|
||||
"$plugin_dir/.openclaw-runtime-deps-copy-upgrade-survivor/node_modules/openclaw-upgrade-survivor-dep" \
|
||||
"$plugin_dir/.openclaw-install-stage-upgrade-survivor" \
|
||||
"$plugin_dir/.openclaw-pnpm-store" \
|
||||
"$(package_root)/.local/bundled-plugin-runtime-deps/$plugin-upgrade-survivor/node_modules/openclaw-upgrade-survivor-dep" \
|
||||
"$OPENCLAW_STATE_DIR/.local/bundled-plugin-runtime-deps/$plugin-upgrade-survivor/node_modules/openclaw-upgrade-survivor-dep" \
|
||||
"$OPENCLAW_STATE_DIR/plugin-runtime-deps/$plugin-upgrade-survivor/node_modules/openclaw-upgrade-survivor-dep"
|
||||
printf '{"name":"openclaw-upgrade-survivor-dep","version":"0.0.0"}\n' \
|
||||
>"$plugin_dir/node_modules/openclaw-upgrade-survivor-dep/package.json"
|
||||
printf '{"plugin":"%s","scenario":"plugin-deps-cleanup"}\n' "$plugin" \
|
||||
>"$plugin_dir/.openclaw-runtime-deps.json"
|
||||
printf '{"plugin":"%s","scenario":"plugin-deps-cleanup","stale":true}\n' "$plugin" \
|
||||
>"$plugin_dir/.openclaw-runtime-deps-stamp.json"
|
||||
printf '{"name":"openclaw-upgrade-survivor-dep","version":"0.0.0"}\n' \
|
||||
>"$plugin_dir/.openclaw-runtime-deps-copy-upgrade-survivor/node_modules/openclaw-upgrade-survivor-dep/package.json"
|
||||
printf '{"name":"openclaw-upgrade-survivor-dep","version":"0.0.0"}\n' \
|
||||
>"$(package_root)/.local/bundled-plugin-runtime-deps/$plugin-upgrade-survivor/node_modules/openclaw-upgrade-survivor-dep/package.json"
|
||||
printf '{"name":"openclaw-upgrade-survivor-dep","version":"0.0.0"}\n' \
|
||||
>"$OPENCLAW_STATE_DIR/.local/bundled-plugin-runtime-deps/$plugin-upgrade-survivor/node_modules/openclaw-upgrade-survivor-dep/package.json"
|
||||
printf '{"name":"openclaw-upgrade-survivor-dep","version":"0.0.0"}\n' \
|
||||
>"$OPENCLAW_STATE_DIR/plugin-runtime-deps/$plugin-upgrade-survivor/node_modules/openclaw-upgrade-survivor-dep/package.json"
|
||||
echo "Seeded legacy plugin dependency debris for configured plugin: $plugin"
|
||||
done
|
||||
|
||||
if [ "$found" -ne 1 ]; then
|
||||
echo "plugin-deps-cleanup scenario could not find a packaged Discord or Telegram plugin directory" >&2
|
||||
find "$(package_root)/dist" -maxdepth 3 -type d 2>/dev/null >&2 || true
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_legacy_plugin_dependency_debris_present() {
|
||||
plugin_deps_cleanup_enabled || return 0
|
||||
|
||||
local found=0
|
||||
local plugin
|
||||
for plugin in $(plugin_deps_cleanup_plugins); do
|
||||
local probe
|
||||
while IFS= read -r probe; do
|
||||
if [ -e "$probe" ] || [ -L "$probe" ]; then
|
||||
found=1
|
||||
fi
|
||||
done < <(legacy_plugin_dependency_probe_paths "$plugin")
|
||||
done
|
||||
if [ "$found" -ne 1 ]; then
|
||||
echo "plugin-deps-cleanup scenario did not create legacy plugin dependency debris" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_legacy_plugin_dependency_debris_cleaned() {
|
||||
plugin_deps_cleanup_enabled || return 0
|
||||
|
||||
local remaining=0
|
||||
local plugin
|
||||
for plugin in $(plugin_deps_cleanup_plugins); do
|
||||
local probe
|
||||
while IFS= read -r probe; do
|
||||
if [ -e "$probe" ] || [ -L "$probe" ]; then
|
||||
echo "legacy plugin dependency debris survived update/doctor: $probe" >&2
|
||||
remaining=1
|
||||
fi
|
||||
done < <(legacy_plugin_dependency_probe_paths "$plugin")
|
||||
done
|
||||
if [ "$remaining" -ne 0 ]; then
|
||||
return 1
|
||||
fi
|
||||
echo "Legacy plugin dependency debris cleaned for configured plugin dependencies."
|
||||
}
|
||||
|
||||
seed_legacy_runtime_deps_symlink() {
|
||||
local plugin
|
||||
plugin="$(legacy_runtime_deps_symlink_plugin)" || {
|
||||
@@ -532,11 +650,16 @@ phase install-baseline install_baseline
|
||||
phase seed-state seed_state
|
||||
phase apply-baseline-config-recipe apply_baseline_config_recipe
|
||||
phase validate-baseline-config validate_baseline_config
|
||||
phase install-baseline-plugin-dependencies install_baseline_plugin_dependencies
|
||||
phase seed-legacy-plugin-dependency-debris seed_legacy_plugin_dependency_debris
|
||||
phase assert-legacy-plugin-dependency-debris assert_legacy_plugin_dependency_debris_present
|
||||
phase assert-baseline assert_baseline_state
|
||||
phase seed-legacy-runtime-deps-symlink seed_legacy_runtime_deps_symlink
|
||||
phase resolve-candidate resolve_candidate_version
|
||||
phase update-candidate update_candidate
|
||||
phase assert-legacy-plugin-dependency-debris-before-doctor assert_legacy_plugin_dependency_debris_present
|
||||
phase doctor run_doctor
|
||||
phase assert-legacy-plugin-dependency-debris-cleaned assert_legacy_plugin_dependency_debris_cleaned
|
||||
phase assert-legacy-runtime-deps-symlink-repaired assert_legacy_runtime_deps_symlink_repaired
|
||||
phase validate-post-doctor-config validate_post_doctor_config
|
||||
phase assert-survival assert_survival
|
||||
|
||||
@@ -73,6 +73,7 @@ export const UPGRADE_SURVIVOR_SCENARIOS = [
|
||||
"base",
|
||||
"feishu-channel",
|
||||
"bootstrap-persona",
|
||||
"plugin-deps-cleanup",
|
||||
"tilde-log-path",
|
||||
"versioned-runtime-deps",
|
||||
];
|
||||
@@ -153,7 +154,7 @@ export function expandUpgradeSurvivorBaselineLanes(poolLanes, rawBaselineSpecs,
|
||||
return poolLanes;
|
||||
}
|
||||
return poolLanes.flatMap((poolLane) => {
|
||||
if (poolLane.name !== "published-upgrade-survivor") {
|
||||
if (poolLane.name !== "published-upgrade-survivor" && poolLane.name !== "update-migration") {
|
||||
return [poolLane];
|
||||
}
|
||||
const matrixBaselines = baselineSpecs.length > 0 ? baselineSpecs : [undefined];
|
||||
|
||||
@@ -222,6 +222,11 @@ export const mainLanes = [
|
||||
weight: 3,
|
||||
},
|
||||
),
|
||||
npmLane("update-migration", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:update-migration", {
|
||||
stateScenario: "upgrade-survivor",
|
||||
timeoutMs: 30 * 60 * 1000,
|
||||
weight: 3,
|
||||
}),
|
||||
lane("plugins", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:plugins", {
|
||||
resources: ["npm", "service"],
|
||||
stateScenario: "empty",
|
||||
|
||||
@@ -50,6 +50,29 @@ function stableVersionFromTag(tagName) {
|
||||
return version;
|
||||
}
|
||||
|
||||
function parseStableVersion(version) {
|
||||
const match = /^([0-9]{4})\.([0-9]+)\.([0-9]+)(?:-([0-9]+))?$/u.exec(String(version ?? ""));
|
||||
if (!match) {
|
||||
return undefined;
|
||||
}
|
||||
return match.slice(1).map((part) => Number.parseInt(part ?? "0", 10));
|
||||
}
|
||||
|
||||
function compareStableVersions(left, right) {
|
||||
const leftParts = parseStableVersion(left);
|
||||
const rightParts = parseStableVersion(right);
|
||||
if (!leftParts || !rightParts) {
|
||||
throw new Error(`cannot compare release versions: ${left} ${right}`);
|
||||
}
|
||||
for (let index = 0; index < Math.max(leftParts.length, rightParts.length); index += 1) {
|
||||
const delta = (leftParts[index] ?? 0) - (rightParts[index] ?? 0);
|
||||
if (delta !== 0) {
|
||||
return delta;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function npmPublishedVersion(version, publishedVersions) {
|
||||
if (!version || !publishedVersions) {
|
||||
return version;
|
||||
@@ -105,6 +128,20 @@ export function resolveReleaseHistory(args) {
|
||||
return dedupeSpecs(versions);
|
||||
}
|
||||
|
||||
export function resolveAllSince(args, minimumVersion) {
|
||||
const releasesJson = args.get("releases-json");
|
||||
if (!releasesJson) {
|
||||
throw new Error("--releases-json is required when requested baselines include all-since-*");
|
||||
}
|
||||
const publishedVersions = readPublishedVersions(args.get("npm-versions-json"));
|
||||
const releases = readStableReleases(releasesJson, publishedVersions);
|
||||
return dedupeSpecs(
|
||||
releases
|
||||
.map((release) => release.version)
|
||||
.filter((version) => compareStableVersions(version, minimumVersion) >= 0),
|
||||
);
|
||||
}
|
||||
|
||||
export function resolveBaselines(args) {
|
||||
const requested = args.get("requested") ?? "";
|
||||
const fallback = args.get("fallback") ?? "openclaw@latest";
|
||||
@@ -117,6 +154,12 @@ export function resolveBaselines(args) {
|
||||
for (const token of requestedTokens) {
|
||||
if (token === "release-history") {
|
||||
resolved.push(...resolveReleaseHistory(args));
|
||||
} else if (token.startsWith("all-since-")) {
|
||||
const minimumVersion = token.slice("all-since-".length);
|
||||
if (!parseStableVersion(minimumVersion)) {
|
||||
throw new Error(`invalid all-since baseline token: ${token}`);
|
||||
}
|
||||
resolved.push(...resolveAllSince(args, minimumVersion));
|
||||
} else {
|
||||
exactTokens.push(token);
|
||||
}
|
||||
|
||||
@@ -352,11 +352,39 @@ describe("scripts/lib/docker-e2e-plan", () => {
|
||||
"published-upgrade-survivor-2026.4.29",
|
||||
"published-upgrade-survivor-2026.4.29-feishu-channel",
|
||||
"published-upgrade-survivor-2026.4.29-bootstrap-persona",
|
||||
"published-upgrade-survivor-2026.4.29-plugin-deps-cleanup",
|
||||
"published-upgrade-survivor-2026.4.29-tilde-log-path",
|
||||
"published-upgrade-survivor-2026.4.29-versioned-runtime-deps",
|
||||
]);
|
||||
});
|
||||
|
||||
it("expands update migration across baselines and cleanup scenarios", () => {
|
||||
const plan = planFor({
|
||||
selectedLaneNames: ["update-migration"],
|
||||
upgradeSurvivorBaselines: "2026.4.29 2026.4.23",
|
||||
upgradeSurvivorScenarios: "plugin-deps-cleanup",
|
||||
});
|
||||
|
||||
expect(plan.lanes.map((lane) => lane.name)).toEqual([
|
||||
"update-migration-2026.4.29-plugin-deps-cleanup",
|
||||
"update-migration-2026.4.23-plugin-deps-cleanup",
|
||||
]);
|
||||
expect(plan.lanes).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
command: expect.stringContaining("pnpm test:docker:update-migration"),
|
||||
imageKind: "bare",
|
||||
stateScenario: "upgrade-survivor",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
command: expect.stringContaining(
|
||||
"OPENCLAW_UPGRADE_SURVIVOR_SCENARIO='plugin-deps-cleanup'",
|
||||
),
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("plans a live-only selected lane without package e2e images", () => {
|
||||
const plan = planFor({ selectedLaneNames: ["live-models"] });
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ const PACKAGE_JSON = "package.json";
|
||||
const RELEASE_CHECKS_WORKFLOW = ".github/workflows/openclaw-release-checks.yml";
|
||||
const FULL_RELEASE_VALIDATION_WORKFLOW = ".github/workflows/full-release-validation.yml";
|
||||
const QA_LIVE_TRANSPORTS_WORKFLOW = ".github/workflows/qa-live-transports-convex.yml";
|
||||
const UPDATE_MIGRATION_WORKFLOW = ".github/workflows/update-migration.yml";
|
||||
const UPGRADE_SURVIVOR_RUN_SCRIPT = "scripts/e2e/lib/upgrade-survivor/run.sh";
|
||||
|
||||
type WorkflowStep = {
|
||||
@@ -94,6 +95,7 @@ describe("package acceptance workflow", () => {
|
||||
expect(workflow).toContain("--history-count 6");
|
||||
expect(workflow).toContain("--include-version 2026.4.23");
|
||||
expect(workflow).toContain("--pre-date 2026-03-15T00:00:00Z");
|
||||
expect(workflow).toContain('"all-since-"');
|
||||
expect(workflow).toContain("npm-onboard-channel-agent gateway-network config-reload");
|
||||
expect(workflow).toContain("npm-onboard-channel-agent doctor-switch");
|
||||
expect(workflow).toContain("update-channel-switch upgrade-survivor");
|
||||
@@ -133,6 +135,22 @@ describe("package acceptance workflow", () => {
|
||||
expect(workflow).toContain("Published upgrade survivor baselines:");
|
||||
expect(workflow).toContain("Published upgrade survivor scenarios:");
|
||||
});
|
||||
|
||||
it("keeps exhaustive update migration as a separate manual package gate", () => {
|
||||
const workflow = readFileSync(UPDATE_MIGRATION_WORKFLOW, "utf8");
|
||||
const packageWorkflow = readFileSync(PACKAGE_ACCEPTANCE_WORKFLOW, "utf8");
|
||||
|
||||
expect(workflow).toContain("name: Update Migration");
|
||||
expect(workflow).toContain("uses: ./.github/workflows/package-acceptance.yml");
|
||||
expect(workflow).toContain("source: ref");
|
||||
expect(workflow).toContain("suite_profile: custom");
|
||||
expect(workflow).toContain("docker_lanes: update-migration");
|
||||
expect(workflow).toContain("default: all-since-2026.4.23");
|
||||
expect(workflow).toContain("default: plugin-deps-cleanup");
|
||||
expect(workflow).toContain("telegram_mode: none");
|
||||
expect(workflow).toContain("secrets: inherit");
|
||||
expect(packageWorkflow).toContain("published-upgrade-survivor/update-migration");
|
||||
});
|
||||
});
|
||||
|
||||
describe("package artifact reuse", () => {
|
||||
|
||||
@@ -75,6 +75,46 @@ describe("scripts/resolve-upgrade-survivor-baselines", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("resolves all-since baselines to every stable published release at or after the requested version", () => {
|
||||
const releases = (
|
||||
[
|
||||
["v2026.5.2", "2026-05-03T00:00:00Z"],
|
||||
["v2026.4.30", "2026-05-01T00:00:00Z"],
|
||||
["v2026.4.29", "2026-04-30T00:00:00Z"],
|
||||
["v2026.4.23", "2026-04-23T00:00:00Z"],
|
||||
["v2026.4.22", "2026-04-22T00:00:00Z"],
|
||||
["v2026.4.31-beta.1", "2026-05-02T00:00:00Z", true],
|
||||
] as const
|
||||
).map(([tagName, publishedAt, isPrerelease = false]) => ({
|
||||
isPrerelease,
|
||||
publishedAt,
|
||||
tagName,
|
||||
}));
|
||||
|
||||
withReleaseFixture(releases, (releasesFile) => {
|
||||
withJsonFixture(
|
||||
"versions.json",
|
||||
["2026.5.2", "2026.4.30", "2026.4.29", "2026.4.23", "2026.4.22"],
|
||||
(versionsFile) => {
|
||||
expect(
|
||||
resolveBaselines(
|
||||
new Map([
|
||||
["requested", "all-since-2026.4.23"],
|
||||
["releases-json", releasesFile],
|
||||
["npm-versions-json", versionsFile],
|
||||
]),
|
||||
),
|
||||
).toEqual([
|
||||
"openclaw@2026.5.2",
|
||||
"openclaw@2026.4.30",
|
||||
"openclaw@2026.4.29",
|
||||
"openclaw@2026.4.23",
|
||||
]);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("maps release-history anchors to npm-published package versions when GitHub tags have republish suffixes", () => {
|
||||
const releases = (
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user