mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
fix(ci): require factory auth for droid live docker
This commit is contained in:
6
.github/actions/docker-e2e-plan/action.yml
vendored
6
.github/actions/docker-e2e-plan/action.yml
vendored
@@ -150,3 +150,9 @@ runs:
|
||||
echo "ANTHROPIC_API_TOKEN or ANTHROPIC_API_KEY is required for selected Docker E2E lanes." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$credentials" == *",factory,"* ]]; then
|
||||
[[ -n "${FACTORY_API_KEY:-}" ]] || {
|
||||
echo "FACTORY_API_KEY is required for selected Docker E2E lanes." >&2
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
|
||||
1
.github/workflows/ci-check-testbox.yml
vendored
1
.github/workflows/ci-check-testbox.yml
vendored
@@ -103,6 +103,7 @@ jobs:
|
||||
ANTHROPIC_API_TOKEN: ${{ secrets.ANTHROPIC_API_TOKEN }}
|
||||
CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }}
|
||||
DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_API_KEY }}
|
||||
FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
|
||||
FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }}
|
||||
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
||||
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
|
||||
|
||||
1
.github/workflows/crabbox-hydrate.yml
vendored
1
.github/workflows/crabbox-hydrate.yml
vendored
@@ -134,6 +134,7 @@ jobs:
|
||||
ANTHROPIC_API_TOKEN: ${{ secrets.ANTHROPIC_API_TOKEN }}
|
||||
CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }}
|
||||
DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_API_KEY }}
|
||||
FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
|
||||
FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }}
|
||||
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
||||
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
|
||||
|
||||
@@ -219,6 +219,8 @@ on:
|
||||
required: false
|
||||
ANTHROPIC_API_TOKEN:
|
||||
required: false
|
||||
FACTORY_API_KEY:
|
||||
required: false
|
||||
BYTEPLUS_API_KEY:
|
||||
required: false
|
||||
CEREBRAS_API_KEY:
|
||||
@@ -696,6 +698,7 @@ jobs:
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
ANTHROPIC_API_TOKEN: ${{ secrets.ANTHROPIC_API_TOKEN }}
|
||||
ANTHROPIC_API_KEY_OLD: ${{ secrets.ANTHROPIC_API_KEY_OLD }}
|
||||
FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
|
||||
BYTEPLUS_API_KEY: ${{ secrets.BYTEPLUS_API_KEY }}
|
||||
CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }}
|
||||
DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_API_KEY }}
|
||||
@@ -851,6 +854,12 @@ jobs:
|
||||
echo "ANTHROPIC_API_TOKEN or ANTHROPIC_API_KEY is required for selected Docker E2E lanes." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$credentials" == *",factory,"* ]]; then
|
||||
[[ -n "${FACTORY_API_KEY:-}" ]] || {
|
||||
echo "FACTORY_API_KEY is required for selected Docker E2E lanes." >&2
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
|
||||
- name: Run Docker E2E chunk
|
||||
if: contains(matrix.profiles, inputs.release_test_profile)
|
||||
@@ -937,6 +946,7 @@ jobs:
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
ANTHROPIC_API_TOKEN: ${{ secrets.ANTHROPIC_API_TOKEN }}
|
||||
ANTHROPIC_API_KEY_OLD: ${{ secrets.ANTHROPIC_API_KEY_OLD }}
|
||||
FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
|
||||
BYTEPLUS_API_KEY: ${{ secrets.BYTEPLUS_API_KEY }}
|
||||
CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }}
|
||||
DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_API_KEY }}
|
||||
@@ -1088,6 +1098,12 @@ jobs:
|
||||
echo "ANTHROPIC_API_TOKEN or ANTHROPIC_API_KEY is required for selected Docker E2E lanes." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$credentials" == *",factory,"* ]]; then
|
||||
[[ -n "${FACTORY_API_KEY:-}" ]] || {
|
||||
echo "FACTORY_API_KEY is required for selected Docker E2E lanes." >&2
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
|
||||
- name: Run targeted Docker E2E lanes
|
||||
shell: bash
|
||||
|
||||
@@ -596,6 +596,7 @@ jobs:
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
ANTHROPIC_API_KEY_OLD: ${{ secrets.ANTHROPIC_API_KEY_OLD }}
|
||||
ANTHROPIC_API_TOKEN: ${{ secrets.ANTHROPIC_API_TOKEN }}
|
||||
FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
|
||||
BYTEPLUS_API_KEY: ${{ secrets.BYTEPLUS_API_KEY }}
|
||||
CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }}
|
||||
DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_API_KEY }}
|
||||
@@ -688,6 +689,7 @@ jobs:
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
ANTHROPIC_API_KEY_OLD: ${{ secrets.ANTHROPIC_API_KEY_OLD }}
|
||||
ANTHROPIC_API_TOKEN: ${{ secrets.ANTHROPIC_API_TOKEN }}
|
||||
FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
|
||||
BYTEPLUS_API_KEY: ${{ secrets.BYTEPLUS_API_KEY }}
|
||||
CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }}
|
||||
DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_API_KEY }}
|
||||
|
||||
@@ -38,6 +38,7 @@ jobs:
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
ANTHROPIC_API_KEY_OLD: ${{ secrets.ANTHROPIC_API_KEY_OLD }}
|
||||
ANTHROPIC_API_TOKEN: ${{ secrets.ANTHROPIC_API_TOKEN }}
|
||||
FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
|
||||
BYTEPLUS_API_KEY: ${{ secrets.BYTEPLUS_API_KEY }}
|
||||
CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }}
|
||||
DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_API_KEY }}
|
||||
|
||||
3
.github/workflows/package-acceptance.yml
vendored
3
.github/workflows/package-acceptance.yml
vendored
@@ -190,6 +190,8 @@ on:
|
||||
required: false
|
||||
ANTHROPIC_API_TOKEN:
|
||||
required: false
|
||||
FACTORY_API_KEY:
|
||||
required: false
|
||||
BYTEPLUS_API_KEY:
|
||||
required: false
|
||||
CEREBRAS_API_KEY:
|
||||
@@ -561,6 +563,7 @@ jobs:
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
ANTHROPIC_API_KEY_OLD: ${{ secrets.ANTHROPIC_API_KEY_OLD }}
|
||||
ANTHROPIC_API_TOKEN: ${{ secrets.ANTHROPIC_API_TOKEN }}
|
||||
FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
|
||||
BYTEPLUS_API_KEY: ${{ secrets.BYTEPLUS_API_KEY }}
|
||||
CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }}
|
||||
DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_API_KEY }}
|
||||
|
||||
@@ -51,6 +51,7 @@ for env_key in \
|
||||
OPENCLAW_LIVE_SETUP_TOKEN_MODEL \
|
||||
OPENCLAW_LIVE_SETUP_TOKEN_PROFILE \
|
||||
OPENCLAW_LIVE_SETUP_TOKEN_VALUE \
|
||||
FACTORY_API_KEY \
|
||||
GEMINI_API_KEY \
|
||||
GOOGLE_API_KEY \
|
||||
OPENROUTER_API_KEY \
|
||||
|
||||
@@ -332,6 +332,9 @@ function laneCredentialRequirements(poolLane) {
|
||||
if (poolLane.name === "install-e2e-anthropic") {
|
||||
credentials.push("anthropic");
|
||||
}
|
||||
if (poolLane.name === "live-acp-bind-droid") {
|
||||
credentials.push("factory");
|
||||
}
|
||||
if (
|
||||
poolLane.name === "openwebui" ||
|
||||
poolLane.name === "openai-chat-tools" ||
|
||||
|
||||
@@ -173,8 +173,8 @@ WRAP
|
||||
fi
|
||||
droid --version
|
||||
if [ -z "${FACTORY_API_KEY:-}" ]; then
|
||||
echo "SKIP: Droid Docker ACP bind requires FACTORY_API_KEY; Factory OAuth/keyring auth in ~/.factory is not portable into the container." >&2
|
||||
exit 0
|
||||
echo "ERROR: Droid Docker ACP bind requires FACTORY_API_KEY; Factory OAuth/keyring auth in ~/.factory is not portable into the container." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
gemini)
|
||||
@@ -306,8 +306,8 @@ for ACP_AGENT in "${ACP_AGENTS[@]}"; do
|
||||
echo "==> Profile file: $PROFILE_STATUS"
|
||||
echo "==> Auth dirs: ${AUTH_DIRS_CSV:-none}"
|
||||
echo "==> Auth files: ${AUTH_FILES_CSV:-none}"
|
||||
echo "SKIP: Droid Docker ACP bind requires FACTORY_API_KEY; Factory OAuth/keyring auth in ~/.factory is not portable into the container." >&2
|
||||
continue
|
||||
echo "ERROR: Droid Docker ACP bind requires FACTORY_API_KEY; Factory OAuth/keyring auth in ~/.factory is not portable into the container." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
EXTERNAL_AUTH_MOUNTS=()
|
||||
|
||||
@@ -748,6 +748,23 @@ describe("scripts/lib/docker-e2e-plan", () => {
|
||||
expect(plan.needs.package).toBe(true);
|
||||
});
|
||||
|
||||
it("plans the Droid ACP bind live lane as Factory-auth proof", () => {
|
||||
const plan = planFor({ selectedLaneNames: ["live-acp-bind-droid"] });
|
||||
|
||||
expect(plan.credentials).toEqual(["factory"]);
|
||||
expect(plan.lanes).toHaveLength(1);
|
||||
const lane = requireFirstLane(plan);
|
||||
expect(lane.command).toBe(
|
||||
'OPENCLAW_LIVE_ACP_BIND_AGENT=droid OPENCLAW_LIVE_ACP_BIND_REQUIRE_TRANSCRIPT=1 OPENCLAW_SKIP_DOCKER_BUILD=1 bash -c \'harness="${OPENCLAW_DOCKER_E2E_TRUSTED_HARNESS_DIR:-}"; if [ -z "$harness" ]; then if [ -d .release-harness/scripts ]; then harness=.release-harness; else harness=.; fi; fi; OPENCLAW_LIVE_DOCKER_REPO_ROOT="${OPENCLAW_DOCKER_E2E_REPO_ROOT:-$PWD}" bash "$harness/scripts/test-live-acp-bind-docker.sh"\'',
|
||||
);
|
||||
expect(lane.imageKind).toBeUndefined();
|
||||
expect(lane.live).toBe(true);
|
||||
expect(lane.name).toBe("live-acp-bind-droid");
|
||||
expect(lane.resources).toEqual(["docker", "live", "live:droid", "npm"]);
|
||||
expect(lane.timeoutMs).toBe(1_200_000);
|
||||
expect(plan.needs.liveImage).toBe(true);
|
||||
});
|
||||
|
||||
it("plans Open WebUI as a live-auth functional image lane", () => {
|
||||
const plan = planFor({
|
||||
includeOpenWebUI: true,
|
||||
|
||||
@@ -7,11 +7,16 @@ const LIVE_E2E_WORKFLOW = ".github/workflows/openclaw-live-and-e2e-checks-reusab
|
||||
const NPM_TELEGRAM_WORKFLOW = ".github/workflows/npm-telegram-beta-e2e.yml";
|
||||
const PACKAGE_JSON = "package.json";
|
||||
const SETUP_PNPM_STORE_CACHE_ACTION = ".github/actions/setup-pnpm-store-cache/action.yml";
|
||||
const DOCKER_E2E_PLAN_ACTION = ".github/actions/docker-e2e-plan/action.yml";
|
||||
const RELEASE_CHECKS_WORKFLOW = ".github/workflows/openclaw-release-checks.yml";
|
||||
const RELEASE_PUBLISH_WORKFLOW = ".github/workflows/openclaw-release-publish.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 CI_CHECK_TESTBOX_WORKFLOW = ".github/workflows/ci-check-testbox.yml";
|
||||
const CRABBOX_HYDRATE_WORKFLOW = ".github/workflows/crabbox-hydrate.yml";
|
||||
const SCHEDULED_LIVE_CHECKS_WORKFLOW = ".github/workflows/openclaw-scheduled-live-checks.yml";
|
||||
const CI_HYDRATE_LIVE_AUTH_SCRIPT = "scripts/ci-hydrate-live-auth.sh";
|
||||
const UPGRADE_SURVIVOR_RUN_SCRIPT = "scripts/e2e/lib/upgrade-survivor/run.sh";
|
||||
|
||||
type WorkflowStep = {
|
||||
@@ -614,6 +619,50 @@ describe("package artifact reuse", () => {
|
||||
expect(stage).toContain('node --import tsx "$scripts_dir/live-docker-normalize-config.ts"');
|
||||
});
|
||||
|
||||
it("fails Droid ACP Docker live proof when Factory auth is missing", () => {
|
||||
const script = readFileSync("scripts/test-live-acp-bind-docker.sh", "utf8");
|
||||
|
||||
expect(script).toContain(
|
||||
"ERROR: Droid Docker ACP bind requires FACTORY_API_KEY; Factory OAuth/keyring auth in ~/.factory is not portable into the container.",
|
||||
);
|
||||
expect(script).not.toContain(
|
||||
"SKIP: Droid Docker ACP bind requires FACTORY_API_KEY; Factory OAuth/keyring auth in ~/.factory is not portable into the container.",
|
||||
);
|
||||
expect(script).not.toMatch(
|
||||
/Droid Docker ACP bind requires FACTORY_API_KEY[\s\S]{0,160}(exit 0|continue)/u,
|
||||
);
|
||||
});
|
||||
|
||||
it("plumbs Factory credentials through planned Docker E2E live lanes", () => {
|
||||
const reusableWorkflow = readFileSync(LIVE_E2E_WORKFLOW, "utf8");
|
||||
const releaseChecksWorkflow = readFileSync(RELEASE_CHECKS_WORKFLOW, "utf8");
|
||||
const scheduledWorkflow = readFileSync(SCHEDULED_LIVE_CHECKS_WORKFLOW, "utf8");
|
||||
const packageAcceptanceWorkflow = readFileSync(PACKAGE_ACCEPTANCE_WORKFLOW, "utf8");
|
||||
const testboxWorkflow = readFileSync(CI_CHECK_TESTBOX_WORKFLOW, "utf8");
|
||||
const crabboxHydrateWorkflow = readFileSync(CRABBOX_HYDRATE_WORKFLOW, "utf8");
|
||||
const dockerPlanAction = readFileSync(DOCKER_E2E_PLAN_ACTION, "utf8");
|
||||
const hydrateScript = readFileSync(CI_HYDRATE_LIVE_AUTH_SCRIPT, "utf8");
|
||||
|
||||
expect(hydrateScript).toContain(" FACTORY_API_KEY \\");
|
||||
expect(dockerPlanAction).toContain('if [[ "$credentials" == *",factory,"* ]]; then');
|
||||
expect(dockerPlanAction).toContain(
|
||||
"FACTORY_API_KEY is required for selected Docker E2E lanes.",
|
||||
);
|
||||
for (const workflow of [
|
||||
reusableWorkflow,
|
||||
releaseChecksWorkflow,
|
||||
scheduledWorkflow,
|
||||
packageAcceptanceWorkflow,
|
||||
testboxWorkflow,
|
||||
crabboxHydrateWorkflow,
|
||||
]) {
|
||||
expect(workflow).toContain("FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}");
|
||||
}
|
||||
expect(reusableWorkflow).toContain("FACTORY_API_KEY:\n required: false");
|
||||
expect(packageAcceptanceWorkflow).toContain("FACTORY_API_KEY:\n required: false");
|
||||
expect(reusableWorkflow).toContain('if [[ "$credentials" == *",factory,"* ]]; then');
|
||||
});
|
||||
|
||||
it("allows the Telegram lane to run from reusable package acceptance artifacts", () => {
|
||||
const workflow = readFileSync(NPM_TELEGRAM_WORKFLOW, "utf8");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user