Merge remote-tracking branch 'origin/main' into hugo/resource-type-guidance

This commit is contained in:
hugocasa
2026-10-12 09:51:15 +02:00
338 changed files with 7866 additions and 3496 deletions
+2 -2
View File
@@ -77,8 +77,8 @@ const WORKSPACE_CONFIG_SCHEMA = {
*/
export const CONFIG_REFERENCE: ConfigOption[] = [
// ── Core ──────────────────────────────────────────────────────────────
{ name: "defaultTs", type: "string", enum: ["bun", "deno"], default: "bun", description: "Default TypeScript runtime for new scripts",
inlineComment: "bun or deno" },
{ name: "defaultTs", type: "string", enum: ["bun", "deno"], default: "bun", description: "TypeScript runtime of a plain .ts script (bun when absent)",
skipInTemplate: true },
{ name: "includes", type: "array", items: { type: "string" }, default: '["f/**"]', description: "Glob patterns for files to include in sync",
templateValue: '\n - "f/**"', inlineComment: 'use ** for all, f/** for folder-scoped' },
{ name: "extraIncludes", type: "array", items: { type: "string" }, default: "[]", description: "Additional glob patterns merged with includes (useful in workspace overrides)",
-8
View File
@@ -320,12 +320,6 @@ export async function readConfigFile(opts?: { warnIfMissing?: boolean }): Promis
delete (conf as any)?.environments;
delete conf?.git_branches;
if (conf?.defaultTs == undefined) {
log.warn(
"No defaultTs defined in your wmill.yaml. Using 'bun' as default."
);
}
// Initialize global nonDottedPaths setting from config
setNonDottedPaths(conf?.nonDottedPaths ?? false);
@@ -376,7 +370,6 @@ export const DEFAULT_SYNC_OPTIONS: Readonly<
Required<
Pick<
SyncOptions,
| "defaultTs"
| "includes"
| "excludes"
| "codebases"
@@ -401,7 +394,6 @@ export const DEFAULT_SYNC_OPTIONS: Readonly<
>
>
> = {
defaultTs: "bun",
includes: ["f/**"],
excludes: [],
codebases: [],
+1 -1
View File
@@ -10,4 +10,4 @@ export const WM_FORK_PREFIX = "wm-fork";
// (e.g. utils.ts) can read it without importing main.ts and creating a circular
// dependency (main → workspace → utils → main) that triggers a TDZ.
// Re-exported from main.ts for backwards compatibility.
export const VERSION = "1.829.0";
export const VERSION = "1.830.0";
+6 -2
View File
@@ -34,10 +34,14 @@ export async function buildPermissionedAsContext(
return undefined;
}
const user = await wmill.whoami({ workspace });
// A superadmin acts as admin in every workspace, so the backend preserves
// ownership for them even when their own `usr` row there is not an admin.
const userIsAdminOrDeployer =
user.is_admin || (user.groups ?? []).includes("wm_deployers");
user.is_admin ||
user.is_super_admin ||
(user.groups ?? []).includes("wm_deployers");
log.debug(
`permissioned_as: user=${user.email}, is_admin=${user.is_admin}, groups=${JSON.stringify(user.groups)}, isAdminOrDeployer=${userIsAdminOrDeployer}`
`permissioned_as: user=${user.email}, is_admin=${user.is_admin}, is_super_admin=${user.is_super_admin}, groups=${JSON.stringify(user.groups)}, isAdminOrDeployer=${userIsAdminOrDeployer}`
);
return {
userCache: new Map(),
+1 -1
View File
@@ -413,7 +413,7 @@ export async function pushWorkspaceSettings(
const localOperatorSettings = localSettings.operator_settings && {
...localSettings.operator_settings,
};
for (const key of ["builder_flows", "manage_schedules", "manage_triggers"] as const) {
for (const key of ["builder_flows", "builder_apps", "manage_schedules", "manage_triggers"] as const) {
const remote = settings.operator_settings?.[key];
if (localOperatorSettings && localOperatorSettings[key] === undefined && remote !== undefined) {
localOperatorSettings[key] = remote;
+3 -1
View File
@@ -189,6 +189,8 @@ const LANG_ANNOTATIONS: Partial<Record<ScriptLanguage, string[]>> = {
"py311",
"py312",
"py313",
"py314",
"py315",
"sandbox",
],
bun: ["npm", "nodejs", "native", "nobundling", "sandbox"],
@@ -208,7 +210,7 @@ const LANG_ANNOTATIONS: Partial<Record<ScriptLanguage, string[]>> = {
*
* `# py: <specifier>` is the exception the macro does not cover: the python
* import parser reads it directly (`windmill-parser-py-imports`, alongside the
* `py310`..`py313` flags) to pick the interpreter, which changes what resolves.
* `py310`..`py315` flags) to pick the interpreter, which changes what resolves.
*/
export function hasLockAffectingAnnotation(
scriptContent: string,
+1 -5
View File
@@ -56,8 +56,6 @@ test("Init: createWorkspaceProfile includes defaults when no repositories exist"
expect(Array.isArray(workspaceProfile.excludes)).toBeTruthy();
expect(workspaceProfile.excludes.length).toEqual(0);
expect(workspaceProfile.defaultTs).toEqual('bun');
console.log('Workspace profile correctly includes default sync settings when no repositories exist');
});
@@ -74,8 +72,6 @@ test("Init: verify DEFAULT_SYNC_OPTIONS has expected values", () => {
expect(Array.isArray(DEFAULT_SYNC_OPTIONS.excludes)).toBeTruthy();
expect(DEFAULT_SYNC_OPTIONS.excludes.length).toEqual(0);
expect(DEFAULT_SYNC_OPTIONS.defaultTs).toEqual('bun');
console.log('DEFAULT_SYNC_OPTIONS has expected values');
});
@@ -178,7 +174,7 @@ test.skipIf(shouldSkipOnCI())("Init: --use-default bypasses backend settings che
// Should have default settings, not backend settings
expect(wmillYaml).toContain("includes:\n - f/**");
expect(wmillYaml).toContain("defaultTs: bun");
expect(wmillYaml).not.toContain("defaultTs");
// Should NOT have backend-specific settings
expect(wmillYaml.includes("f/should-be-ignored/**")).toEqual(false);
-1
View File
@@ -90,7 +90,6 @@ describe("generateCommentedTemplate", () => {
test("default values match expected defaults", () => {
const config = parse(generateCommentedTemplate("main"));
expect(config.defaultTs).toBe("bun");
expect(config.skipSecrets).toBe(true);
expect(config.nonDottedPaths).toBe(true);
expect(config.codebases).toEqual([]);
@@ -0,0 +1,27 @@
/**
* Regression guard: a superadmin whose `usr` row in the workspace is not an
* admin still preserves ownership on the backend, so the CLI must not refuse
* their push as if it would reassign every item to them.
*/
import { expect, test } from "bun:test";
import { mockServices } from "./mock_services.ts";
mockServices({
whoami: async () => ({
email: "super@windmill.dev",
username: "super",
is_admin: false,
is_super_admin: true,
groups: [],
}),
});
const { buildPermissionedAsContext } = await import(
"../src/core/permissioned_as.ts"
);
test("a superadmin with a non-admin membership counts as able to preserve", async () => {
const ctx = await buildPermissionedAsContext("w", "v1");
expect(ctx?.userIsAdminOrDeployer).toBe(true);
});
+1 -1
View File
@@ -10,7 +10,7 @@
"bun",
"deno"
],
"description": "Default TypeScript runtime for new scripts"
"description": "TypeScript runtime of a plain .ts script (bun when absent)"
},
"includes": {
"type": "array",