mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:30:43 +00:00
ci: derive docker e2e artifacts from plan
This commit is contained in:
@@ -466,56 +466,54 @@ jobs:
|
||||
- name: Hydrate live auth/profile inputs
|
||||
run: bash scripts/ci-hydrate-live-auth.sh
|
||||
|
||||
- name: Plan Docker E2E chunk
|
||||
id: plan
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p .artifacts/docker-tests
|
||||
export OPENCLAW_DOCKER_ALL_PROFILE=release-path
|
||||
export OPENCLAW_DOCKER_ALL_CHUNK="${DOCKER_E2E_CHUNK}"
|
||||
export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI="${INCLUDE_OPENWEBUI}"
|
||||
node scripts/test-docker-all.mjs --plan-json > ".artifacts/docker-tests/release-${DOCKER_E2E_CHUNK}-plan.json"
|
||||
node scripts/docker-e2e.mjs github-outputs ".artifacts/docker-tests/release-${DOCKER_E2E_CHUNK}-plan.json" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Download OpenClaw Docker E2E package
|
||||
if: steps.plan.outputs.needs_package == '1'
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: docker-e2e-package
|
||||
path: .artifacts/docker-e2e-package
|
||||
|
||||
- name: Pull shared Docker E2E image
|
||||
- name: Pull shared bare Docker E2E image
|
||||
if: steps.plan.outputs.needs_bare_image == '1'
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
case "${DOCKER_E2E_CHUNK}" in
|
||||
core)
|
||||
docker pull "${OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE}"
|
||||
;;
|
||||
package-update)
|
||||
docker pull "${OPENCLAW_DOCKER_E2E_BARE_IMAGE}"
|
||||
;;
|
||||
plugins-integrations)
|
||||
docker pull "${OPENCLAW_DOCKER_E2E_BARE_IMAGE}"
|
||||
docker pull "${OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE}"
|
||||
;;
|
||||
*)
|
||||
docker pull "${OPENCLAW_DOCKER_E2E_IMAGE}"
|
||||
;;
|
||||
esac
|
||||
docker pull "${OPENCLAW_DOCKER_E2E_BARE_IMAGE}"
|
||||
|
||||
- name: Pull shared functional Docker E2E image
|
||||
if: steps.plan.outputs.needs_functional_image == '1'
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
docker pull "${OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE}"
|
||||
|
||||
- name: Validate chunk credentials
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
case "${DOCKER_E2E_CHUNK}" in
|
||||
package-update)
|
||||
[[ -n "${OPENAI_API_KEY:-}" ]] || {
|
||||
echo "OPENAI_API_KEY is required for installer Docker E2E." >&2
|
||||
exit 1
|
||||
}
|
||||
if [[ -z "${ANTHROPIC_API_TOKEN:-}" && -z "${ANTHROPIC_API_KEY:-}" ]]; then
|
||||
echo "ANTHROPIC_API_TOKEN or ANTHROPIC_API_KEY is required for installer Docker E2E." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
plugins-integrations)
|
||||
if [[ "${INCLUDE_OPENWEBUI}" == "true" ]]; then
|
||||
[[ -n "${OPENAI_API_KEY:-}" ]] || {
|
||||
echo "OPENAI_API_KEY is required for the Open WebUI Docker smoke." >&2
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
credentials=",${{ steps.plan.outputs.credentials }},"
|
||||
if [[ "$credentials" == *",openai,"* ]]; then
|
||||
[[ -n "${OPENAI_API_KEY:-}" ]] || {
|
||||
echo "OPENAI_API_KEY is required for selected Docker E2E lanes." >&2
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
if [[ "$credentials" == *",anthropic,"* && -z "${ANTHROPIC_API_TOKEN:-}" && -z "${ANTHROPIC_API_KEY:-}" ]]; then
|
||||
echo "ANTHROPIC_API_TOKEN or ANTHROPIC_API_KEY is required for selected Docker E2E lanes." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Run Docker E2E chunk
|
||||
shell: bash
|
||||
@@ -542,31 +540,7 @@ jobs:
|
||||
echo "Docker chunk summary missing: \`$summary\`" >> "$GITHUB_STEP_SUMMARY"
|
||||
exit 0
|
||||
fi
|
||||
node --input-type=module - "$summary" <<'NODE' >> "$GITHUB_STEP_SUMMARY"
|
||||
import fs from "node:fs";
|
||||
const summary = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
|
||||
const lanes = Array.isArray(summary.lanes) ? summary.lanes : [];
|
||||
console.log(`### Docker E2E chunk: ${summary.chunk ?? "unknown"}`);
|
||||
console.log("");
|
||||
console.log(`Status: \`${summary.status}\``);
|
||||
console.log("");
|
||||
console.log("| Lane | Status | Seconds | Timed out | Rerun |");
|
||||
console.log("| --- | ---: | ---: | --- | --- |");
|
||||
for (const lane of lanes) {
|
||||
const status = lane.status === 0 ? "pass" : `fail ${lane.status}`;
|
||||
const rerun = String(lane.rerunCommand ?? "").replaceAll("`", "\\`");
|
||||
console.log(`| \`${lane.name}\` | ${status} | ${lane.elapsedSeconds ?? ""} | ${lane.timedOut ? "yes" : "no"} | \`${rerun}\` |`);
|
||||
}
|
||||
const phases = Array.isArray(summary.phases) ? summary.phases : [];
|
||||
if (phases.length > 0) {
|
||||
console.log("");
|
||||
console.log("| Phase | Seconds | Status | Image kind |");
|
||||
console.log("| --- | ---: | --- | --- |");
|
||||
for (const phase of phases) {
|
||||
console.log(`| \`${phase.name}\` | ${phase.elapsedSeconds ?? ""} | ${phase.status ?? ""} | ${phase.imageKind ?? ""} |`);
|
||||
}
|
||||
}
|
||||
NODE
|
||||
node scripts/docker-e2e.mjs summary "$summary" "Docker E2E chunk: ${DOCKER_E2E_CHUNK:-unknown}" >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Upload Docker E2E chunk artifacts
|
||||
if: always()
|
||||
@@ -658,71 +632,65 @@ jobs:
|
||||
- name: Hydrate live auth/profile inputs
|
||||
run: bash scripts/ci-hydrate-live-auth.sh
|
||||
|
||||
- name: Detect targeted Docker lane image needs
|
||||
id: lane_class
|
||||
- name: Plan targeted Docker E2E lanes
|
||||
id: plan
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
needs_e2e=0
|
||||
IFS=', ' read -r -a lanes <<< "${DOCKER_E2E_LANES}"
|
||||
for lane in "${lanes[@]}"; do
|
||||
[[ -z "$lane" ]] && continue
|
||||
if [[ "$lane" != live-* ]]; then
|
||||
needs_e2e=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
echo "needs_e2e=${needs_e2e}" >> "$GITHUB_OUTPUT"
|
||||
mkdir -p .artifacts/docker-tests
|
||||
export OPENCLAW_DOCKER_ALL_LANES="${DOCKER_E2E_LANES}"
|
||||
export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI="${INCLUDE_OPENWEBUI}"
|
||||
node scripts/test-docker-all.mjs --plan-json > .artifacts/docker-tests/targeted-plan.json
|
||||
node scripts/docker-e2e.mjs github-outputs .artifacts/docker-tests/targeted-plan.json >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Download OpenClaw Docker E2E package
|
||||
if: steps.lane_class.outputs.needs_e2e == '1'
|
||||
if: steps.plan.outputs.needs_package == '1'
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: docker-e2e-package
|
||||
path: .artifacts/docker-e2e-package
|
||||
|
||||
- name: Pull shared Docker E2E images
|
||||
if: steps.lane_class.outputs.needs_e2e == '1'
|
||||
- name: Pull shared bare Docker E2E image
|
||||
if: steps.plan.outputs.needs_bare_image == '1'
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
docker pull "${OPENCLAW_DOCKER_E2E_BARE_IMAGE}"
|
||||
|
||||
- name: Pull shared functional Docker E2E image
|
||||
if: steps.plan.outputs.needs_functional_image == '1'
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
docker pull "${OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE}"
|
||||
|
||||
- name: Validate targeted lane credentials
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
lanes=" ${DOCKER_E2E_LANES//,/ } "
|
||||
if [[ "$lanes" == *" install-e2e "* ]]; then
|
||||
credentials=",${{ steps.plan.outputs.credentials }},"
|
||||
if [[ "$credentials" == *",openai,"* ]]; then
|
||||
[[ -n "${OPENAI_API_KEY:-}" ]] || {
|
||||
echo "OPENAI_API_KEY is required for installer Docker E2E." >&2
|
||||
echo "OPENAI_API_KEY is required for selected Docker E2E lanes." >&2
|
||||
exit 1
|
||||
}
|
||||
if [[ -z "${ANTHROPIC_API_TOKEN:-}" && -z "${ANTHROPIC_API_KEY:-}" ]]; then
|
||||
echo "ANTHROPIC_API_TOKEN or ANTHROPIC_API_KEY is required for installer Docker E2E." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
if [[ "$lanes" == *" openwebui "* || "$lanes" == *" openai-web-search-minimal "* ]]; then
|
||||
[[ -n "${OPENAI_API_KEY:-}" ]] || {
|
||||
echo "OPENAI_API_KEY is required for selected OpenAI Docker lanes." >&2
|
||||
exit 1
|
||||
}
|
||||
if [[ "$credentials" == *",anthropic,"* && -z "${ANTHROPIC_API_TOKEN:-}" && -z "${ANTHROPIC_API_KEY:-}" ]]; then
|
||||
echo "ANTHROPIC_API_TOKEN or ANTHROPIC_API_KEY is required for selected Docker E2E lanes." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Run targeted Docker E2E lanes
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
lanes=" ${DOCKER_E2E_LANES//,/ } "
|
||||
export OPENCLAW_DOCKER_ALL_LANES="${DOCKER_E2E_LANES}"
|
||||
export OPENCLAW_DOCKER_ALL_PREFLIGHT=0
|
||||
export OPENCLAW_DOCKER_ALL_FAIL_FAST=0
|
||||
export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI="${INCLUDE_OPENWEBUI}"
|
||||
export OPENCLAW_DOCKER_ALL_LOG_DIR=".artifacts/docker-tests/targeted"
|
||||
export OPENCLAW_DOCKER_ALL_TIMINGS_FILE=".artifacts/docker-tests/targeted-timings.json"
|
||||
if [[ "$lanes" == *" live-"* ]]; then
|
||||
if [[ "${{ steps.plan.outputs.needs_live_image }}" == "1" ]]; then
|
||||
pnpm test:docker:live-build
|
||||
fi
|
||||
export OPENCLAW_DOCKER_ALL_BUILD=0
|
||||
@@ -739,31 +707,7 @@ jobs:
|
||||
echo "Docker targeted summary missing: \`$summary\`" >> "$GITHUB_STEP_SUMMARY"
|
||||
exit 0
|
||||
fi
|
||||
node --input-type=module - "$summary" <<'NODE' >> "$GITHUB_STEP_SUMMARY"
|
||||
import fs from "node:fs";
|
||||
const summary = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
|
||||
const lanes = Array.isArray(summary.lanes) ? summary.lanes : [];
|
||||
console.log("### Docker E2E targeted lanes");
|
||||
console.log("");
|
||||
console.log(`Status: \`${summary.status}\``);
|
||||
console.log("");
|
||||
console.log("| Lane | Status | Seconds | Timed out | Rerun |");
|
||||
console.log("| --- | ---: | ---: | --- | --- |");
|
||||
for (const lane of lanes) {
|
||||
const status = lane.status === 0 ? "pass" : `fail ${lane.status}`;
|
||||
const rerun = String(lane.rerunCommand ?? "").replaceAll("`", "\\`");
|
||||
console.log(`| \`${lane.name}\` | ${status} | ${lane.elapsedSeconds ?? ""} | ${lane.timedOut ? "yes" : "no"} | \`${rerun}\` |`);
|
||||
}
|
||||
const phases = Array.isArray(summary.phases) ? summary.phases : [];
|
||||
if (phases.length > 0) {
|
||||
console.log("");
|
||||
console.log("| Phase | Seconds | Status | Image kind |");
|
||||
console.log("| --- | ---: | --- | --- |");
|
||||
for (const phase of phases) {
|
||||
console.log(`| \`${phase.name}\` | ${phase.elapsedSeconds ?? ""} | ${phase.status ?? ""} | ${phase.imageKind ?? ""} |`);
|
||||
}
|
||||
}
|
||||
NODE
|
||||
node scripts/docker-e2e.mjs summary "$summary" "Docker E2E targeted lanes" >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Upload targeted Docker E2E artifacts
|
||||
if: always()
|
||||
@@ -829,6 +773,11 @@ jobs:
|
||||
image: ${{ steps.image.outputs.image }}
|
||||
bare_image: ${{ steps.image.outputs.bare_image }}
|
||||
functional_image: ${{ steps.image.outputs.functional_image }}
|
||||
needs_bare_image: ${{ steps.plan.outputs.needs_bare_image }}
|
||||
needs_e2e_image: ${{ steps.plan.outputs.needs_e2e_image }}
|
||||
needs_functional_image: ${{ steps.plan.outputs.needs_functional_image }}
|
||||
needs_live_image: ${{ steps.plan.outputs.needs_live_image }}
|
||||
needs_package: ${{ steps.plan.outputs.needs_package }}
|
||||
env:
|
||||
DOCKER_BUILD_SUMMARY: "false"
|
||||
DOCKER_BUILD_RECORD_UPLOAD: "false"
|
||||
@@ -856,8 +805,8 @@ jobs:
|
||||
echo "Shared Docker E2E bare image: \`$bare_image\`" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "Shared Docker E2E functional image: \`$functional_image\`" >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Classify selected Docker lanes
|
||||
id: lane_class
|
||||
- name: Plan Docker E2E images
|
||||
id: plan
|
||||
shell: bash
|
||||
env:
|
||||
DOCKER_E2E_LANES: ${{ inputs.docker_lanes }}
|
||||
@@ -865,23 +814,21 @@ jobs:
|
||||
INCLUDE_OPENWEBUI: ${{ inputs.include_openwebui }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
needs_e2e=0
|
||||
if [[ "${INCLUDE_RELEASE_PATH_SUITES}" == "true" || "${INCLUDE_OPENWEBUI}" == "true" ]]; then
|
||||
needs_e2e=1
|
||||
mkdir -p .artifacts/docker-tests
|
||||
if [[ "${INCLUDE_RELEASE_PATH_SUITES}" == "true" ]]; then
|
||||
export OPENCLAW_DOCKER_ALL_PROFILE=release-path
|
||||
export OPENCLAW_DOCKER_ALL_PLAN_RELEASE_ALL=1
|
||||
elif [[ -n "${DOCKER_E2E_LANES}" ]]; then
|
||||
IFS=', ' read -r -a lanes <<< "${DOCKER_E2E_LANES}"
|
||||
for lane in "${lanes[@]}"; do
|
||||
[[ -z "$lane" ]] && continue
|
||||
if [[ "$lane" != live-* ]]; then
|
||||
needs_e2e=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
export OPENCLAW_DOCKER_ALL_LANES="${DOCKER_E2E_LANES}"
|
||||
elif [[ "${INCLUDE_OPENWEBUI}" == "true" ]]; then
|
||||
export OPENCLAW_DOCKER_ALL_LANES=openwebui
|
||||
fi
|
||||
echo "needs_e2e=${needs_e2e}" >> "$GITHUB_OUTPUT"
|
||||
export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI="${INCLUDE_OPENWEBUI}"
|
||||
node scripts/test-docker-all.mjs --plan-json > .artifacts/docker-tests/plan.json
|
||||
node scripts/docker-e2e.mjs github-outputs .artifacts/docker-tests/plan.json >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Setup Node environment
|
||||
if: steps.lane_class.outputs.needs_e2e == '1'
|
||||
if: steps.plan.outputs.needs_package == '1'
|
||||
uses: ./.github/actions/setup-node-env
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
@@ -889,19 +836,17 @@ jobs:
|
||||
install-bun: "true"
|
||||
|
||||
- name: Pack OpenClaw package for Docker E2E
|
||||
if: steps.lane_class.outputs.needs_e2e == '1'
|
||||
if: steps.plan.outputs.needs_package == '1'
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p .artifacts/docker-e2e-package
|
||||
pnpm build
|
||||
node --import tsx --input-type=module -e 'const { writePackageDistInventory } = await import("./src/infra/package-dist-inventory.ts"); await writePackageDistInventory(process.cwd());'
|
||||
npm pack --silent --ignore-scripts --pack-destination .artifacts/docker-e2e-package >/tmp/openclaw-docker-e2e-pack.out
|
||||
packed="$(tail -n 1 /tmp/openclaw-docker-e2e-pack.out | tr -d '\r')"
|
||||
mv ".artifacts/docker-e2e-package/$packed" .artifacts/docker-e2e-package/openclaw-current.tgz
|
||||
node scripts/package-openclaw-for-docker.mjs \
|
||||
--output-dir .artifacts/docker-e2e-package \
|
||||
--output-name openclaw-current.tgz
|
||||
|
||||
- name: Upload OpenClaw Docker E2E package
|
||||
if: steps.lane_class.outputs.needs_e2e == '1'
|
||||
if: steps.plan.outputs.needs_package == '1'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: docker-e2e-package
|
||||
@@ -909,7 +854,7 @@ jobs:
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Log in to GHCR
|
||||
if: steps.lane_class.outputs.needs_e2e == '1'
|
||||
if: steps.plan.outputs.needs_e2e_image == '1'
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
@@ -917,11 +862,11 @@ jobs:
|
||||
password: ${{ github.token }}
|
||||
|
||||
- name: Setup Docker builder
|
||||
if: steps.lane_class.outputs.needs_e2e == '1'
|
||||
if: steps.plan.outputs.needs_e2e_image == '1'
|
||||
uses: useblacksmith/setup-docker-builder@ac083cc84672d01c60d5e8561d0a939b697de542 # v1
|
||||
|
||||
- name: Build and push bare Docker E2E image
|
||||
if: steps.lane_class.outputs.needs_e2e == '1' && (inputs.include_release_path_suites || inputs.docker_lanes != '')
|
||||
if: steps.plan.outputs.needs_bare_image == '1'
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
||||
with:
|
||||
context: .
|
||||
@@ -936,7 +881,7 @@ jobs:
|
||||
push: true
|
||||
|
||||
- name: Build and push functional Docker E2E image
|
||||
if: steps.lane_class.outputs.needs_e2e == '1'
|
||||
if: steps.plan.outputs.needs_functional_image == '1'
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
||||
with:
|
||||
context: .
|
||||
|
||||
Reference in New Issue
Block a user