Files
windmill/cli/test/raw_app_workspace_deps_unit.test.ts
Ruben FiszelandClaude Opus 5 38b6099b4c fix: apply default workspace dependencies to raw app runnables (#10427)
* fix: apply default workspace dependencies to raw app runnables

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

* test: state the tree-path constraint in the raw app deps regression test

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

* fix: honor defaultTs when loading raw app runnables for lock generation

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

* fix: resolve defaultTs from wmill.yaml at every raw app runnable read

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

* refactor: thread defaultTs from command entry instead of re-reading the config

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

* refactor: give pushObj an options object for its optional arguments

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

* docs: reattach the pushObj JSDoc after the options-object refactor

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:42:43 +02:00

85 lines
2.9 KiB
TypeScript

/**
* Raw app workspace dependencies
*
* A raw app keeps its runnables in `backend/`, not in `raw_app.yaml`. The
* workspace dependency filtering must resolve those files, otherwise the
* default `dependencies/package.json` is dropped and locks are regenerated
* against unpinned versions.
*
* Exercised through the legacy (tree-less) path: tree mode sources its deps
* from `getMismatchedWorkspaceDeps()`, which is only populated by an
* `uploadScripts` round-trip, so it cannot run offline. Both paths filter the
* same `appValue`, so resolving it correctly is what this pins.
*/
import { expect, test } from "bun:test";
import * as path from "node:path";
import os from "node:os";
import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
import { generateAppLocksInternal } from "../src/commands/app/app_metadata.ts";
import { Workspace } from "../src/commands/workspace/workspace.ts";
const stubWorkspace: Workspace = {
remote: "http://localhost:0/",
workspaceId: "test",
name: "test",
token: "test",
};
const APP_FOLDER = path.join("f", "example.raw_app");
async function withTempDir(fn: (tempDir: string) => Promise<void>): Promise<void> {
const tempDir = await mkdtemp(path.join(os.tmpdir(), "wmill_raw_app_deps_"));
const originalCwd = process.cwd();
try {
process.chdir(tempDir);
await fn(tempDir);
} finally {
process.chdir(originalCwd);
await rm(tempDir, { recursive: true, force: true });
}
}
test("raw app: default workspace deps are picked up from backend runnables", async () => {
await withTempDir(async () => {
await mkdir(path.join(APP_FOLDER, "backend"), { recursive: true });
await writeFile(
path.join(APP_FOLDER, "raw_app.yaml"),
`summary: "example raw app"\npolicy:\n execution_mode: publisher\n triggerables: {}\n`,
"utf-8",
);
await writeFile(
path.join(APP_FOLDER, "backend", "test.ts"),
`import * as wmill from "windmill-client"\n\nexport async function main() {\n return wmill.getVariable("example")\n}\n`,
"utf-8",
);
await generateAppLocksInternal(
APP_FOLDER,
true,
false,
stubWorkspace,
{ defaultTs: "bun" },
true, // justUpdateMetadataLock — no backend round-trip
true,
);
expect(
await generateAppLocksInternal(APP_FOLDER, true, true, stubWorkspace, { defaultTs: "bun" }, false, true),
).toBeUndefined();
// The runnable has no `package_json` annotation, so it uses the default
// manifest — adding it must invalidate the app.
await mkdir("dependencies", { recursive: true });
await writeFile(
path.join("dependencies", "package.json"),
`{"dependencies": {"windmill-client": "1.742.0"}}`,
"utf-8",
);
expect(
await generateAppLocksInternal(APP_FOLDER, true, true, stubWorkspace, { defaultTs: "bun" }, false, true),
).toEqual("f/example.raw_app");
});
});