mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-12 16:05:43 +00:00
* fix: keep an app's deployed policy on wmill push Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X47gNFqf1SsA67zTm71f8c * fix: keep a first push's file-stated app policy Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X47gNFqf1SsA67zTm71f8c * fix: deploy a file-stated viewer app as viewer, not publisher Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X47gNFqf1SsA67zTm71f8c * fix: never put a repo-stated run identity on the wire Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X47gNFqf1SsA67zTm71f8c * fix: send the deployed run identity only when the push may claim it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X47gNFqf1SsA67zTm71f8c * fix: let a file-stated execution mode win in both directions Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X47gNFqf1SsA67zTm71f8c * refactor: drop the app-file execution mode helper with no caller Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X47gNFqf1SsA67zTm71f8c * feat: warn before a raw-app push takes over the run-as user Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X47gNFqf1SsA67zTm71f8c * fix: keep an app's run-as user when a push only deletes one of its files Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X47gNFqf1SsA67zTm71f8c * refactor: settle an app's ownership check before any content parsing Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X47gNFqf1SsA67zTm71f8c * fix: share one rule for which raw-app files a push sends Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X47gNFqf1SsA67zTm71f8c * fix: stop tracking raw-app files no push ever sends Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X47gNFqf1SsA67zTm71f8c * test: bundle for real instead of stubbing the module for every suite Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X47gNFqf1SsA67zTm71f8c * docs: record why a module mock cannot be undone by afterAll Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X47gNFqf1SsA67zTm71f8c * fix: keep tracking a runnable whose file shares a bundle-excluded name Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X47gNFqf1SsA67zTm71f8c * test: pin both halves of the backend runnable rule Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X47gNFqf1SsA67zTm71f8c --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
52 lines
2.4 KiB
TypeScript
52 lines
2.4 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import {
|
|
executionModeForPush,
|
|
generatingPolicy,
|
|
markAccessFromPolicy,
|
|
} from "../src/commands/app/app.ts";
|
|
|
|
// The access mode is the one policy field a tracked app keeps; a pull then a push must
|
|
// deploy the mode that was pulled, guest included, not a default.
|
|
test("the access mode survives the app.yaml round trip", async () => {
|
|
const guest: any = { policy: { execution_mode: "guest" }, value: {} };
|
|
markAccessFromPolicy(guest);
|
|
guest.policy = undefined;
|
|
expect(guest.guests).toBe(true);
|
|
expect(guest.public).toBeUndefined();
|
|
expect(executionModeForPush(guest, undefined)).toBe("guest");
|
|
await generatingPolicy(
|
|
guest,
|
|
"u/test/app",
|
|
executionModeForPush(guest, undefined),
|
|
undefined,
|
|
);
|
|
expect(guest.policy.execution_mode).toBe("guest");
|
|
|
|
const anonymous: any = { policy: { execution_mode: "anonymous" }, value: {} };
|
|
markAccessFromPolicy(anonymous);
|
|
anonymous.policy = undefined;
|
|
expect(anonymous.public).toBe(true);
|
|
expect(executionModeForPush(anonymous, undefined)).toBe("anonymous");
|
|
|
|
expect(executionModeForPush({ policy: { execution_mode: "publisher" } }, undefined)).toBe("publisher");
|
|
expect(executionModeForPush({}, undefined)).toBe("publisher");
|
|
});
|
|
|
|
// `viewer` is the narrowest mode — each runnable runs as the viewer, not as the
|
|
// app's identity — and the only one with no marker in the file, so both ways it
|
|
// can reach a push must survive rather than widen to `publisher`.
|
|
test("viewer is never widened to publisher by a push", () => {
|
|
// Carried over from the deployed app: a pull writes no marker for it.
|
|
expect(executionModeForPush({}, { execution_mode: "viewer" })).toBe("viewer");
|
|
// Stated by the file, which is all a first push has to go on.
|
|
expect(executionModeForPush({ policy: { execution_mode: "viewer" } }, undefined)).toBe("viewer");
|
|
// The open-access markers still win, in either direction.
|
|
expect(executionModeForPush({ public: true }, { execution_mode: "viewer" })).toBe("anonymous");
|
|
expect(executionModeForPush({}, { execution_mode: "anonymous" })).toBe("publisher");
|
|
// A stated mode is authoritative both ways: the carry-over is for a file that
|
|
// says nothing, so it must not pin a deployed app to `viewer` forever.
|
|
expect(
|
|
executionModeForPush({ policy: { execution_mode: "publisher" } }, { execution_mode: "viewer" })
|
|
).toBe("publisher");
|
|
});
|