Files
windmill/cli/test/script_push_up_to_date.test.ts
Ruben Fiszel ef4dc46d4b fix(cli): keep script settings on push and repair the up-to-date check (#10741)
* fix(cli): keep script retention, debounce and cache settings on push

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(cli): surface the create response when the fixture fails

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(cli): drop debounce settings the CI build refuses to accept

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* repair the script push up-to-date comparison (#10743)

* test: settle the backlog before the capped audit-export drain (#10737)

* test: settle the backlog before the capped audit-export drain

* chore: update ee-repo-ref to bd4de74eb37b32a2b6c7c69f6dedac031ef8436b

This commit updates the EE repository reference after PR #730 was merged in windmill-ee-private.

Previous ee-repo-ref: b5a5f9114df26088cfe976d91f10e55ba8bfcaa6

New ee-repo-ref: bd4de74eb37b32a2b6c7c69f6dedac031ef8436b

Automated by sync-ee-ref workflow.

---------

Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>

* fix(cli): repair the script push up-to-date comparison

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(cli): drain dependency jobs and pin a non-1 priority skip

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(cli): describe the priority fixture without the old comparison

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* refactor(cli): read cache_ignore_s3_path off the typed response

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(cli): stop redeploying bunnative scripts on every push

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-08-18 12:26:41 +02:00

146 lines
5.7 KiB
TypeScript

/**
* `wmill script push` short-circuits when the local script already matches the
* remote. The comparison has to hold in both directions: an untouched script
* deploys nothing, and every field the push body carries (labels and the language
* inferred from defaultTs included) still counts as a change.
*/
import { expect, test } from "bun:test";
import { writeFile, readFile, mkdir } from "node:fs/promises";
import { withTestBackend } from "./test_backend.ts";
import { waitForDeploymentJobs } from "./new_commands_helpers.ts";
test("Integration: script push skips an unchanged script and deploys a changed one", async () => {
await withTestBackend(async (backend, tempDir) => {
const uniqueId = Date.now();
const scriptPath = `f/test/uptodate_${uniqueId}`;
const getScript = async () =>
await (
await backend.apiRequest!(
`/api/w/${backend.workspace}/scripts/get/p/${scriptPath}`,
)
).json();
const push = async () =>
await backend.runCLICommand(["script", "push", `${scriptPath}.ts`], tempDir);
const wmillYaml = (defaultTs: string) =>
`defaultTs: ${defaultTs}\nincludes:\n - "${scriptPath}**"\nexcludes: []\n`;
await mkdir(`${tempDir}/f/test`, { recursive: true });
await backend.apiRequest!(`/api/w/${backend.workspace}/folders/create`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "test" }),
});
const createResp = await backend.apiRequest!(
`/api/w/${backend.workspace}/scripts/create`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
path: scriptPath,
content: `export async function main() {\n return "Hello world";\n}`,
summary: "Test up to date",
description: "",
language: "bun",
kind: "script",
schema: {
$schema: "https://json-schema.org/draft/2020-12/schema",
type: "object",
properties: {},
required: [],
},
labels: ["l1"],
}),
},
);
expect(createResp.ok).toEqual(true);
await writeFile(`${tempDir}/wmill.yaml`, wmillYaml("bun"), "utf-8");
// The lock a deploy's dependency job writes is part of the comparison, so every
// pull has to happen after that job has landed or the skip races it.
await waitForDeploymentJobs(backend);
expect((await backend.runCLICommand(["sync", "pull", "--yes"], tempDir)).code).toEqual(0);
const hashBefore = (await getScript()).hash;
expect((await push()).stdout).toContain("is up to date");
expect((await getScript()).hash).toEqual(hashBefore);
const metadataPath = `${tempDir}/${scriptPath}.script.yaml`;
await writeFile(
metadataPath,
(await readFile(metadataPath, "utf-8")).replace("- l1", "- l2"),
"utf-8",
);
expect((await push()).stdout).not.toContain("is up to date");
expect((await getScript()).labels).toEqual(["l2"]);
// 2, not 0 or 1: those two are the values a truthiness comparison would also
// call equal, so they cannot pin that priority is compared by value.
await waitForDeploymentJobs(backend);
expect((await backend.runCLICommand(["sync", "pull", "--yes"], tempDir)).code).toEqual(0);
await writeFile(metadataPath, (await readFile(metadataPath, "utf-8")) + "priority: 2\n", "utf-8");
expect((await push()).stdout).not.toContain("is up to date");
expect((await getScript()).priority).toEqual(2);
await waitForDeploymentJobs(backend);
expect((await backend.runCLICommand(["sync", "pull", "--yes"], tempDir)).code).toEqual(0);
expect((await push()).stdout).toContain("is up to date");
await writeFile(`${tempDir}/wmill.yaml`, wmillYaml("deno"), "utf-8");
expect((await push()).stdout).not.toContain("is up to date");
expect((await getScript()).language).toEqual("deno");
});
});
test("Integration: an unchanged bunnative script is not redeployed", async () => {
await withTestBackend(async (backend, tempDir) => {
const uniqueId = Date.now();
const scriptPath = `f/test/native_${uniqueId}`;
await mkdir(`${tempDir}/f/test`, { recursive: true });
await backend.apiRequest!(`/api/w/${backend.workspace}/folders/create`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "test" }),
});
const createResp = await backend.apiRequest!(
`/api/w/${backend.workspace}/scripts/create`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
path: scriptPath,
// The server stores this as bunnative: the language is derived from the
// annotation, and no file extension can express it back.
content: `//native\nexport async function main() {\n return "Hello world";\n}`,
summary: "Test bunnative",
description: "",
language: "bun",
kind: "script",
}),
},
);
expect(createResp.ok).toEqual(true);
await writeFile(
`${tempDir}/wmill.yaml`,
`defaultTs: bun\nincludes:\n - "${scriptPath}**"\nexcludes: []\n`,
"utf-8",
);
await waitForDeploymentJobs(backend);
expect((await backend.runCLICommand(["sync", "pull", "--yes"], tempDir)).code).toEqual(0);
const remote = await (
await backend.apiRequest!(
`/api/w/${backend.workspace}/scripts/get/p/${scriptPath}`,
)
).json();
expect(remote.language).toEqual("bunnative");
const result = await backend.runCLICommand(
["script", "push", `${scriptPath}.ts`],
tempDir,
);
expect(result.stdout).toContain("is up to date");
});
});