
























@@ -466,56 +466,54 @@ jobs:
466466 - name: Hydrate live auth/profile inputs
467467run: bash scripts/ci-hydrate-live-auth.sh
468468469+ - name: Plan Docker E2E chunk
470+id: plan
471+shell: bash
472+run: |
473+ set -euo pipefail
474+ mkdir -p .artifacts/docker-tests
475+ export OPENCLAW_DOCKER_ALL_PROFILE=release-path
476+ export OPENCLAW_DOCKER_ALL_CHUNK="${DOCKER_E2E_CHUNK}"
477+ export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI="${INCLUDE_OPENWEBUI}"
478+ node scripts/test-docker-all.mjs --plan-json > ".artifacts/docker-tests/release-${DOCKER_E2E_CHUNK}-plan.json"
479+ node scripts/docker-e2e.mjs github-outputs ".artifacts/docker-tests/release-${DOCKER_E2E_CHUNK}-plan.json" >> "$GITHUB_OUTPUT"
480+469481 - name: Download OpenClaw Docker E2E package
482+if: steps.plan.outputs.needs_package == '1'
470483uses: actions/download-artifact@v8
471484with:
472485name: docker-e2e-package
473486path: .artifacts/docker-e2e-package
474487475- - name: Pull shared Docker E2E image
488+ - name: Pull shared bare Docker E2E image
489+if: steps.plan.outputs.needs_bare_image == '1'
476490shell: bash
477491run: |
478492 set -euo pipefail
479- case "${DOCKER_E2E_CHUNK}" in
480- core)
481- docker pull "${OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE}"
482- ;;
483- package-update)
484- docker pull "${OPENCLAW_DOCKER_E2E_BARE_IMAGE}"
485- ;;
486- plugins-integrations)
487- docker pull "${OPENCLAW_DOCKER_E2E_BARE_IMAGE}"
488- docker pull "${OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE}"
489- ;;
490- *)
491- docker pull "${OPENCLAW_DOCKER_E2E_IMAGE}"
492- ;;
493- esac
493+ docker pull "${OPENCLAW_DOCKER_E2E_BARE_IMAGE}"
494+495+ - name: Pull shared functional Docker E2E image
496+if: steps.plan.outputs.needs_functional_image == '1'
497+shell: bash
498+run: |
499+ set -euo pipefail
500+ docker pull "${OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE}"
494501495502 - name: Validate chunk credentials
496503shell: bash
497504run: |
498505 set -euo pipefail
499- case "${DOCKER_E2E_CHUNK}" in
500- package-update)
501- [[ -n "${OPENAI_API_KEY:-}" ]] || {
502- echo "OPENAI_API_KEY is required for installer Docker E2E." >&2
503- exit 1
504- }
505- if [[ -z "${ANTHROPIC_API_TOKEN:-}" && -z "${ANTHROPIC_API_KEY:-}" ]]; then
506- echo "ANTHROPIC_API_TOKEN or ANTHROPIC_API_KEY is required for installer Docker E2E." >&2
507- exit 1
508- fi
509- ;;
510- plugins-integrations)
511- if [[ "${INCLUDE_OPENWEBUI}" == "true" ]]; then
512- [[ -n "${OPENAI_API_KEY:-}" ]] || {
513- echo "OPENAI_API_KEY is required for the Open WebUI Docker smoke." >&2
514- exit 1
515- }
516- fi
517- ;;
518- esac
506+ credentials=",${{ steps.plan.outputs.credentials }},"
507+ if [[ "$credentials" == *",openai,"* ]]; then
508+ [[ -n "${OPENAI_API_KEY:-}" ]] || {
509+ echo "OPENAI_API_KEY is required for selected Docker E2E lanes." >&2
510+ exit 1
511+ }
512+ fi
513+ if [[ "$credentials" == *",anthropic,"* && -z "${ANTHROPIC_API_TOKEN:-}" && -z "${ANTHROPIC_API_KEY:-}" ]]; then
514+ echo "ANTHROPIC_API_TOKEN or ANTHROPIC_API_KEY is required for selected Docker E2E lanes." >&2
515+ exit 1
516+ fi
519517520518 - name: Run Docker E2E chunk
521519shell: bash
@@ -542,31 +540,7 @@ jobs:
542540 echo "Docker chunk summary missing: \`$summary\`" >> "$GITHUB_STEP_SUMMARY"
543541 exit 0
544542 fi
545- node --input-type=module - "$summary" <<'NODE' >> "$GITHUB_STEP_SUMMARY"
546- import fs from "node:fs";
547- const summary = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
548- const lanes = Array.isArray(summary.lanes) ? summary.lanes : [];
549- console.log(`### Docker E2E chunk: ${summary.chunk ?? "unknown"}`);
550- console.log("");
551- console.log(`Status: \`${summary.status}\``);
552- console.log("");
553- console.log("| Lane | Status | Seconds | Timed out | Rerun |");
554- console.log("| --- | ---: | ---: | --- | --- |");
555- for (const lane of lanes) {
556- const status = lane.status === 0 ? "pass" : `fail ${lane.status}`;
557- const rerun = String(lane.rerunCommand ?? "").replaceAll("`", "\\`");
558- console.log(`| \`${lane.name}\` | ${status} | ${lane.elapsedSeconds ?? ""} | ${lane.timedOut ? "yes" : "no"} | \`${rerun}\` |`);
559- }
560- const phases = Array.isArray(summary.phases) ? summary.phases : [];
561- if (phases.length > 0) {
562- console.log("");
563- console.log("| Phase | Seconds | Status | Image kind |");
564- console.log("| --- | ---: | --- | --- |");
565- for (const phase of phases) {
566- console.log(`| \`${phase.name}\` | ${phase.elapsedSeconds ?? ""} | ${phase.status ?? ""} | ${phase.imageKind ?? ""} |`);
567- }
568- }
569- NODE
543+ node scripts/docker-e2e.mjs summary "$summary" "Docker E2E chunk: ${DOCKER_E2E_CHUNK:-unknown}" >> "$GITHUB_STEP_SUMMARY"
570544571545 - name: Upload Docker E2E chunk artifacts
572546if: always()
@@ -658,71 +632,65 @@ jobs:
658632 - name: Hydrate live auth/profile inputs
659633run: bash scripts/ci-hydrate-live-auth.sh
660634661- - name: Detect targeted Docker lane image needs
662-id: lane_class
635+ - name: Plan targeted Docker E2E lanes
636+id: plan
663637shell: bash
664638run: |
665639 set -euo pipefail
666- needs_e2e=0
667- IFS=', ' read -r -a lanes <<< "${DOCKER_E2E_LANES}"
668- for lane in "${lanes[@]}"; do
669- [[ -z "$lane" ]] && continue
670- if [[ "$lane" != live-* ]]; then
671- needs_e2e=1
672- break
673- fi
674- done
675- echo "needs_e2e=${needs_e2e}" >> "$GITHUB_OUTPUT"
640+ mkdir -p .artifacts/docker-tests
641+ export OPENCLAW_DOCKER_ALL_LANES="${DOCKER_E2E_LANES}"
642+ export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI="${INCLUDE_OPENWEBUI}"
643+ node scripts/test-docker-all.mjs --plan-json > .artifacts/docker-tests/targeted-plan.json
644+ node scripts/docker-e2e.mjs github-outputs .artifacts/docker-tests/targeted-plan.json >> "$GITHUB_OUTPUT"
676645677646 - name: Download OpenClaw Docker E2E package
678-if: steps.lane_class.outputs.needs_e2e == '1'
647+if: steps.plan.outputs.needs_package == '1'
679648uses: actions/download-artifact@v8
680649with:
681650name: docker-e2e-package
682651path: .artifacts/docker-e2e-package
683652684- - name: Pull shared Docker E2E images
685-if: steps.lane_class.outputs.needs_e2e == '1'
653+ - name: Pull shared bare Docker E2E image
654+if: steps.plan.outputs.needs_bare_image == '1'
686655shell: bash
687656run: |
688657 set -euo pipefail
689658 docker pull "${OPENCLAW_DOCKER_E2E_BARE_IMAGE}"
659+660+ - name: Pull shared functional Docker E2E image
661+if: steps.plan.outputs.needs_functional_image == '1'
662+shell: bash
663+run: |
664+ set -euo pipefail
690665 docker pull "${OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE}"
691666692667 - name: Validate targeted lane credentials
693668shell: bash
694669run: |
695670 set -euo pipefail
696- lanes=" ${DOCKER_E2E_LANES//,/ } "
697- if [[ "$lanes" == *" install-e2e "* ]]; then
671+ credentials=",${{ steps.plan.outputs.credentials }},"
672+ if [[ "$credentials" == *",openai,"* ]]; then
698673 [[ -n "${OPENAI_API_KEY:-}" ]] || {
699- echo "OPENAI_API_KEY is required for installer Docker E2E." >&2
674+ echo "OPENAI_API_KEY is required for selected Docker E2E lanes." >&2
700675 exit 1
701676 }
702- if [[ -z "${ANTHROPIC_API_TOKEN:-}" && -z "${ANTHROPIC_API_KEY:-}" ]]; then
703- echo "ANTHROPIC_API_TOKEN or ANTHROPIC_API_KEY is required for installer Docker E2E." >&2
704- exit 1
705- fi
706677 fi
707- if [[ "$lanes" == *" openwebui "* || "$lanes" == *" openai-web-search-minimal "* ]]; then
708- [[ -n "${OPENAI_API_KEY:-}" ]] || {
709- echo "OPENAI_API_KEY is required for selected OpenAI Docker lanes." >&2
710- exit 1
711- }
678+ if [[ "$credentials" == *",anthropic,"* && -z "${ANTHROPIC_API_TOKEN:-}" && -z "${ANTHROPIC_API_KEY:-}" ]]; then
679+ echo "ANTHROPIC_API_TOKEN or ANTHROPIC_API_KEY is required for selected Docker E2E lanes." >&2
680+ exit 1
712681 fi
713682714683 - name: Run targeted Docker E2E lanes
715684shell: bash
716685run: |
717686 set -euo pipefail
718- lanes=" ${DOCKER_E2E_LANES//,/ } "
719687 export OPENCLAW_DOCKER_ALL_LANES="${DOCKER_E2E_LANES}"
720688 export OPENCLAW_DOCKER_ALL_PREFLIGHT=0
721689 export OPENCLAW_DOCKER_ALL_FAIL_FAST=0
722690 export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI="${INCLUDE_OPENWEBUI}"
723691 export OPENCLAW_DOCKER_ALL_LOG_DIR=".artifacts/docker-tests/targeted"
724692 export OPENCLAW_DOCKER_ALL_TIMINGS_FILE=".artifacts/docker-tests/targeted-timings.json"
725- if [[ "$lanes" == *" live-"* ]]; then
693+ if [[ "${{ steps.plan.outputs.needs_live_image }}" == "1" ]]; then
726694 pnpm test:docker:live-build
727695 fi
728696 export OPENCLAW_DOCKER_ALL_BUILD=0
@@ -739,31 +707,7 @@ jobs:
739707 echo "Docker targeted summary missing: \`$summary\`" >> "$GITHUB_STEP_SUMMARY"
740708 exit 0
741709 fi
742- node --input-type=module - "$summary" <<'NODE' >> "$GITHUB_STEP_SUMMARY"
743- import fs from "node:fs";
744- const summary = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
745- const lanes = Array.isArray(summary.lanes) ? summary.lanes : [];
746- console.log("### Docker E2E targeted lanes");
747- console.log("");
748- console.log(`Status: \`${summary.status}\``);
749- console.log("");
750- console.log("| Lane | Status | Seconds | Timed out | Rerun |");
751- console.log("| --- | ---: | ---: | --- | --- |");
752- for (const lane of lanes) {
753- const status = lane.status === 0 ? "pass" : `fail ${lane.status}`;
754- const rerun = String(lane.rerunCommand ?? "").replaceAll("`", "\\`");
755- console.log(`| \`${lane.name}\` | ${status} | ${lane.elapsedSeconds ?? ""} | ${lane.timedOut ? "yes" : "no"} | \`${rerun}\` |`);
756- }
757- const phases = Array.isArray(summary.phases) ? summary.phases : [];
758- if (phases.length > 0) {
759- console.log("");
760- console.log("| Phase | Seconds | Status | Image kind |");
761- console.log("| --- | ---: | --- | --- |");
762- for (const phase of phases) {
763- console.log(`| \`${phase.name}\` | ${phase.elapsedSeconds ?? ""} | ${phase.status ?? ""} | ${phase.imageKind ?? ""} |`);
764- }
765- }
766- NODE
710+ node scripts/docker-e2e.mjs summary "$summary" "Docker E2E targeted lanes" >> "$GITHUB_STEP_SUMMARY"
767711768712 - name: Upload targeted Docker E2E artifacts
769713if: always()
@@ -829,6 +773,11 @@ jobs:
829773image: ${{ steps.image.outputs.image }}
830774bare_image: ${{ steps.image.outputs.bare_image }}
831775functional_image: ${{ steps.image.outputs.functional_image }}
776+needs_bare_image: ${{ steps.plan.outputs.needs_bare_image }}
777+needs_e2e_image: ${{ steps.plan.outputs.needs_e2e_image }}
778+needs_functional_image: ${{ steps.plan.outputs.needs_functional_image }}
779+needs_live_image: ${{ steps.plan.outputs.needs_live_image }}
780+needs_package: ${{ steps.plan.outputs.needs_package }}
832781env:
833782DOCKER_BUILD_SUMMARY: "false"
834783DOCKER_BUILD_RECORD_UPLOAD: "false"
@@ -856,72 +805,68 @@ jobs:
856805 echo "Shared Docker E2E bare image: \`$bare_image\`" >> "$GITHUB_STEP_SUMMARY"
857806 echo "Shared Docker E2E functional image: \`$functional_image\`" >> "$GITHUB_STEP_SUMMARY"
858807859- - name: Classify selected Docker lanes
860-id: lane_class
808+ - name: Plan Docker E2E images
809+id: plan
861810shell: bash
862811env:
863812DOCKER_E2E_LANES: ${{ inputs.docker_lanes }}
864813INCLUDE_RELEASE_PATH_SUITES: ${{ inputs.include_release_path_suites }}
865814INCLUDE_OPENWEBUI: ${{ inputs.include_openwebui }}
866815run: |
867816 set -euo pipefail
868- needs_e2e=0
869- if [[ "${INCLUDE_RELEASE_PATH_SUITES}" == "true" || "${INCLUDE_OPENWEBUI}" == "true" ]]; then
870- needs_e2e=1
817+ mkdir -p .artifacts/docker-tests
818+ if [[ "${INCLUDE_RELEASE_PATH_SUITES}" == "true" ]]; then
819+ export OPENCLAW_DOCKER_ALL_PROFILE=release-path
820+ export OPENCLAW_DOCKER_ALL_PLAN_RELEASE_ALL=1
871821 elif [[ -n "${DOCKER_E2E_LANES}" ]]; then
872- IFS=', ' read -r -a lanes <<< "${DOCKER_E2E_LANES}"
873- for lane in "${lanes[@]}"; do
874- [[ -z "$lane" ]] && continue
875- if [[ "$lane" != live-* ]]; then
876- needs_e2e=1
877- break
878- fi
879- done
822+ export OPENCLAW_DOCKER_ALL_LANES="${DOCKER_E2E_LANES}"
823+ elif [[ "${INCLUDE_OPENWEBUI}" == "true" ]]; then
824+ export OPENCLAW_DOCKER_ALL_LANES=openwebui
880825 fi
881- echo "needs_e2e=${needs_e2e}" >> "$GITHUB_OUTPUT"
826+ export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI="${INCLUDE_OPENWEBUI}"
827+ node scripts/test-docker-all.mjs --plan-json > .artifacts/docker-tests/plan.json
828+ node scripts/docker-e2e.mjs github-outputs .artifacts/docker-tests/plan.json >> "$GITHUB_OUTPUT"
882829883830 - name: Setup Node environment
884-if: steps.lane_class.outputs.needs_e2e == '1'
831+if: steps.plan.outputs.needs_package == '1'
885832uses: ./.github/actions/setup-node-env
886833with:
887834node-version: ${{ env.NODE_VERSION }}
888835pnpm-version: ${{ env.PNPM_VERSION }}
889836install-bun: "true"
890837891838 - name: Pack OpenClaw package for Docker E2E
892-if: steps.lane_class.outputs.needs_e2e == '1'
839+if: steps.plan.outputs.needs_package == '1'
893840shell: bash
894841run: |
895842 set -euo pipefail
896843 mkdir -p .artifacts/docker-e2e-package
897- pnpm build
898- node --import tsx --input-type=module -e 'const { writePackageDistInventory } = await import("./src/infra/package-dist-inventory.ts"); await writePackageDistInventory(process.cwd());'
899- npm pack --silent --ignore-scripts --pack-destination .artifacts/docker-e2e-package >/tmp/openclaw-docker-e2e-pack.out
900- packed="$(tail -n 1 /tmp/openclaw-docker-e2e-pack.out | tr -d '\r')"
901- mv ".artifacts/docker-e2e-package/$packed" .artifacts/docker-e2e-package/openclaw-current.tgz
844+ node scripts/package-openclaw-for-docker.mjs \
845+ --output-dir .artifacts/docker-e2e-package \
846+ --output-name openclaw-current.tgz
902847903848 - name: Upload OpenClaw Docker E2E package
904-if: steps.lane_class.outputs.needs_e2e == '1'
849+if: steps.plan.outputs.needs_package == '1'
905850uses: actions/upload-artifact@v7
906851with:
907852name: docker-e2e-package
908853path: .artifacts/docker-e2e-package/openclaw-current.tgz
909854if-no-files-found: error
910855911856 - name: Log in to GHCR
912-if: steps.lane_class.outputs.needs_e2e == '1'
857+if: steps.plan.outputs.needs_e2e_image == '1'
913858uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
914859with:
915860registry: ghcr.io
916861username: ${{ github.actor }}
917862password: ${{ github.token }}
918863919864 - name: Setup Docker builder
920-if: steps.lane_class.outputs.needs_e2e == '1'
865+if: steps.plan.outputs.needs_e2e_image == '1'
921866uses: useblacksmith/setup-docker-builder@ac083cc84672d01c60d5e8561d0a939b697de542 # v1
922867923868 - name: Build and push bare Docker E2E image
924-if: steps.lane_class.outputs.needs_e2e == '1' && (inputs.include_release_path_suites || inputs.docker_lanes != '')
869+if: steps.plan.outputs.needs_bare_image == '1'
925870uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
926871with:
927872context: .
@@ -936,7 +881,7 @@ jobs:
936881push: true
937882938883 - name: Build and push functional Docker E2E image
939-if: steps.lane_class.outputs.needs_e2e == '1'
884+if: steps.plan.outputs.needs_functional_image == '1'
940885uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
941886with:
942887context: .
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。