mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-24 19:35:28 -06:00
fix(release): bind frozen harness layouts to candidate
This commit is contained in:
@@ -476,8 +476,9 @@ jobs:
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
ref: ${{ needs.preflight.outputs.checkout_revision }}
|
||||
fetch-depth: 1
|
||||
fetch-depth: 0
|
||||
fetch-tags: false
|
||||
filter: blob:none
|
||||
persist-credentials: false
|
||||
submodules: false
|
||||
|
||||
|
||||
@@ -77,17 +77,14 @@ function extractStatusSection(text, title) {
|
||||
return stripAnsi(section.join("\n"));
|
||||
}
|
||||
|
||||
function readSharedAuthProfileStoreText(stateDir) {
|
||||
const dbPath = path.join(stateDir, "state", "openclaw.sqlite");
|
||||
function readAuthProfileStoreText(dbPath, query, storeKey) {
|
||||
if (!fs.existsSync(dbPath)) {
|
||||
return "";
|
||||
}
|
||||
let db;
|
||||
try {
|
||||
db = new DatabaseSync(dbPath, { readOnly: true });
|
||||
const row = db
|
||||
.prepare("SELECT store_json FROM auth_profile_stores WHERE store_key = ?")
|
||||
.get("shared");
|
||||
const row = db.prepare(query).get(storeKey);
|
||||
return typeof row?.store_json === "string" ? row.store_json : "";
|
||||
} catch {
|
||||
return "";
|
||||
@@ -96,10 +93,50 @@ function readSharedAuthProfileStoreText(stateDir) {
|
||||
}
|
||||
}
|
||||
|
||||
function readCurrentAuthProfileStoreText(stateDir) {
|
||||
const dbPath = path.join(stateDir, "state", "openclaw.sqlite");
|
||||
return readAuthProfileStoreText(
|
||||
dbPath,
|
||||
"SELECT store_json FROM auth_profile_stores WHERE store_key = ?",
|
||||
"shared",
|
||||
);
|
||||
}
|
||||
|
||||
function readLegacyAuthProfileStoreText(stateDir) {
|
||||
const dbPath = path.join(stateDir, "agents", "main", "agent", "openclaw-agent.sqlite");
|
||||
return readAuthProfileStoreText(
|
||||
dbPath,
|
||||
"SELECT store_json FROM auth_profile_store WHERE store_key = ?",
|
||||
"primary",
|
||||
);
|
||||
}
|
||||
|
||||
function assertOnboardState() {
|
||||
const home = process.argv[3];
|
||||
const expectedLayout = process.argv[3];
|
||||
const home = process.argv[4];
|
||||
if (expectedLayout !== "legacy" && expectedLayout !== "current") {
|
||||
throw new Error(`unsupported onboard auth profile store layout: ${expectedLayout}`);
|
||||
}
|
||||
const stateDir = path.join(home, ".openclaw");
|
||||
const configPath = path.join(stateDir, "openclaw.json");
|
||||
|
||||
if (!fs.existsSync(configPath)) {
|
||||
throw new Error("onboard did not write openclaw.json");
|
||||
}
|
||||
const legacyAuthStoreText = readLegacyAuthProfileStoreText(stateDir);
|
||||
const currentAuthStoreText = readCurrentAuthProfileStoreText(stateDir);
|
||||
if (legacyAuthStoreText && currentAuthStoreText) {
|
||||
throw new Error("onboard persisted mixed legacy and current auth profile stores");
|
||||
}
|
||||
const observedLayout = legacyAuthStoreText ? "legacy" : currentAuthStoreText ? "current" : "";
|
||||
if (!observedLayout) {
|
||||
throw new Error("onboard did not persist auth profile store");
|
||||
}
|
||||
if (observedLayout !== expectedLayout) {
|
||||
throw new Error(
|
||||
`onboard persisted ${observedLayout} auth profile store; expected ${expectedLayout}`,
|
||||
);
|
||||
}
|
||||
const legacyAuthDatabase = path.join(
|
||||
stateDir,
|
||||
"agents",
|
||||
@@ -107,14 +144,10 @@ function assertOnboardState() {
|
||||
"agent",
|
||||
"openclaw-agent.sqlite",
|
||||
);
|
||||
|
||||
if (!fs.existsSync(configPath)) {
|
||||
throw new Error("onboard did not write openclaw.json");
|
||||
}
|
||||
if (fs.existsSync(legacyAuthDatabase)) {
|
||||
if (expectedLayout === "current" && fs.existsSync(legacyAuthDatabase)) {
|
||||
throw new Error("onboard created the retired main-agent auth database");
|
||||
}
|
||||
const authStoreText = readSharedAuthProfileStoreText(stateDir);
|
||||
const authStoreText = expectedLayout === "legacy" ? legacyAuthStoreText : currentAuthStoreText;
|
||||
if (!authStoreText) {
|
||||
throw new Error("onboard did not persist auth profile store");
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ HOST_BUILD="${OPENCLAW_NPM_ONBOARD_HOST_BUILD:-1}"
|
||||
PACKAGE_TGZ="${OPENCLAW_CURRENT_PACKAGE_TGZ:-}"
|
||||
CHANNEL="${OPENCLAW_NPM_ONBOARD_CHANNEL:-telegram}"
|
||||
USE_SOURCE_PLUGIN_PACKAGE="${OPENCLAW_NPM_ONBOARD_USE_SOURCE_PLUGIN_PACKAGE:-0}"
|
||||
AUTH_PROFILE_STORE_CUTOVER_SHA="a8a9f284fb91af6a9d78fe66f9141eb01e009b21"
|
||||
JSON_ARTIFACT_MAX_BYTES="$(
|
||||
docker_e2e_read_positive_int_env OPENCLAW_NPM_ONBOARD_JSON_ARTIFACT_MAX_BYTES 1048576
|
||||
)"
|
||||
@@ -24,6 +25,50 @@ run_log=""
|
||||
plugin_pack_dir=""
|
||||
plugin_package_args=()
|
||||
|
||||
resolve_auth_profile_store_layout() {
|
||||
local selected_sha="${OPENCLAW_DOCKER_E2E_SELECTED_SHA:-}"
|
||||
local source_sha
|
||||
source_sha="$(git -C "$SOURCE_ROOT" rev-parse HEAD)"
|
||||
if [ -z "$selected_sha" ]; then
|
||||
selected_sha="$source_sha"
|
||||
fi
|
||||
if [[ ! "$selected_sha" =~ ^[0-9a-f]{40}$ ]] || [ "$source_sha" != "$selected_sha" ]; then
|
||||
echo "Cannot bind npm onboarding auth layout to candidate $selected_sha at $source_sha." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$(git -C "$SOURCE_ROOT" rev-parse --is-shallow-repository)" = "true" ]; then
|
||||
git -C "$SOURCE_ROOT" fetch --no-tags --filter=blob:none --deepen=1024 origin \
|
||||
"$selected_sha" "$AUTH_PROFILE_STORE_CUTOVER_SHA"
|
||||
elif ! git -C "$SOURCE_ROOT" cat-file -e "$AUTH_PROFILE_STORE_CUTOVER_SHA^{commit}"; then
|
||||
git -C "$SOURCE_ROOT" fetch --no-tags --filter=blob:none --depth=1024 origin \
|
||||
"$AUTH_PROFILE_STORE_CUTOVER_SHA"
|
||||
fi
|
||||
|
||||
if ! git -C "$SOURCE_ROOT" merge-base "$AUTH_PROFILE_STORE_CUTOVER_SHA" "$selected_sha" \
|
||||
>/dev/null; then
|
||||
echo "Cannot prove candidate $selected_sha auth layout ancestry relative to $AUTH_PROFILE_STORE_CUTOVER_SHA." >&2
|
||||
return 1
|
||||
fi
|
||||
set +e
|
||||
git -C "$SOURCE_ROOT" merge-base --is-ancestor \
|
||||
"$AUTH_PROFILE_STORE_CUTOVER_SHA" "$selected_sha"
|
||||
local ancestry_status=$?
|
||||
set -e
|
||||
if [ "$ancestry_status" -eq 0 ]; then
|
||||
printf '%s\n' "current"
|
||||
elif [ "$ancestry_status" -eq 1 ]; then
|
||||
# Frozen release branches can diverge before the cutover; absence of the
|
||||
# cutover commit is the legacy contract, not a requirement to follow main.
|
||||
printf '%s\n' "legacy"
|
||||
else
|
||||
echo "Cannot determine candidate $selected_sha auth layout ancestry relative to $AUTH_PROFILE_STORE_CUTOVER_SHA." >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
AUTH_PROFILE_STORE_LAYOUT="$(resolve_auth_profile_store_layout)"
|
||||
|
||||
cleanup() {
|
||||
if [ -n "${PACKAGE_TGZ:-}" ]; then
|
||||
docker_e2e_cleanup_package_tgz "$PACKAGE_TGZ"
|
||||
@@ -103,6 +148,7 @@ echo "Running npm tarball onboard/channel/agent Docker E2E ($CHANNEL)..."
|
||||
if ! docker_e2e_run_with_harness \
|
||||
-e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
|
||||
-e OPENCLAW_NPM_ONBOARD_CHANNEL="$CHANNEL" \
|
||||
-e "OPENCLAW_NPM_ONBOARD_AUTH_LAYOUT=$AUTH_PROFILE_STORE_LAYOUT" \
|
||||
-e "OPENCLAW_NPM_ONBOARD_JSON_ARTIFACT_MAX_BYTES=$JSON_ARTIFACT_MAX_BYTES" \
|
||||
-e "OPENCLAW_NPM_ONBOARD_STATUS_TEXT_MAX_BYTES=$STATUS_TEXT_MAX_BYTES" \
|
||||
-e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" \
|
||||
@@ -208,7 +254,8 @@ openclaw onboard --non-interactive --accept-risk \
|
||||
--skip-health \
|
||||
--json >/tmp/openclaw-onboard.json
|
||||
|
||||
node scripts/e2e/lib/npm-onboard-channel-agent/assertions.mjs assert-onboard-state "$HOME"
|
||||
node scripts/e2e/lib/npm-onboard-channel-agent/assertions.mjs \
|
||||
assert-onboard-state "$OPENCLAW_NPM_ONBOARD_AUTH_LAYOUT" "$HOME"
|
||||
|
||||
openclaw_e2e_assert_dep_absent "$DEP_SENTINEL" "$HOME/.openclaw"
|
||||
|
||||
|
||||
@@ -2716,6 +2716,21 @@ docker_e2e_docker_run_cmd run demo
|
||||
expect(script).not.toContain("/tmp/openclaw-channel-add.log");
|
||||
});
|
||||
|
||||
it("binds npm onboarding auth layout to the exact candidate ancestry", () => {
|
||||
const script = readFileSync(NPM_ONBOARD_CHANNEL_AGENT_DOCKER_E2E_PATH, "utf8");
|
||||
|
||||
expectTextToIncludeAll(script, [
|
||||
'AUTH_PROFILE_STORE_CUTOVER_SHA="a8a9f284fb91af6a9d78fe66f9141eb01e009b21"',
|
||||
'selected_sha="${OPENCLAW_DOCKER_E2E_SELECTED_SHA:-}"',
|
||||
"fetch --no-tags --filter=blob:none --deepen=1024 origin",
|
||||
'merge-base "$AUTH_PROFILE_STORE_CUTOVER_SHA" "$selected_sha"',
|
||||
'merge-base --is-ancestor \\\n "$AUTH_PROFILE_STORE_CUTOVER_SHA" "$selected_sha"',
|
||||
"OPENCLAW_NPM_ONBOARD_AUTH_LAYOUT=$AUTH_PROFILE_STORE_LAYOUT",
|
||||
]);
|
||||
expect(script).toContain('elif [ "$ancestry_status" -eq 1 ]; then');
|
||||
expect(script).toContain('if ! git -C "$SOURCE_ROOT" merge-base');
|
||||
});
|
||||
|
||||
it("keeps real-TTY onboarding drivers aligned with the first-agent prompt", () => {
|
||||
expectOrderedScriptFragments(readFileSync(RELEASE_TYPED_ONBOARDING_SCENARIO_PATH, "utf8"), [
|
||||
'wait_for_log "Continue?"',
|
||||
|
||||
@@ -37,7 +37,7 @@ function writeOnboardConfig(home: string): void {
|
||||
);
|
||||
}
|
||||
|
||||
function writeSharedAuthProfileStoreSqlite(home: string, store: unknown): void {
|
||||
function writeCurrentAuthProfileStoreSqlite(home: string, store: unknown): void {
|
||||
const stateDir = path.join(home, ".openclaw", "state");
|
||||
fs.mkdirSync(stateDir, { recursive: true });
|
||||
const db = new DatabaseSync(path.join(stateDir, "openclaw.sqlite"));
|
||||
@@ -60,6 +60,29 @@ function writeSharedAuthProfileStoreSqlite(home: string, store: unknown): void {
|
||||
}
|
||||
}
|
||||
|
||||
function writeLegacyAuthProfileStoreSqlite(home: string, store: unknown): void {
|
||||
const agentDir = path.join(home, ".openclaw", "agents", "main", "agent");
|
||||
fs.mkdirSync(agentDir, { recursive: true });
|
||||
const db = new DatabaseSync(path.join(agentDir, "openclaw-agent.sqlite"));
|
||||
try {
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS auth_profile_store (
|
||||
store_key TEXT NOT NULL PRIMARY KEY,
|
||||
store_json TEXT NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
`);
|
||||
db.prepare(
|
||||
`
|
||||
INSERT INTO auth_profile_store (store_key, store_json, updated_at)
|
||||
VALUES (?, ?, ?)
|
||||
`,
|
||||
).run("primary", JSON.stringify(store), Date.now());
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
|
||||
function runAssert(home: string, channel: string, ...tokens: string[]) {
|
||||
return spawnSync(
|
||||
process.execPath,
|
||||
@@ -75,8 +98,8 @@ function runAssert(home: string, channel: string, ...tokens: string[]) {
|
||||
);
|
||||
}
|
||||
|
||||
function runOnboardAssert(home: string) {
|
||||
return spawnSync(process.execPath, [assertionsPath, "assert-onboard-state", home], {
|
||||
function runOnboardAssert(home: string, layout: "current" | "legacy") {
|
||||
return spawnSync(process.execPath, [assertionsPath, "assert-onboard-state", layout, home], {
|
||||
encoding: "utf8",
|
||||
env: {
|
||||
...process.env,
|
||||
@@ -218,33 +241,36 @@ describe("npm onboard channel agent assertions", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("validates OpenAI env refs from the shared SQLite auth profile store", () => {
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-onboard-assertions-"));
|
||||
const agentDir = path.join(tempDir, ".openclaw", "agents", "main", "agent");
|
||||
it.each([
|
||||
["legacy", writeLegacyAuthProfileStoreSqlite],
|
||||
["current", writeCurrentAuthProfileStoreSqlite],
|
||||
] as const)(
|
||||
"validates OpenAI env refs from the %s SQLite auth profile store",
|
||||
(layout, writeStore) => {
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-onboard-assertions-"));
|
||||
|
||||
try {
|
||||
writeOnboardConfig(tempDir);
|
||||
writeSharedAuthProfileStoreSqlite(tempDir, {
|
||||
version: 1,
|
||||
profiles: {
|
||||
"openai:api-key": {
|
||||
type: "api_key",
|
||||
provider: "openai",
|
||||
keyRef: { source: "env", provider: "default", id: "OPENAI_API_KEY" },
|
||||
try {
|
||||
writeOnboardConfig(tempDir);
|
||||
writeStore(tempDir, {
|
||||
version: 1,
|
||||
profiles: {
|
||||
"openai:api-key": {
|
||||
type: "api_key",
|
||||
provider: "openai",
|
||||
keyRef: { source: "env", provider: "default", id: "OPENAI_API_KEY" },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const result = runOnboardAssert(tempDir);
|
||||
const result = runOnboardAssert(tempDir, layout);
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
expect(result.stderr).toBe("");
|
||||
expect(fs.existsSync(agentDir)).toBe(false);
|
||||
expect(fs.existsSync(path.join(agentDir, "auth-profiles.json"))).toBe(false);
|
||||
} finally {
|
||||
fs.rmSync(tempDir, { force: true, recursive: true });
|
||||
}
|
||||
});
|
||||
expect(result.status).toBe(0);
|
||||
expect(result.stderr).toBe("");
|
||||
} finally {
|
||||
fs.rmSync(tempDir, { force: true, recursive: true });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it("rejects auth profile stores without a usable OpenAI env ref", () => {
|
||||
const cases: unknown[] = [
|
||||
@@ -262,9 +288,9 @@ describe("npm onboard channel agent assertions", () => {
|
||||
|
||||
try {
|
||||
writeOnboardConfig(tempDir);
|
||||
writeSharedAuthProfileStoreSqlite(tempDir, store);
|
||||
writeCurrentAuthProfileStoreSqlite(tempDir, store);
|
||||
|
||||
const result = runOnboardAssert(tempDir);
|
||||
const result = runOnboardAssert(tempDir, "current");
|
||||
|
||||
expect(result.status).not.toBe(0);
|
||||
expect(result.stderr).toContain("auth profile did not persist OPENAI_API_KEY env ref");
|
||||
@@ -278,7 +304,7 @@ describe("npm onboard channel agent assertions", () => {
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-onboard-assertions-"));
|
||||
try {
|
||||
writeOnboardConfig(tempDir);
|
||||
writeSharedAuthProfileStoreSqlite(tempDir, {
|
||||
writeCurrentAuthProfileStoreSqlite(tempDir, {
|
||||
version: 1,
|
||||
profiles: {
|
||||
"openai:api-key": {
|
||||
@@ -289,7 +315,7 @@ describe("npm onboard channel agent assertions", () => {
|
||||
},
|
||||
});
|
||||
|
||||
const result = runOnboardAssert(tempDir);
|
||||
const result = runOnboardAssert(tempDir, "current");
|
||||
|
||||
expect(result.status).not.toBe(0);
|
||||
expect(result.stderr).toContain("auth profile persisted the raw OpenAI test key");
|
||||
@@ -298,13 +324,75 @@ describe("npm onboard channel agent assertions", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects a fresh install that recreates the retired main-agent auth database", () => {
|
||||
it("rejects mixed legacy and current auth profile stores", () => {
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-onboard-assertions-"));
|
||||
|
||||
try {
|
||||
writeOnboardConfig(tempDir);
|
||||
const store = {
|
||||
version: 1,
|
||||
profiles: {
|
||||
"openai:api-key": {
|
||||
type: "api_key",
|
||||
provider: "openai",
|
||||
keyRef: { source: "env", provider: "default", id: "OPENAI_API_KEY" },
|
||||
},
|
||||
},
|
||||
};
|
||||
writeLegacyAuthProfileStoreSqlite(tempDir, store);
|
||||
writeCurrentAuthProfileStoreSqlite(tempDir, store);
|
||||
|
||||
const result = runOnboardAssert(tempDir, "current");
|
||||
|
||||
expect(result.status).not.toBe(0);
|
||||
expect(result.stderr).toContain(
|
||||
"onboard persisted mixed legacy and current auth profile stores",
|
||||
);
|
||||
} finally {
|
||||
fs.rmSync(tempDir, { force: true, recursive: true });
|
||||
}
|
||||
});
|
||||
|
||||
it.each([
|
||||
["current", "legacy", writeCurrentAuthProfileStoreSqlite],
|
||||
["legacy", "current", writeLegacyAuthProfileStoreSqlite],
|
||||
] as const)(
|
||||
"rejects the %s layout when the candidate requires %s",
|
||||
(observedLayout, expectedLayout, writeStore) => {
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-onboard-assertions-"));
|
||||
|
||||
try {
|
||||
writeOnboardConfig(tempDir);
|
||||
writeStore(tempDir, {
|
||||
version: 1,
|
||||
profiles: {
|
||||
"openai:api-key": {
|
||||
type: "api_key",
|
||||
provider: "openai",
|
||||
keyRef: { source: "env", provider: "default", id: "OPENAI_API_KEY" },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const result = runOnboardAssert(tempDir, expectedLayout);
|
||||
|
||||
expect(result.status).not.toBe(0);
|
||||
expect(result.stderr).toContain(
|
||||
`onboard persisted ${observedLayout} auth profile store; expected ${expectedLayout}`,
|
||||
);
|
||||
} finally {
|
||||
fs.rmSync(tempDir, { force: true, recursive: true });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it("keeps the current-layout ban on the retired main-agent database", () => {
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-onboard-assertions-"));
|
||||
const legacyAgentDir = path.join(tempDir, ".openclaw", "agents", "main", "agent");
|
||||
|
||||
try {
|
||||
writeOnboardConfig(tempDir);
|
||||
writeSharedAuthProfileStoreSqlite(tempDir, {
|
||||
writeCurrentAuthProfileStoreSqlite(tempDir, {
|
||||
version: 1,
|
||||
profiles: {
|
||||
"openai:api-key": {
|
||||
@@ -317,7 +405,7 @@ describe("npm onboard channel agent assertions", () => {
|
||||
fs.mkdirSync(legacyAgentDir, { recursive: true });
|
||||
new DatabaseSync(path.join(legacyAgentDir, "openclaw-agent.sqlite")).close();
|
||||
|
||||
const result = runOnboardAssert(tempDir);
|
||||
const result = runOnboardAssert(tempDir, "current");
|
||||
|
||||
expect(result.status).not.toBe(0);
|
||||
expect(result.stderr).toContain("onboard created the retired main-agent auth database");
|
||||
@@ -326,6 +414,21 @@ describe("npm onboard channel agent assertions", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects a missing auth profile store", () => {
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-onboard-assertions-"));
|
||||
|
||||
try {
|
||||
writeOnboardConfig(tempDir);
|
||||
|
||||
const result = runOnboardAssert(tempDir, "legacy");
|
||||
|
||||
expect(result.status).not.toBe(0);
|
||||
expect(result.stderr).toContain("onboard did not persist auth profile store");
|
||||
} finally {
|
||||
fs.rmSync(tempDir, { force: true, recursive: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("validates channel tokens in their canonical config fields", () => {
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-channel-assertions-"));
|
||||
try {
|
||||
|
||||
@@ -754,6 +754,16 @@ describe("scripts/lib/plugin-prerelease-test-plan.mts", () => {
|
||||
expect(extensionShard.strategy.matrix).toBe(
|
||||
"${{ fromJson(needs.preflight.outputs.plugin_prerelease_extension_matrix) }}",
|
||||
);
|
||||
expect(
|
||||
extensionShard.steps.find((step: WorkflowStep) => step.name === "Checkout").with,
|
||||
).toEqual({
|
||||
"fetch-depth": 0,
|
||||
"fetch-tags": false,
|
||||
filter: "blob:none",
|
||||
"persist-credentials": false,
|
||||
ref: "${{ needs.preflight.outputs.checkout_revision }}",
|
||||
submodules: false,
|
||||
});
|
||||
expect(
|
||||
extensionShard.steps.find((step: WorkflowStep) => step.name === "Run extension shard").run,
|
||||
).toContain("--retry=1");
|
||||
|
||||
Reference in New Issue
Block a user