@zitadel/cli 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +185 -72
- package/dist/index.mjs +888 -80
- package/dist/index.mjs.map +1 -1
- package/dist/lib/oclif/help.mjs +1 -1
- package/dist/{skills-hint-CBoak9HL.mjs → skills-hint-N1H5-ny1.mjs} +129 -33
- package/dist/{skills-hint-CBoak9HL.mjs.map → skills-hint-N1H5-ny1.mjs.map} +1 -1
- package/oclif.manifest.json +185 -67
- package/package.json +7 -6
- package/skills/zitadel-cli/SKILL.md +49 -5
- package/skills/zitadel-cli/references/driving-login-ui.md +16 -1
package/dist/lib/oclif/help.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { M as COMMAND_GROUPS, N as CommandGroups, P as compareInGroup, i as exploreAllCommands, t as showSkillsHint } from "../../skills-hint-N1H5-ny1.mjs";
|
|
2
2
|
import { Help, toConfiguredId, ux } from "@oclif/core";
|
|
3
3
|
//#region src/lib/oclif/help.ts
|
|
4
4
|
/**
|
|
@@ -13,7 +13,7 @@ import { promisify } from "node:util";
|
|
|
13
13
|
import { BlockList, isIP } from "node:net";
|
|
14
14
|
import pc from "picocolors";
|
|
15
15
|
import { Agent, getGlobalDispatcher, setGlobalDispatcher } from "undici";
|
|
16
|
-
import { brandingConfigSchema, flowConfigSchema, idpConnectionConfigSchema, policyConfigSchema, schemaConfigSchema } from "@zitadel/config/schemas";
|
|
16
|
+
import { RECOMMENDED_PASSWORD_MIN_LENGTH, brandingConfigSchema, flowConfigSchema, idpConnectionConfigSchema, policyConfigSchema, schemaConfigSchema } from "@zitadel/config/schemas";
|
|
17
17
|
import { PURPOSE_FLIP_TARGETS, validateFlowDefinition, validateProject } from "@zitadel/config/validate";
|
|
18
18
|
import { CUSTOM_PROVIDER, IDP_PROVIDERS, RESERVED_SLUGS, credentialVariables, idpProvider, isLoopbackIssuer, isVariableReference, missingEndpoints, parseConnectionSlug, parseDisplayName, parseIssuer, parseProviderSelection, parseScopes, referencedVariable, selectionName } from "@zitadel/config/idp";
|
|
19
19
|
import { SETUP_PRESETS, getDefaultLoginFlow } from "@zitadel/config/defaults";
|
|
@@ -3232,9 +3232,11 @@ async function readJsonDir(absDir) {
|
|
|
3232
3232
|
}
|
|
3233
3233
|
/**
|
|
3234
3234
|
* {@link readJsonDir}, keeping each body's file name — for callers that report
|
|
3235
|
-
* a problem against the file it came from.
|
|
3235
|
+
* a problem against the file it came from. `skip` leaves out files by name
|
|
3236
|
+
* before they are read, so a file the caller does not own (a policy test file
|
|
3237
|
+
* beside the policies) can never fail it.
|
|
3236
3238
|
*/
|
|
3237
|
-
async function readJsonDirEntries(absDir) {
|
|
3239
|
+
async function readJsonDirEntries(absDir, { skip } = {}) {
|
|
3238
3240
|
let entries;
|
|
3239
3241
|
try {
|
|
3240
3242
|
entries = await readdir(absDir);
|
|
@@ -3243,7 +3245,7 @@ async function readJsonDirEntries(absDir) {
|
|
|
3243
3245
|
throw error;
|
|
3244
3246
|
}
|
|
3245
3247
|
const result = [];
|
|
3246
|
-
for (const entry of entries.filter((name) => name.endsWith(".json")).sort()) {
|
|
3248
|
+
for (const entry of entries.filter((name) => name.endsWith(".json") && !skip?.(name)).sort()) {
|
|
3247
3249
|
const abs = join(absDir, entry);
|
|
3248
3250
|
let raw;
|
|
3249
3251
|
try {
|
|
@@ -3288,6 +3290,15 @@ function toPolicyWireBody(file) {
|
|
|
3288
3290
|
config: file.config
|
|
3289
3291
|
};
|
|
3290
3292
|
}
|
|
3293
|
+
/**
|
|
3294
|
+
* The suffix of a policy test file (`<operation>.test.json`), which sits
|
|
3295
|
+
* beside the policies but is never deployed: `zitadel policies test` runs it.
|
|
3296
|
+
*/
|
|
3297
|
+
const POLICY_TEST_SUFFIX = ".test.json";
|
|
3298
|
+
/** Whether a file in `.zitadel/policies/` is a test file rather than a policy. */
|
|
3299
|
+
function isPolicyTestFile(name) {
|
|
3300
|
+
return name.endsWith(POLICY_TEST_SUFFIX);
|
|
3301
|
+
}
|
|
3291
3302
|
/** The name a policy file must have: its operation, so one file holds one operation's policy. */
|
|
3292
3303
|
function policyFileName(operation) {
|
|
3293
3304
|
return `${operation}.json`;
|
|
@@ -3317,7 +3328,7 @@ const PASSWORD_POLICY_PATH = `${POLICIES_DIR}/${policyFileName(PASSWORD_POLICY_O
|
|
|
3317
3328
|
* empty `config`, which enforces nothing, for an explicit "none".
|
|
3318
3329
|
*/
|
|
3319
3330
|
function passwordPolicyFile(minLength) {
|
|
3320
|
-
const value = minLength === void 0 ?
|
|
3331
|
+
const value = minLength === void 0 ? RECOMMENDED_PASSWORD_MIN_LENGTH : minLength;
|
|
3321
3332
|
return {
|
|
3322
3333
|
$schema: POLICY_FILE_SCHEMA_REF,
|
|
3323
3334
|
operation: PASSWORD_POLICY_OPERATION,
|
|
@@ -3367,7 +3378,7 @@ async function buildBundle(cwd) {
|
|
|
3367
3378
|
const flow_definitions = (await readJsonDir(join(cwd, FLOWS_DIR))).map(stripSchema);
|
|
3368
3379
|
const idpEntries = await readJsonDirEntries(join(cwd, IDPS_DIR));
|
|
3369
3380
|
const idps = idpEntries.map((entry) => stripSchema(entry.body));
|
|
3370
|
-
const policyEntries = await readJsonDirEntries(join(cwd, POLICIES_DIR));
|
|
3381
|
+
const policyEntries = await readJsonDirEntries(join(cwd, POLICIES_DIR), { skip: isPolicyTestFile });
|
|
3371
3382
|
const policies = policyEntries.map((entry) => stripSchema(entry.body));
|
|
3372
3383
|
const brandingSources = (await readJsonDir(join(cwd, BRANDING_DIR))).map((descriptor) => ({ ...descriptor }));
|
|
3373
3384
|
if (brandingSources.length > 1) throw new ZitadelError("E_VALIDATION", `A release carries at most one branding, but .zitadel/branding/ holds ${brandingSources.length}`, { hint: "A project has a single branding; keep one descriptor under .zitadel/branding/." });
|
|
@@ -3496,12 +3507,87 @@ async function worktreeIsDirty(cwd) {
|
|
|
3496
3507
|
return stdout !== null && stdout.trim() !== "";
|
|
3497
3508
|
}
|
|
3498
3509
|
/**
|
|
3510
|
+
* The `git diff` options the terminal's diff and the `--json` one share, so
|
|
3511
|
+
* the two never disagree: git's own unified diff, never an external diff
|
|
3512
|
+
* program's, with paths relative to the project, as the CLI reports them
|
|
3513
|
+
* everywhere else.
|
|
3514
|
+
*/
|
|
3515
|
+
const DIFF_OPTIONS = ["--no-ext-diff", "--relative"];
|
|
3516
|
+
/**
|
|
3499
3517
|
* The coloured diff of `.zitadel/` against `sha`, or null when no diff can be
|
|
3500
3518
|
* shown: git is missing, this is not a repo, or the commit is not in local
|
|
3501
3519
|
* history (a shallow clone, say). A dead commit is told apart from a clean tree
|
|
3502
3520
|
* by first checking the commit resolves.
|
|
3503
3521
|
*/
|
|
3504
3522
|
async function diffConfigAgainst(cwd, sha, color, referenced = []) {
|
|
3523
|
+
return diffConfig(cwd, sha, [color ? "--color=always" : "--no-color"], referenced);
|
|
3524
|
+
}
|
|
3525
|
+
/**
|
|
3526
|
+
* {@link diffConfigAgainst}, uncoloured and split by file: joined back
|
|
3527
|
+
* together, the patches are the diff the terminal shows. Null when no diff can
|
|
3528
|
+
* be shown, for the same reasons.
|
|
3529
|
+
*/
|
|
3530
|
+
async function diffConfigFilesAgainst(cwd, sha, referenced = []) {
|
|
3531
|
+
const output = await diffConfig(cwd, sha, [
|
|
3532
|
+
"--no-color",
|
|
3533
|
+
"--raw",
|
|
3534
|
+
"-z",
|
|
3535
|
+
"--patch"
|
|
3536
|
+
], referenced);
|
|
3537
|
+
return output === null ? null : splitByFile(output);
|
|
3538
|
+
}
|
|
3539
|
+
/**
|
|
3540
|
+
* Pair the `--raw -z` records with the patch's per-file sections. git writes
|
|
3541
|
+
* both from one list of files, in one order, so they line up one to one, except
|
|
3542
|
+
* for a file whose type changed (a file replaced by a symlink): git shows it as
|
|
3543
|
+
* a deletion and a creation, two sections under one header, joined here.
|
|
3544
|
+
*/
|
|
3545
|
+
function splitByFile(output) {
|
|
3546
|
+
let at = 0;
|
|
3547
|
+
const field = () => {
|
|
3548
|
+
const end = output.indexOf("\0", at);
|
|
3549
|
+
const value = output.slice(at, end === -1 ? output.length : end);
|
|
3550
|
+
at = end === -1 ? output.length : end + 1;
|
|
3551
|
+
return value;
|
|
3552
|
+
};
|
|
3553
|
+
const records = [];
|
|
3554
|
+
while (output.startsWith(":", at)) {
|
|
3555
|
+
const meta = field();
|
|
3556
|
+
const letter = meta.charAt(meta.lastIndexOf(" ") + 1);
|
|
3557
|
+
const path = field();
|
|
3558
|
+
if (letter === "R" || letter === "C") {
|
|
3559
|
+
const status = letter === "R" ? "renamed" : "copied";
|
|
3560
|
+
records.push({
|
|
3561
|
+
path: field(),
|
|
3562
|
+
status,
|
|
3563
|
+
previous_path: path
|
|
3564
|
+
});
|
|
3565
|
+
} else {
|
|
3566
|
+
const status = letter === "A" ? "added" : letter === "D" ? "deleted" : "modified";
|
|
3567
|
+
records.push({
|
|
3568
|
+
path,
|
|
3569
|
+
status
|
|
3570
|
+
});
|
|
3571
|
+
}
|
|
3572
|
+
}
|
|
3573
|
+
const sections = output.slice(at + 1).split(/^(?=diff --git )/m);
|
|
3574
|
+
let next = 0;
|
|
3575
|
+
return records.map((record) => {
|
|
3576
|
+
let patch = sections[next++] ?? "";
|
|
3577
|
+
while (next < sections.length && headerOf(sections[next]) === headerOf(patch)) patch += sections[next++];
|
|
3578
|
+
return {
|
|
3579
|
+
...record,
|
|
3580
|
+
patch
|
|
3581
|
+
};
|
|
3582
|
+
});
|
|
3583
|
+
}
|
|
3584
|
+
/** A patch section's first line, its `diff --git` header. */
|
|
3585
|
+
function headerOf(section = "") {
|
|
3586
|
+
const end = section.indexOf("\n");
|
|
3587
|
+
return end === -1 ? section : section.slice(0, end);
|
|
3588
|
+
}
|
|
3589
|
+
/** `git diff` of the release's paths against `sha`, in `format`. */
|
|
3590
|
+
async function diffConfig(cwd, sha, format, referenced) {
|
|
3505
3591
|
const paths = [...CONFIG_PATHS, ...referenced];
|
|
3506
3592
|
if (await git(cwd, [
|
|
3507
3593
|
"cat-file",
|
|
@@ -3510,7 +3596,8 @@ async function diffConfigAgainst(cwd, sha, color, referenced = []) {
|
|
|
3510
3596
|
]) === null) return null;
|
|
3511
3597
|
const diffArgs = [
|
|
3512
3598
|
"diff",
|
|
3513
|
-
|
|
3599
|
+
...DIFF_OPTIONS,
|
|
3600
|
+
...format,
|
|
3514
3601
|
sha,
|
|
3515
3602
|
"--",
|
|
3516
3603
|
...paths
|
|
@@ -4159,42 +4246,51 @@ function assertEnvRefsResolve(ctx) {
|
|
|
4159
4246
|
* library and no server fallback: when git is missing, this is not a repo, the
|
|
4160
4247
|
* commit is not in local history, or the live release carries no `git_sha`, it
|
|
4161
4248
|
* prints one line saying no diff is available and carries on. Colour is
|
|
4162
|
-
* suppressed under `--
|
|
4163
|
-
*
|
|
4249
|
+
* suppressed under `--no-color` and a non-terminal. Under `--json` nothing is
|
|
4250
|
+
* printed, so the envelope travels alone on stdout: the diff, split by file, or
|
|
4251
|
+
* the line saying why there is none, is reported as `diff` instead.
|
|
4164
4252
|
*/
|
|
4165
4253
|
const diffStep = {
|
|
4166
4254
|
name: "diff",
|
|
4167
4255
|
runsFor: ["deploy", "preview"],
|
|
4168
4256
|
async run(ctx) {
|
|
4169
|
-
const note = (line) => {
|
|
4170
|
-
if (!ctx.json) process.stdout.write(`${line}\n`);
|
|
4171
|
-
};
|
|
4172
4257
|
const deployments = await listLiveDeployments(ctx.client, ctx.projectId, { withRelease: true });
|
|
4173
4258
|
const live = deployments.find(servesPrimary)?.release?.metadata;
|
|
4174
4259
|
const sha = live?.git_sha;
|
|
4175
|
-
if (!sha)
|
|
4176
|
-
|
|
4177
|
-
return;
|
|
4178
|
-
}
|
|
4179
|
-
if (live?.git_dirty) {
|
|
4180
|
-
note(`No reliable diff available: the live release was built from an uncommitted tree at ${sha.slice(0, 12)}.`);
|
|
4181
|
-
return;
|
|
4182
|
-
}
|
|
4260
|
+
if (!sha) return noDiff(ctx, null, "No diff available: nothing is deployed yet, or the live release has no commit.");
|
|
4261
|
+
if (live?.git_dirty) return noDiff(ctx, sha, `No reliable diff available: the live release was built from an uncommitted tree at ${sha.slice(0, 12)}.`);
|
|
4183
4262
|
const releases = new Set(deployments.filter(servesPrimary).map((entry) => entry.release_id));
|
|
4184
|
-
|
|
4263
|
+
const note = releases.size > 1 ? `The primary origins serve ${releases.size} different releases; the diff is against the newest, ${sha.slice(0, 12)}.` : void 0;
|
|
4264
|
+
const unknown = `No diff available: ${sha.slice(0, 12)} is not in this repository's history.`;
|
|
4185
4265
|
const referenced = ctx.brandingSources.flatMap((descriptor) => brandingReferencePaths(ctx.cwd, descriptor));
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
return
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4266
|
+
if (ctx.json) {
|
|
4267
|
+
const files = await diffConfigFilesAgainst(ctx.cwd, sha, referenced);
|
|
4268
|
+
if (files === null) return noDiff(ctx, sha, unknown);
|
|
4269
|
+
return { diff: {
|
|
4270
|
+
against: sha,
|
|
4271
|
+
files,
|
|
4272
|
+
...note === void 0 ? {} : { note }
|
|
4273
|
+
} };
|
|
4194
4274
|
}
|
|
4195
|
-
if (
|
|
4275
|
+
if (note !== void 0) process.stdout.write(`${note}\n`);
|
|
4276
|
+
const diff = await diffConfigAgainst(ctx.cwd, sha, ctx.color, referenced);
|
|
4277
|
+
if (diff === null) return noDiff(ctx, sha, unknown);
|
|
4278
|
+
const shown = diff.trim() === "" ? `No changes to .zitadel/ since ${sha.slice(0, 12)}.` : diff;
|
|
4279
|
+
process.stdout.write(shown.endsWith("\n") ? shown : `${shown}\n`);
|
|
4196
4280
|
}
|
|
4197
4281
|
};
|
|
4282
|
+
/** No diff to show: the line saying why, printed, or reported under `--json`. */
|
|
4283
|
+
function noDiff(ctx, against, note) {
|
|
4284
|
+
if (!ctx.json) {
|
|
4285
|
+
process.stdout.write(`${note}\n`);
|
|
4286
|
+
return;
|
|
4287
|
+
}
|
|
4288
|
+
return { diff: {
|
|
4289
|
+
against,
|
|
4290
|
+
files: null,
|
|
4291
|
+
note
|
|
4292
|
+
} };
|
|
4293
|
+
}
|
|
4198
4294
|
//#endregion
|
|
4199
4295
|
//#region src/lib/release/index.ts
|
|
4200
4296
|
/**
|
|
@@ -4279,7 +4375,7 @@ function exploreAllCommands(cliVersion) {
|
|
|
4279
4375
|
* the run was about.
|
|
4280
4376
|
*/
|
|
4281
4377
|
function customizeAndPublishActions(cliVersion, selector) {
|
|
4282
|
-
return ["Customize what you ask for and how people sign in: edit .zitadel/schemas/ to change the information you collect, and .zitadel/flows/ to change the sign-up/sign-in experience. Each folder has a README with examples.", `See your changes before they go live: ${projectCommand("deploy --dry-run", selector, cliVersion)} to preview, then ${projectCommand(DEPLOY_ARGS, selector, cliVersion)} to publish.`];
|
|
4378
|
+
return ["Customize what you ask for and how people sign in: edit .zitadel/schemas/ to change the information you collect, and .zitadel/flows/ to change the sign-up/sign-in experience. Each folder has a README with examples.", `See your changes before they go live: ${projectCommand("deploy --dry-run", selector, cliVersion)} to preview, then ${projectCommand(DEPLOY_ARGS, selector, cliVersion)} to publish. Nothing is released yet, so publishing is what pins this configuration rather than leaving the project on whatever its newest revisions happen to be.`];
|
|
4283
4379
|
}
|
|
4284
4380
|
/**
|
|
4285
4381
|
* How to change the login's look after setup. Setup never ejects a template
|
|
@@ -4343,6 +4439,6 @@ function isOptedOut(env) {
|
|
|
4343
4439
|
return value !== void 0 && value !== "" && value !== "0" && value !== "false";
|
|
4344
4440
|
}
|
|
4345
4441
|
//#endregion
|
|
4346
|
-
export {
|
|
4442
|
+
export { checkGivenCustomFlags as $, isPortableShellWord as $t, readJsonDirEntries as A, toVariableRow as At, selectSchema as B, installUserAgent as Bt, isPolicyTestFile as C, isErrno$1 as Cn, checkFlow as Ct, policyFileName as D, parseVariableValue as Dt, policyFileIssues as E, assertVariableName as Et, authMethods as F, variableReadScope as Ft, republishArgs as G, resolveConsent as Gt, publishClientId as H, hostAgent as Ht, enabledMethods as I, variableScope as It, askConnectionEndpoints as J, inShell as Jt, republishCommands as K, envEnabled as Kt, methodsOf as L, reportWarning as Lt, COMMAND_GROUPS as M, valuesOfKind as Mt, CommandGroups as N, variableCells as Nt, toPolicyWireBody as O, readStdin as Ot, compareInGroup as P, variableInventoryCells as Pt, SETUP_FLAG_NAMES as Q, unscopedCommand as Qt, readFlowFiles as R, takeReportedWarnings as Rt, POLICY_TEST_SUFFIX as S, ZitadelError as Sn, FLOWS_DIR as St, passwordPolicyFile as T, toZitadelError as Tn, VARIABLE_TYPES as Tt, reportClientIdOutcome as U, ciProvider as Ut, SCHEMAS_DIR as V, processUserAgentFacts as Vt, reportSecretOutcome as W, loadOrCreateIdentity as Wt, ENABLE_FLAG_NAMES as X, projectCommand as Xt, ENABLE_FLAG_KEYS as Y, portableSuggestions as Yt, SETUP_FLAG_KEYS as Z, shellCommand as Zt, servesPrimary as _, sanitizeRejection as _n, credentialVariablesOf as _t, serverLogAction as a, normalizePublicCliProse as an, loopbackIssuerWarning as at, PASSWORD_POLICY_PATH as b, listenForInterrupt as bn, refuseChangedIssuer as bt, wrapForBox as c, sdkVersionForCli as cn, applySsoToFlow as ct, validateStep as d, parseJsonObject as dn, removeSsoFromSchema as dt, isTrainVersion as en, customExample as et, headSha as f, setTopLevelJsonKey as fn, ssoEditRefusal as ft, rollbackDeployment as g, escapeControlCharacters as gn, checkCustomSlug as gt, listLiveDeployments as h, createZitadelClient$1 as hn, IDPS_DIR as ht, exploreAllCommands as i, normalizePublicCliJson as in, givenCustomFlags as it, BRANDING_DIR as j, toVariableRows as jt, readJsonDir as k, toInventoryRows as kt, DEPLOY_ARGS as l, collectPages as ln, applySsoToSchema as lt, createDeployment as m, REQUEST_TIMEOUT_MS as mn, CONNECTION_SCHEMA_REF as mt, brandingGuidanceAction as n, normalizePublicCliCommand as nn, customFlagValues as nt, verifyLoginAction as o, optionArgs as on, selectionFromFlags as ot, worktreeIsDirty as p, stableStringify as pn, ssoProvidersRefusal as pt, storeClientSecret as q, asEnvironmentSelector as qt, customizeAndPublishActions as r, normalizePublicCliCommands as rn, flagText as rt, renderBoxActions as s, publicCliCommand as sn, ssoEnableArgs as st, showSkillsHint as t, maskPublicCliVersion as tn, customFlagText as tt, diffStep as u, isObject$1 as un, removeSsoFromFlow as ut, buildBundle as v, sanitizeResponse as vn, planConnection as vt, parsePasswordMinLength as w, isMissingEndpoint as wn, offeredSignInMethods as wt, POLICIES_DIR as x, stopListeningForInterrupt as xn, storedCustomSettings as xt, PASSWORD_POLICY_OPERATION as y, interruptSignal as yn, readConnectionFiles as yt, readSchemaFiles as z, buildUserAgent as zt };
|
|
4347
4443
|
|
|
4348
|
-
//# sourceMappingURL=skills-hint-
|
|
4444
|
+
//# sourceMappingURL=skills-hint-N1H5-ny1.mjs.map
|