Compare commits

..

8 Commits

Author SHA1 Message Date
Ruben Fiszel
e87a860626 chore(main): release 1.417.2 (#4633)
* chore(main): release 1.417.2

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-11-04 17:51:04 +01:00
Ruben Fiszel
00748f43fe fix(cli): improve pulling instance with folderPerInstance 2024-11-04 17:45:42 +01:00
Ruben Fiszel
021ed5a38b chore(main): release 1.417.1 (#4629)
* chore(main): release 1.417.1

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-11-04 17:17:22 +01:00
Wítallo
e92a338432 fix: missing opts for pull and push instance configs (#4630) 2024-11-04 17:12:51 +01:00
Ruben Fiszel
a1c0f676ca chore(main): release 1.417.0 (#4628)
* chore(main): release 1.0.0

* Apply automatic changes

* update

* Apply automatic changes

* Update CHANGELOG.md

---------

Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2024-11-03 03:09:26 +01:00
Henri Courdent
372f5f4335 Regex Postgres Connection String (#3746)
* Regex Postgres Connection String

* Fix regex

* Fix indentation

* Indentation

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2024-11-03 02:54:53 +01:00
Henri Courdent
ea40331ab7 Deploy UI warning update (#4601)
* Deploy UI warning update

* Harmonization icons

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2024-11-03 02:53:40 +01:00
Ruben Fiszel
57d59bc90a feat(cli): instance yaml files inside the instance prefix folders (#4627)
* feat(cli): instance yaml files inside the instance folders

* fix: add opt-in for save instance yamls inside prefixed folders

* chore: change baseUrl not needed to opts.baseUrl

* do not reassign global const path

* set prefix from instance

---------

Co-authored-by: Wítallo <15958770+witalloliveira@users.noreply.github.com>
2024-11-03 02:37:19 +01:00
33 changed files with 565 additions and 430 deletions

View File

@@ -1,5 +1,23 @@
# Changelog
## [1.417.2](https://github.com/windmill-labs/windmill/compare/v1.417.1...v1.417.2) (2024-11-04)
### Bug Fixes
* **cli:** improve pulling instance with folderPerInstance ([00748f4](https://github.com/windmill-labs/windmill/commit/00748f43feab4f28f0dd2682e3dc8ccc15a5f49f))
## [1.417.1](https://github.com/windmill-labs/windmill/compare/v1.417.0...v1.417.1) (2024-11-04)
### Bug Fixes
* missing opts for pull and push instance configs ([#4630](https://github.com/windmill-labs/windmill/issues/4630)) ([e92a338](https://github.com/windmill-labs/windmill/commit/e92a338432d2732e6be74c3604b9fa95239e1ca3))
## 1.417.0 (2024-11-03)
* feat(cli): instance yaml files inside the instance prefix folders (#4627)
## [1.416.2](https://github.com/windmill-labs/windmill/compare/v1.416.1...v1.416.2) (2024-11-02)

430
backend/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "windmill"
version = "1.416.2"
version = "1.417.2"
authors.workspace = true
edition.workspace = true
@@ -29,7 +29,7 @@ members = [
]
[workspace.package]
version = "1.416.2"
version = "1.417.2"
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
edition = "2021"

View File

@@ -1,11 +0,0 @@
# Api integration tests
You'll need deno to be installed and a windmill api endpoint.
By default the tests use the local environment endpoint <http://localhost:8000>. You can override it using `BASE_URL` environment variable.
By default the api authentication uses default local env user credentials. You can override api token using `WM_TOKEN` environment variable.
## Running the api tests
`deno test --allow-env --allow-net`

View File

@@ -1,9 +0,0 @@
{
"tasks": {
"dev": "deno run --watch main.ts",
"test": "deno test --allow-env --allow-net"
},
"imports": {
"@std/assert": "jsr:@std/assert@1"
}
}

View File

@@ -1,40 +0,0 @@
{
"version": "3",
"packages": {
"specifiers": {
"jsr:@std/assert": "jsr:@std/assert@1.0.6",
"jsr:@std/assert@1": "jsr:@std/assert@1.0.6",
"jsr:@std/internal@^1.0.4": "jsr:@std/internal@1.0.4"
},
"jsr": {
"@std/assert@1.0.6": {
"integrity": "1904c05806a25d94fe791d6d883b685c9e2dcd60e4f9fc30f4fc5cf010c72207",
"dependencies": [
"jsr:@std/internal@^1.0.4"
]
},
"@std/internal@1.0.4": {
"integrity": "62e8e4911527e5e4f307741a795c0b0a9e6958d0b3790716ae71ce085f755422"
}
}
},
"redirects": {
"https://cdn.skypack.dev/human-id@3.0.0": "https://cdn.skypack.dev/new/human-id@v3.0.0/dist=es2019",
"https://cdn.skypack.dev/new/human-id@v3.0.0/dist=es2019": "https://cdn.skypack.dev/error/build:human-id@v3.0.0-ktOXHm1YV4tQ5io95RTp",
"https://deno.land/x/nanoid/mod.ts": "https://deno.land/x/nanoid@v3.0.0/mod.ts"
},
"remote": {
"https://cdn.skypack.dev/error/build:human-id@v3.0.0-ktOXHm1YV4tQ5io95RTp": "54c50c8a33ab04f2a14c966809df583d6c59f0f8b549c270d1127afc8d4c652b",
"https://deno.land/x/nanoid@v3.0.0/customAlphabet.ts": "1cfd7cfd2f07ca8d78a7e7855fcc9f59abf01ef2a127484ef94328fadf940ead",
"https://deno.land/x/nanoid@v3.0.0/customRandom.ts": "af56e19038c891a4b4ef2be931554c27579bd407ee5bbea5cb64f6ee1347cbe3",
"https://deno.land/x/nanoid@v3.0.0/mod.ts": "3ead610e40c58d8fdca21d5da9ec661445a2b82526e19c34d05de5f90be8a1be",
"https://deno.land/x/nanoid@v3.0.0/nanoid.ts": "8d119bc89a0f34e7bbe0c2dbdc280d01753e431af553d189663492310a31085d",
"https://deno.land/x/nanoid@v3.0.0/random.ts": "4da71d5f72f2bfcc6a4ee79b5d4e72f48dcf4fe4c3835fd5ebab08b9f33cd598",
"https://deno.land/x/nanoid@v3.0.0/urlAlphabet.ts": "8b1511deb1ecb23c66202b6000dc10fb68f9a96b5550c6c8cef5009324793431"
},
"workspace": {
"dependencies": [
"jsr:@std/assert@1"
]
}
}

View File

@@ -1,80 +0,0 @@
import { assertEquals } from "jsr:@std/assert";
import type { FlowPreview } from "../../../cli/gen/index.ts";
import * as api from "../../../cli/gen/index.ts";
import { awaitJobCompletion, setup } from "./utils.ts";
const flowPreview: FlowPreview = {
"args": {
"a": 4,
"b": 9
},
"value": {
"modules": [
{
"id": "a",
"value": {
"type": "rawscript",
"content": "// import * as wmill from \"windmill-client\"\n\nexport async function main(a: number, b : number) {\n return a + b\n}\n",
"language": "bun",
"input_transforms": {
"a": {
"type": "javascript",
"expr": "flow_input.a"
},
"b": {
"type": "javascript",
"expr": "flow_input.b"
}
},
"tag": ""
}
},
{
"id": "b",
"value": {
"type": "rawscript",
"content": "// import * as wmill from \"windmill-client\"\n\nexport async function main(n: number) {\n return n / 2.0\n}\n",
"language": "bun",
"input_transforms": {
"n": {
"type": "javascript",
"expr": "results.a"
}
},
"tag": ""
}
}
]
},
"path": "u/admin/beauteous_flow"
};
Deno.test("Run a flow preview and check its completion", async () => {
const { workspace } = await setup()
const id = await api.runFlowPreview({
workspace,
requestBody: flowPreview
});
const result = await awaitJobCompletion({ id, workspace, timeoutMs: 2000 });
assertEquals(result.result, 6.5);
assertEquals(result.canceled, false);
assertEquals(result.success, true);
});
Deno.test("Run a flow preview - Immediately cancel and check cancellation status", async () => {
const { workspace } = await setup()
const id = await api.runFlowPreview({
workspace,
requestBody: flowPreview
});
await api.cancelQueuedJob({ id, workspace, requestBody: { reason: "test cancellation"}});
const result = await awaitJobCompletion({ id, workspace, timeoutMs: 200});
assertEquals(result.canceled, true);
assertEquals(result.success, false);
});

View File

@@ -1,51 +0,0 @@
import { OpenAPI, createWorkspace, getJob, login } from "../../../cli/gen/index.ts";
// Setup method for each api test
export async function setup() {
await generateApiToken()
const workspace = await genTestWorkspace();
return { workspace }
}
async function generateApiToken() {
// if WM_TOKEN env variable not set
if (!OpenAPI.TOKEN) {
const email = Deno.env.get("LOGIN_WM_EMAIL") || "admin@windmill.dev";
const password = Deno.env.get("LOGIN_WM_PASSWORD") || "changeme";
const token = await login({ requestBody: { email, password } });
OpenAPI.TOKEN = token;
}
}
/**
* Ensure a workspace named "api-tests" is created
*/
async function genTestWorkspace() {
const id = "api-tests"
await createWorkspace({ requestBody: { id, name: id}}).catch(()=> null)
return id
}
/**
*
* @returns The job result
*/
export async function awaitJobCompletion(data: { workspace: string, id: string, timeoutMs: number }) {
const start = Date.now();
while (true) {
const result = await getJob(data).catch(()=> null);
if (!!result && result.type === "CompletedJob") {
return result;
}
if ((Date.now() - start) > data.timeoutMs) {
throw new Error("Job did not finish before the specified timeout: " + data.timeoutMs)
}
}
}

View File

@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.416.2
version: 1.417.2
title: Windmill API
contact:

View File

@@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts";
import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts";
import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts";
export const VERSION = "v1.416.2";
export const VERSION = "v1.417.2";
export async function login(email: string, password: string): Promise<string> {
return await windmill.UserService.login({

View File

@@ -184,6 +184,7 @@ export type InstanceSyncOptions = {
folderPerInstance?: boolean;
yes?: boolean;
prefix?: string;
prefixSettings?: boolean;
};
export async function pickInstance(
@@ -260,30 +261,33 @@ export async function pickInstance(
return instance;
}
async function instancePull(opts: GlobalOptions & InstanceSyncOptions) {
async function instancePull(opts: InstanceSyncOptions) {
const instance = await pickInstance(opts, true);
log.info("Pulling instance-level changes");
log.info(`remote (${instance.name}) -> local`);
opts.prefix = opts.prefix ?? instance.prefix;
let uChanges = 0;
if (!opts.skipUsers) {
uChanges = (await pullInstanceUsers(true)) as number;
uChanges = (await pullInstanceUsers(opts, true)) as number;
}
let sChanges = 0;
if (!opts.skipSettings) {
sChanges = (await pullInstanceSettings(true)) as number;
sChanges = (await pullInstanceSettings(opts, true)) as number;
}
let cChanges = 0;
if (!opts.skipConfigs) {
cChanges = (await pullInstanceConfigs(true)) as number;
cChanges = (await pullInstanceConfigs(opts, true)) as number;
}
let gChanges = 0;
if (!opts.skipGroups) {
gChanges = (await pullInstanceGroups(true)) as number;
gChanges = (await pullInstanceGroups(opts, true)) as number;
}
const totalChanges = uChanges + sChanges + cChanges + gChanges;
const rootDir = Deno.cwd();
if (totalChanges > 0) {
let confirm = true;
if (opts.yes !== true) {
@@ -294,17 +298,24 @@ async function instancePull(opts: GlobalOptions & InstanceSyncOptions) {
}
if (confirm) {
if (uChanges > 0) {
if (opts.folderPerInstance && opts.prefixSettings) {
await Deno.mkdir(path.join(rootDir, opts.prefix), {
recursive: true,
});
}
}
if (!opts.skipUsers && uChanges > 0) {
await pullInstanceUsers();
await pullInstanceUsers(opts);
}
if (!opts.skipSettings && sChanges > 0) {
await pullInstanceSettings();
await pullInstanceSettings(opts);
}
if (!opts.skipConfigs && cChanges > 0) {
await pullInstanceConfigs();
await pullInstanceConfigs(opts);
}
if (!opts.skipGroups && gChanges > 0) {
await pullInstanceGroups();
await pullInstanceGroups(opts);
}
}
} else {
@@ -313,7 +324,6 @@ async function instancePull(opts: GlobalOptions & InstanceSyncOptions) {
if (opts.includeWorkspaces) {
log.info("\nPulling all workspaces");
const rootDir = Deno.cwd();
const localWorkspaces = await getLocalWorkspaces(
rootDir,
instance.prefix,
@@ -391,7 +401,7 @@ async function instancePull(opts: GlobalOptions & InstanceSyncOptions) {
}
}
async function instancePush(opts: GlobalOptions & InstanceSyncOptions) {
async function instancePush(opts: InstanceSyncOptions) {
let instances = await allInstances();
const instance = await pickInstance(opts, true);
@@ -400,19 +410,19 @@ async function instancePush(opts: GlobalOptions & InstanceSyncOptions) {
let uChanges = 0;
if (!opts.skipUsers) {
uChanges = (await pushInstanceUsers(true)) as number;
uChanges = (await pushInstanceUsers(opts, true)) as number;
}
let sChanges = 0;
if (!opts.skipSettings) {
sChanges = (await pushInstanceSettings(true, opts.baseUrl)) as number;
sChanges = (await pushInstanceSettings(opts, true)) as number;
}
let cChanges = 0;
if (!opts.skipConfigs) {
cChanges = (await pushInstanceConfigs(true)) as number;
cChanges = (await pushInstanceConfigs(opts, true)) as number;
}
let gChanges = 0;
if (!opts.skipGroups) {
gChanges = (await pushInstanceGroups(true)) as number;
gChanges = (await pushInstanceGroups(opts, true)) as number;
}
const totalChanges = uChanges + sChanges + cChanges + gChanges;
@@ -428,16 +438,16 @@ async function instancePush(opts: GlobalOptions & InstanceSyncOptions) {
if (confirm) {
if (!opts.skipUsers && uChanges > 0) {
await pushInstanceUsers();
await pushInstanceUsers(opts);
}
if (!opts.skipSettings && sChanges > 0) {
await pushInstanceSettings(false, opts.baseUrl);
await pushInstanceSettings(opts);
}
if (!opts.skipConfigs && cChanges > 0) {
await pushInstanceConfigs();
await pushInstanceConfigs(opts);
}
if (!opts.skipGroups && gChanges > 0) {
await pushInstanceGroups();
await pushInstanceGroups(opts);
}
}
} else {
@@ -709,7 +719,10 @@ const command = new Command()
"--prefix <prefix:string>",
"Prefix of the local workspaces to pull, used to create the folders when using --include-workspaces"
)
.option(
"--prefix-settings",
"Store instance yamls inside prefixed folders when using --prefix and --folder-per-instance"
)
.action(instancePull as any)
.command("push")
.description(
@@ -730,6 +743,10 @@ const command = new Command()
"--prefix <prefix:string>",
"Prefix of the local workspaces folders to push"
)
.option(
"--prefix-settings",
"Store instance yamls inside prefixed folders when using --prefix and --folder-per-instance"
)
.action(instancePush as any)
.command("whoami")
.description("Display information about the currently logged-in user")

View File

@@ -60,7 +60,7 @@ export {
// }
// });
export const VERSION = "1.416.2";
export const VERSION = "1.417.2";
const command = new Command()
.name("wmill")

View File

@@ -3,8 +3,8 @@ import { Confirm } from "./deps.ts";
import { colors } from "./deps.ts";
import { yamlParseFile } from "./deps.ts";
import { log } from "./deps.ts";
import { compareInstanceObjects } from "./instance.ts";
import { isSuperset } from "./types.ts";
import { compareInstanceObjects, InstanceSyncOptions } from "./instance.ts";
import { isSuperset, GlobalOptions } from "./types.ts";
import { deepEqual } from "./utils.ts";
import * as wmill from "./gen/services.gen.ts";
import { Config, GlobalSetting } from "./gen/types.gen.ts";
@@ -33,6 +33,22 @@ export interface SimplifiedSettings {
name: string;
}
const INSTANCE_SETTINGS_PATH = "instance_settings.yaml";
let instanceSettingsPath = INSTANCE_SETTINGS_PATH;
async function checkInstanceSettingsPath(opts: InstanceSyncOptions) {
if (opts.prefix && opts.folderPerInstance && opts.prefixSettings) {
instanceSettingsPath = `${opts.prefix}/${INSTANCE_SETTINGS_PATH}`;
}
}
const INSTANCE_CONFIGS_PATH = "instance_configs.yaml";
let instanceConfigsPath = INSTANCE_CONFIGS_PATH;
async function checkInstanceConfigPath(opts: InstanceSyncOptions) {
if (opts.prefix && opts.folderPerInstance && opts.prefixSettings) {
instanceConfigsPath = `${opts.prefix}/${INSTANCE_CONFIGS_PATH}`;
}
}
export async function pushWorkspaceSettings(
workspace: string,
_path: string,
@@ -113,10 +129,10 @@ export async function pushWorkspaceSettings(
workspace,
requestBody: localSettings.auto_invite_enabled
? {
operator: localSettings.auto_invite_as === "operator",
invite_all: true,
auto_add: localSettings.auto_invite_mode === "add",
}
operator: localSettings.auto_invite_as === "operator",
invite_all: true,
auto_add: localSettings.auto_invite_mode === "add",
}
: {},
});
} catch (_) {
@@ -128,10 +144,10 @@ export async function pushWorkspaceSettings(
workspace,
requestBody: localSettings.auto_invite_enabled
? {
operator: localSettings.auto_invite_as === "operator",
invite_all: false,
auto_add: localSettings.auto_invite_mode === "add",
}
operator: localSettings.auto_invite_as === "operator",
invite_all: false,
auto_add: localSettings.auto_invite_mode === "add",
}
: {},
});
}
@@ -156,7 +172,7 @@ export async function pushWorkspaceSettings(
settings.error_handler_extra_args
) ||
localSettings.error_handler_muted_on_cancel !==
settings.error_handler_muted_on_cancel
settings.error_handler_muted_on_cancel
) {
log.debug(`Updating error handler...`);
await wmill.editErrorHandler({
@@ -260,32 +276,36 @@ export async function pushWorkspaceKey(
}
}
const INSTANCE_SETTINGS_PATH = "instance_settings.yaml";
export async function readInstanceSettings() {
export async function readInstanceSettings(opts: InstanceSyncOptions) {
let localSettings: GlobalSetting[] = [];
await checkInstanceSettingsPath(opts);
try {
localSettings = (await yamlParseFile(INSTANCE_SETTINGS_PATH)) as GlobalSetting[];
localSettings = (await yamlParseFile(instanceSettingsPath)) as GlobalSetting[];
} catch {
log.warn(`No ${INSTANCE_SETTINGS_PATH} found`);
log.warn(`No ${instanceSettingsPath} found`);
}
return localSettings;
}
import { decrypt, encrypt } from "./local_encryption.ts";
const SENSITIVE_FIELD: string[] = ["license_key", "jwt_secret"]
const SENSITIVE_FIELD: string[] = ["license_key", "jwt_secret"];
async function processInstanceSettings(settings: GlobalSetting[], mode: "encode" | "decode"): Promise<GlobalSetting[]> {
async function processInstanceSettings(
settings: GlobalSetting[],
mode: "encode" | "decode"
): Promise<GlobalSetting[]> {
const encKey = process.env.WMILL_INSTANCE_LOCAL_ENCRYPTION_KEY;
if (encKey) {
const res: GlobalSetting[] = []
const res: GlobalSetting[] = [];
for (const s of settings) {
if (SENSITIVE_FIELD.includes(s.name) && typeof s.value === "string") {
res.push(await processField(s, "value", encKey, mode) as GlobalSetting);
res.push(
(await processField(s, "value", encKey, mode)) as GlobalSetting
);
} else if (s.name == "oauths") {
if (typeof s.value === "object") {
const oauths = s.value as { [key: string]: any };
@@ -303,24 +323,42 @@ async function processInstanceSettings(settings: GlobalSetting[], mode: "encode"
}
return res;
} else {
log.warn("No encryption key found, skipping encryption. Recommend setting WMILL_INSTANCE_LOCAL_ENCRYPTION_KEY");
log.warn(
"No encryption key found, skipping encryption. Recommend setting WMILL_INSTANCE_LOCAL_ENCRYPTION_KEY"
);
}
return settings;
}
async function processField(obj: { [key: string]: any }, field: string, encKey: string, mode: "encode" | "decode"): Promise<{ [key: string]: any }> {
async function processField(
obj: { [key: string]: any },
field: string,
encKey: string,
mode: "encode" | "decode"
): Promise<{ [key: string]: any }> {
return {
...obj,
[field]: mode === "encode" ? await encrypt(obj[field], encKey) : await decrypt(obj[field], encKey) as any,
}
[field]:
mode === "encode"
? await encrypt(obj[field], encKey)
: ((await decrypt(obj[field], encKey)) as any),
};
}
export async function pullInstanceSettings(preview = false) {
export async function pullInstanceSettings(
opts: InstanceSyncOptions,
preview = false
) {
const remoteSettings = await wmill.listGlobalSettings();
await checkInstanceSettingsPath(opts);
if (preview) {
const localSettings: GlobalSetting[] = await readInstanceSettings();
const processedSettings = await processInstanceSettings(remoteSettings, "encode");
const localSettings: GlobalSetting[] = await readInstanceSettings(opts);
const processedSettings = await processInstanceSettings(
remoteSettings,
"encode"
);
return compareInstanceObjects(
processedSettings,
localSettings,
@@ -330,30 +368,33 @@ export async function pullInstanceSettings(preview = false) {
} else {
log.info("Pulling settings from instance");
const processedSettings = await processInstanceSettings(remoteSettings, "encode");
const processedSettings = await processInstanceSettings(
remoteSettings,
"encode"
);
await Deno.writeTextFile(
INSTANCE_SETTINGS_PATH,
instanceSettingsPath,
yamlStringify(processedSettings)
);
log.info(colors.green(`Settings written to ${INSTANCE_SETTINGS_PATH}`));
log.info(colors.green(`Settings written to ${instanceSettingsPath}`));
}
}
export async function pushInstanceSettings(
preview: boolean = false,
baseUrl?: string
opts: InstanceSyncOptions,
preview: boolean = false
) {
const remoteSettings = await wmill.listGlobalSettings();
let localSettings: GlobalSetting[] = await readInstanceSettings();
let localSettings: GlobalSetting[] = await readInstanceSettings(opts);
localSettings = await processInstanceSettings(localSettings, "decode");
if (baseUrl) {
if (opts.baseUrl) {
localSettings = localSettings.filter((s) => s.name !== "base_url");
localSettings.push({
name: "base_url",
//@ts-ignore
value: baseUrl,
value: opts.baseUrl,
});
}
@@ -404,18 +445,25 @@ export async function pushInstanceSettings(
}
}
export async function readLocalConfigs() {
export async function readLocalConfigs(
opts: InstanceSyncOptions
) {
let localConfigs: Config[] = [];
await checkInstanceConfigPath(opts);
try {
localConfigs = (await yamlParseFile("instance_configs.yaml")) as Config[];
localConfigs = (await yamlParseFile(instanceConfigsPath)) as Config[];
} catch {
log.warn("No instance_configs.yaml found");
log.warn(`No ${instanceConfigsPath} found`);
}
return localConfigs;
}
export async function pullInstanceConfigs(preview = false) {
export async function pullInstanceConfigs(
opts: InstanceSyncOptions,
preview = false
) {
const remoteConfigs = (await wmill.listConfigs()).map((x) => {
return {
...x,
@@ -424,7 +472,7 @@ export async function pullInstanceConfigs(preview = false) {
});
if (preview) {
const localConfigs: Config[] = await readLocalConfigs();
const localConfigs: Config[] = await readLocalConfigs(opts);
return compareInstanceObjects(
remoteConfigs,
@@ -436,22 +484,25 @@ export async function pullInstanceConfigs(preview = false) {
log.info("Pulling configs from instance");
await Deno.writeTextFile(
"instance_configs.yaml",
instanceConfigsPath,
yamlStringify(remoteConfigs as any)
);
log.info(colors.green("Configs written to instance_configs.yaml"));
log.info(colors.green(`Configs written to ${instanceConfigsPath}`));
}
}
export async function pushInstanceConfigs(preview: boolean = false) {
export async function pushInstanceConfigs(
opts: InstanceSyncOptions,
preview: boolean = false
) {
const remoteConfigs = (await wmill.listConfigs()).map((x) => {
return {
...x,
name: removeWorkerPrefix(x.name),
};
});
const localConfigs = await readLocalConfigs();
const localConfigs = await readLocalConfigs(opts);
if (preview) {
return compareInstanceObjects(

View File

@@ -6,7 +6,7 @@ import {
removeType,
removePathPrefix,
} from "./types.ts";
import { compareInstanceObjects } from "./instance.ts";
import { compareInstanceObjects, InstanceSyncOptions } from "./instance.ts";
import {
colors,
Command,
@@ -23,6 +23,22 @@ import {
InstanceGroup,
} from "./gen/types.gen.ts";
const INSTANCE_USERS_PATH = "instance_users.yaml";
let instanceUsersPath = INSTANCE_USERS_PATH;
function checkInstanceUsersPath(opts: InstanceSyncOptions) {
if (opts.prefix && opts.folderPerInstance && opts.prefixSettings) {
instanceUsersPath = `${opts.prefix}/${INSTANCE_USERS_PATH}`;
}
}
const INSTANCE_GROUPS_PATH = "instance_groups.yaml";
let instanceGroupsPath = INSTANCE_GROUPS_PATH;
function checkInstanceGroupsPath(opts: InstanceSyncOptions) {
if (opts.prefix && opts.folderPerInstance && opts.prefixSettings) {
instanceGroupsPath = `${opts.prefix}/${INSTANCE_GROUPS_PATH}`;
}
}
async function list(opts: GlobalOptions) {
await requireLogin(opts);
@@ -386,47 +402,61 @@ export async function pushGroup(
}
}
export async function pullInstanceUsers(preview: boolean = false) {
export async function pullInstanceUsers(
opts: InstanceSyncOptions,
preview: boolean = false
) {
const remoteUsers = await wmill.globalUsersExport();
checkInstanceUsersPath(opts);
if (preview) {
const localUsers: ExportedUser[] = await readInstanceUsers();
const localUsers: ExportedUser[] = await readInstanceUsers(opts);
return compareInstanceObjects(remoteUsers, localUsers, "email", "user");
} else {
log.info("Pulling users from instance...");
await Deno.writeTextFile(
"instance_users.yaml",
instanceUsersPath,
yamlStringify(remoteUsers as any)
);
log.info(colors.green("Users written to instance_users.yaml"));
log.info(colors.green(`Users written to ${instanceUsersPath}`));
}
}
export async function readInstanceUsers() {
export async function readInstanceUsers(opts: InstanceSyncOptions) {
let localUsers: ExportedUser[] = [];
await checkInstanceUsersPath(opts);
try {
localUsers = (await yamlParseFile("instance_users.yaml")) as ExportedUser[];
localUsers = (await yamlParseFile(instanceUsersPath)) as ExportedUser[];
} catch {
log.warn("No instance_users.yaml file found");
log.warn(`No ${instanceUsersPath} file found`);
}
return localUsers;
}
export async function readInstanceGroups() {
export async function readInstanceGroups(opts: InstanceSyncOptions) {
let localGroups: InstanceGroup[] = [];
checkInstanceGroupsPath(opts);
try {
localGroups = (await yamlParseFile(
"instance_groups.yaml"
instanceGroupsPath
)) as ExportedInstanceGroup[];
} catch {
log.warn("No instance_groups.yaml file found");
log.warn(`No ${instanceGroupsPath} file found`);
}
return localGroups;
}
export async function pushInstanceUsers(preview: boolean = false) {
export async function pushInstanceUsers(
opts: InstanceSyncOptions,
preview: boolean = false
) {
const remoteUsers = await wmill.globalUsersExport();
const localUsers: ExportedUser[] = await readInstanceUsers();
const localUsers: ExportedUser[] = await readInstanceUsers(opts);
if (preview) {
return compareInstanceObjects(localUsers, remoteUsers, "email", "user");
@@ -440,27 +470,35 @@ export async function pushInstanceUsers(preview: boolean = false) {
}
}
export async function pullInstanceGroups(preview = false) {
export async function pullInstanceGroups(
opts: InstanceSyncOptions,
preview = false
) {
const remoteGroups = await wmill.exportInstanceGroups();
checkInstanceGroupsPath(opts);
if (preview) {
const localGroups = await readInstanceGroups();
const localGroups = await readInstanceGroups(opts);
return compareInstanceObjects(remoteGroups, localGroups, "name", "group");
} else {
log.info("Pulling groups from instance...");
await Deno.writeTextFile(
"instance_groups.yaml",
instanceGroupsPath,
yamlStringify(remoteGroups as any)
);
log.info(colors.green("Groups written to instance_groups.yaml"));
log.info(colors.green(`Groups written to ${instanceGroupsPath}`));
}
}
export async function pushInstanceGroups(preview: boolean = false) {
export async function pushInstanceGroups(
opts: InstanceSyncOptions,
preview: boolean = false
) {
const remoteGroups = await wmill.exportInstanceGroups();
const localGroups = await readInstanceGroups();
const localGroups = await readInstanceGroups(opts);
if (preview) {
return compareInstanceObjects(localGroups, remoteGroups, "name", "group");

View File

@@ -56,7 +56,7 @@ export async function displayWorkerGroups(opts: void) {
async function pullWorkerGroups(opts: InstanceSyncOptions) {
await pickInstance(opts, true);
const totalChanges = await pullInstanceConfigs(true) ?? 0;
const totalChanges = await pullInstanceConfigs(opts, true) ?? 0;
if (totalChanges === 0) {
log.info("No changes to apply");
@@ -72,14 +72,14 @@ async function pullWorkerGroups(opts: InstanceSyncOptions) {
}
if (confirm) {
await pullInstanceConfigs(false);
await pullInstanceConfigs(opts, false);
}
}
async function pushWorkerGroups(opts: InstanceSyncOptions) {
await pickInstance(opts, true);
const totalChanges = await pushInstanceConfigs(true) ?? 0;
const totalChanges = await pushInstanceConfigs(opts, true) ?? 0;
if (totalChanges === 0) {
log.info("No changes to apply");
@@ -95,7 +95,7 @@ async function pushWorkerGroups(opts: InstanceSyncOptions) {
}
if (confirm) {
await pushInstanceConfigs(false);
await pushInstanceConfigs(opts, false);
}
}

View File

@@ -1,12 +1,12 @@
{
"name": "windmill-components",
"version": "1.416.2",
"version": "1.417.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "windmill-components",
"version": "1.416.2",
"version": "1.417.2",
"license": "AGPL-3.0",
"dependencies": {
"@aws-crypto/sha256-js": "^4.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "windmill-components",
"version": "1.416.2",
"version": "1.417.2",
"scripts": {
"dev": "vite dev",
"build": "vite build",

View File

@@ -80,9 +80,8 @@
let connectionString = ''
let validConnectionString = true
function parseConnectionString(close: (_: any) => void) {
// parse postgres connection string
const regex =
/postgres:\/\/(?<user>[^:@]+)(?::(?<password>[^@]+))?@(?<host>[^:\/?]+)(?::(?<port>\d+))?\/(?<dbname>[^\?]+)?(?:\?.*sslmode=(?<sslmode>[^&]+))?/
/postgres(?:ql)?:\/\/(?<user>[^:@]+)(?::(?<password>[^@]+))?@(?<host>[^:\/?]+)(?::(?<port>\d+))?\/(?<dbname>[^\?]+)?(?:\?.*sslmode=(?<sslmode>[^&]+))?/
const match = connectionString.match(regex)
if (match) {
validConnectionString = true

View File

@@ -533,7 +533,7 @@
>
{:else if notSet == true}
<Alert type="error" title="Staging/Prod deploy not set up"
>As an admin, go to "Workspace {'->'} Dev/Staging/Prod"</Alert
>As an admin, go to Settings {'->'} Workspace {'->'} Deployment UI</Alert
>
{:else}
<Alert type="info" title="Shareable page"

View File

@@ -16,9 +16,9 @@
Eye,
File,
FileJson,
FileUp,
FolderOpen,
GitFork,
Globe,
ChevronUpSquare,
History,
Pen,
Share,
@@ -162,7 +162,7 @@
},
{
displayName: 'Move/Rename',
icon: FileUp,
icon: FolderOpen,
action: () => {
moveDrawer.openDrawer(path, summary, 'app')
},
@@ -173,7 +173,7 @@
? [
{
displayName: 'Deploy to staging/prod',
icon: Globe,
icon: ChevronUpSquare,
action: () => {
deploymentDrawer.openDrawer(path, 'app')
},

View File

@@ -23,8 +23,8 @@
GitFork,
Trash,
List,
FileUp,
Globe,
FolderOpen,
ChevronUpSquare,
Calendar,
Share,
Archive,
@@ -179,7 +179,7 @@
},
{
displayName: 'Move/Rename',
icon: FileUp,
icon: FolderOpen,
action: () => {
moveDrawer.openDrawer(path, flow.summary, 'flow')
},
@@ -197,7 +197,7 @@
? [
{
displayName: 'Deploy to staging/prod',
icon: Globe,
icon: ChevronUpSquare,
action: () => {
deploymentDrawer.openDrawer(path, 'flow')
},

View File

@@ -14,7 +14,7 @@
import FileInput from '../fileInput/FileInput.svelte'
import { goto } from '$lib/navigation'
import type DeployWorkspaceDrawer from '$lib/components/DeployWorkspaceDrawer.svelte'
import { FileUp, Globe, Pen, Share, Trash } from 'lucide-svelte'
import { FolderOpen, Globe, Pen, Share, Trash } from 'lucide-svelte'
import { isDeployable } from '$lib/utils_deployable'
import { getDeployUiSettings } from '$lib/components/home/deploy_ui'
@@ -94,7 +94,7 @@
return [
{
displayName: 'Move/Rename',
icon: FileUp,
icon: FolderOpen,
action: () => {
moveDrawer.openDrawer(path, summary, 'raw_app')
},

View File

@@ -27,7 +27,8 @@
Code,
Copy,
Eye,
FileUp,
FolderOpen,
ChevronUpSquare,
GitFork,
List,
Pen,
@@ -207,7 +208,7 @@
},
{
displayName: 'Move/Rename',
icon: FileUp,
icon: FolderOpen,
action: () => {
moveDrawer.openDrawer(script.path, script.summary, 'script')
},
@@ -218,7 +219,7 @@
? [
{
displayName: 'Deploy to staging/prod',
icon: FileUp,
icon: ChevronUpSquare,
action: () => {
deploymentDrawer.openDrawer(script.path, 'script')
},

View File

@@ -27,7 +27,7 @@
FolderOpen,
Archive,
Trash,
Server,
ChevronUpSquare,
Share,
Badge,
Loader2,
@@ -294,7 +294,7 @@
menuItems.push({
label: 'Deploy to staging/prod',
onclick: () => deploymentDrawer.openDrawer(flow?.path ?? '', 'flow'),
Icon: Server
Icon: ChevronUpSquare
})
}

View File

@@ -57,7 +57,7 @@
History,
Loader2,
Pen,
Server,
ChevronUpSquare,
Share,
Table2,
Trash,
@@ -407,7 +407,7 @@
if (isDeployable('script', script?.path ?? '', deployUiSettings)) {
menuItems.push({
label: 'Deploy to staging/prod',
Icon: Server,
Icon: ChevronUpSquare,
onclick: () => {
deploymentDrawer.openDrawer(script?.path ?? '', 'script')
}

View File

@@ -4,8 +4,8 @@ verify_ssl = true
name = "pypi"
[packages]
wmill = ">=1.416.2"
wmill_pg = ">=1.416.2"
wmill = ">=1.417.2"
wmill_pg = ">=1.417.2"
sendgrid = "*"
mysql-connector-python = "*"
pymongo = "*"

View File

@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.416.2
version: 1.417.2
title: OpenFlow Spec
contact:
name: Ruben Fiszel

View File

@@ -12,7 +12,7 @@
RootModule = 'WindmillClient.psm1'
# Version number of this module.
ModuleVersion = '1.416.2'
ModuleVersion = '1.417.2'
# Supported PSEditions
# CompatiblePSEditions = @()

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill"
version = "1.416.2"
version = "1.417.2"
description = "A client library for accessing Windmill server wrapping the Windmill client API"
license = "Apache-2.0"
homepage = "https://windmill.dev"

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill-pg"
version = "1.416.2"
version = "1.417.2"
description = "An extension client for the wmill client library focused on pg"
license = "Apache-2.0"
homepage = "https://windmill.dev"

View File

@@ -1,6 +1,6 @@
{
"name": "@windmill/windmill",
"version": "1.416.2",
"version": "1.417.2",
"exports": "./src/index.ts",
"publish": {
"exclude": ["!src", "./s3Types.ts", "./client.ts"]

View File

@@ -1,7 +1,7 @@
{
"name": "windmill-client",
"description": "Windmill SDK client for browsers and Node.js",
"version": "1.416.2",
"version": "1.417.2",
"author": "Ruben Fiszel",
"license": "Apache 2.0",
"devDependencies": {

View File

@@ -1 +1 @@
1.416.2
1.417.2