@varde-flyt/vfac 0.11.0 → 0.12.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.
Files changed (3) hide show
  1. package/README.md +19 -7
  2. package/dist/vfac.mjs +70 -18
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -74,14 +74,21 @@ vfac manifest init --product <productId> --name "My resource" > resource.yaml
74
74
 
75
75
  ```sh
76
76
  vfac validate -f resource.yaml
77
- vfac plan -f resource.yaml
77
+ vfac plan -f resource.yaml --secret SETTING=env:VARIABLE
78
78
  vfac apply -f resource.yaml --secret SETTING=env:VARIABLE
79
79
  vfac get operation <operationId>
80
80
  vfac get product-instance <productInstanceId>
81
81
  ```
82
82
 
83
83
  Secrets are supplied per apply and never belong in the manifest — a manifest is a
84
- file you commit. `vfac plan` lists the ones an apply will still need.
84
+ file you commit. **Plan the run that sets one with the same `--secret` inputs the
85
+ apply will carry**: `plan` takes the flag exactly as `apply` does, and without it
86
+ the plan describes a different request.
87
+
88
+ `requiredSecrets` in a plan is what the Product requires for the configuration in
89
+ your document — not a list of what the platform is missing. It names a credential
90
+ whether or not this resource already holds one, so it is not a shopping list to
91
+ act on. `secretsToWrite` is the field that says what this apply would write.
85
92
 
86
93
  ### `metadata.key` is the name you choose
87
94
 
@@ -176,7 +183,8 @@ spec:
176
183
  `vfac manifest init` names the exact property for every dependency you have to
177
184
  choose, and `vfac plan` prints the connections an apply would establish before
178
185
  you approve it. The dependency key comes from the Product, not from you —
179
- `vfac product show <productId>` lists them.
186
+ `vfac product show <productId>` lists every dependency with its key, its
187
+ capability, and whether a setting is what turns it on.
180
188
 
181
189
  This is not the same thing as the section above, and the difference is worth
182
190
  holding on to:
@@ -198,7 +206,8 @@ it.
198
206
 
199
207
  Every command takes `--output json`, and it is the same data rather than a
200
208
  prettier subset — the human format of `vfac product show` prints a readable
201
- selection on purpose, and the JSON carries the whole Product contract.
209
+ selection on purpose and says so at the foot of its output, and the JSON carries
210
+ the whole Product contract.
202
211
 
203
212
  ```sh
204
213
  vfac product show <productId> --output json
@@ -261,12 +270,15 @@ so this answers `UPDATE` and redeploys, even when the value is the value already
261
270
  stored:
262
271
 
263
272
  ```sh
273
+ vfac plan -f resource.yaml --secret SETTING=env:VARIABLE
264
274
  vfac apply -f resource.yaml --secret SETTING=env:VARIABLE
265
275
  ```
266
276
 
267
- Send secrets on the apply that creates the resource and on the run that rotates
268
- one, and on no other run. A pipeline that passes its secrets every run restarts
269
- your service every time somebody pushes a README.
277
+ Three runs carry a secret: the apply that creates the resource, the first time a
278
+ setting needs a credential on a resource that already exists, and a rotation. Send
279
+ none on any other run a pipeline that passes its secrets every run restarts your
280
+ service every time somebody pushes a README. Plan each of those three with the
281
+ same `--secret` inputs: with no secret to write, a rotation plans as `NO_CHANGE`.
270
282
 
271
283
  ## Where the Gate Secret goes
272
284
 
package/dist/vfac.mjs CHANGED
@@ -7932,8 +7932,9 @@ function renderOperation(operationId, data) {
7932
7932
  if (typeof retryable === "boolean") {
7933
7933
  const again = data["type"] === "PRODUCT_INSTANCE_UPGRADE" ? "`vfac lifecycle upgrade <pri_\u2026>`" : "`vfac lifecycle reconcile <pri_\u2026>`";
7934
7934
  const changeIt = data["type"] === "PRODUCT_INSTANCE_UPGRADE" ? "" : ", or `vfac apply` a corrected manifest";
7935
+ const cause2 = failure["cause"];
7935
7936
  lines.push(
7936
- retryable ? ` Another attempt can succeed: ${again}.` : ` Repeating this unchanged fails again \u2014 fix what the lines above name, then ${again}${changeIt}.`
7937
+ retryable ? ` Another attempt can succeed: ${again}.` : cause2 === "unknown" ? ` The platform could not classify this, so there is nothing here to correct. Read the whole record with \`vfac get operation ${operationId} --output json\`, and quote that id if you ask for help. Repeating it unchanged fails the same way.` : ` Repeating this unchanged fails again \u2014 fix what the lines above name, then ${again}${changeIt}.`
7937
7938
  );
7938
7939
  }
7939
7940
  }
@@ -13255,6 +13256,7 @@ function conditionHolds(condition, configuration) {
13255
13256
  // ../product-configuration/dist/customer-configuration-input.js
13256
13257
  var ABSOLUTE_MAX_STRING = MAX_DECLARABLE_STRING_LENGTH;
13257
13258
  var MAX_REPORTED_UNDECLARED_KEYS = 10;
13259
+ var UNDECLARED_SETTING_MESSAGE = "This Product does not declare a setting with that name.";
13258
13260
  function isPlainObject2(value) {
13259
13261
  return typeof value === "object" && value !== null && !Array.isArray(value);
13260
13262
  }
@@ -13338,7 +13340,7 @@ function validateObject(declaration, input, container, walk, root) {
13338
13340
  };
13339
13341
  const undeclared = Object.keys(input).filter((name) => !Object.hasOwn(declaration.properties, name));
13340
13342
  for (const name of undeclared.slice(0, MAX_REPORTED_UNDECLARED_KEYS)) {
13341
- report(walk, fieldFor(name), "This Product does not declare a setting with that name.");
13343
+ report(walk, fieldFor(name), UNDECLARED_SETTING_MESSAGE);
13342
13344
  }
13343
13345
  if (undeclared.length > MAX_REPORTED_UNDECLARED_KEYS) {
13344
13346
  report(walk, container === null ? "" : container, `${undeclared.length - MAX_REPORTED_UNDECLARED_KEYS} further settings are not declared by this Product.`);
@@ -13604,7 +13606,11 @@ function checkString(field, property, value, issues) {
13604
13606
  return void 0;
13605
13607
  }
13606
13608
  if (property.pattern !== void 0 && !matches(property.pattern, value)) {
13607
- issues.push({ field, message: "Value is not in the required format." });
13609
+ const expectation = property.description;
13610
+ issues.push({
13611
+ field,
13612
+ message: expectation === void 0 ? "Value is not in the required format." : `Value is not in the required format. ${expectation.replace(/\s+$/, "")}`
13613
+ });
13608
13614
  return void 0;
13609
13615
  }
13610
13616
  return value;
@@ -16801,8 +16807,16 @@ var MANIFEST_ACTIONS = [
16801
16807
  "ADOPT",
16802
16808
  /** The key names a resource and something in the document differs. */
16803
16809
  "UPDATE",
16804
- /** The key names a resource and the document matches it. A pipeline that runs
16805
- * on every push and changes nothing reports this, and it is SUCCESS. */
16810
+ /** The key names a resource and this REQUEST asks for nothing: no field
16811
+ * differs and no secret would be written.
16812
+ *
16813
+ * NOT "the document matches it", which is how it read and is a stronger claim
16814
+ * than the planner makes. What a document OMITS is left alone rather than
16815
+ * compared — a manifest naming no connections answers this beside a resource
16816
+ * that holds several — so this never means the file mirrors stored state.
16817
+ *
16818
+ * A pipeline that runs on every push and changes nothing reports it, and it
16819
+ * is SUCCESS. */
16806
16820
  "NO_CHANGE",
16807
16821
  /** `adopt` points at a resource that was DELETED. Its own verb, because both
16808
16822
  * alternatives are wrong: `NO_CHANGE` would call a missing resource fine, and
@@ -17089,7 +17103,9 @@ function secretWriteConsequence(plan) {
17089
17103
  const lines = ["the value you send REPLACES the stored one, and the resource redeploys."];
17090
17104
  if (plan.action === "UPDATE" && (plan.changes ?? []).length === 0) {
17091
17105
  lines.push("Nothing else changed \u2014 sending a secret is itself the change.");
17092
- lines.push("Omit --secret on runs that are not rotating one to get NO_CHANGE.");
17106
+ lines.push(
17107
+ "Omit --secret on a run that is not setting or changing one; that is what answers NO_CHANGE."
17108
+ );
17093
17109
  }
17094
17110
  return lines;
17095
17111
  }
@@ -17893,9 +17909,9 @@ async function runGet(parsed) {
17893
17909
  lines.push("", " Outputs");
17894
17910
  const width = Math.max(...names.map((name) => name.length));
17895
17911
  for (const name of names) {
17896
- lines.push(
17897
- ` ${name.padEnd(width)} ${isSecret2(name) ? "[secret]" : String(outputs[name] ?? "")}`
17898
- );
17912
+ const value = outputs[name];
17913
+ const shown = isSecret2(name) ? "[secret]" : value === void 0 || value === null ? "[not available]" : String(value);
17914
+ lines.push(` ${name.padEnd(width)} ${shown}`);
17899
17915
  const meaning = declared.find((entry) => entry["key"] === name);
17900
17916
  const description = meaning?.["description"];
17901
17917
  if (typeof description === "string") {
@@ -18085,14 +18101,41 @@ function renderProduct(product) {
18085
18101
  lines.push(` ${setting.label} ${setting.type ?? ""}`.trimEnd());
18086
18102
  }
18087
18103
  }
18088
- const needed = (product.dependencies ?? []).filter((dependency) => dependency.required);
18089
- if (needed.length > 0) {
18090
- lines.push("", " Requires a connection to another resource:");
18091
- for (const dependency of needed) {
18092
- lines.push(` ${dependency.productId ?? "(a Product providing a service)"}`);
18104
+ const dependencies = product.dependencies ?? [];
18105
+ if (dependencies.length > 0) {
18106
+ lines.push("", " Connections to another resource");
18107
+ for (const dependency of dependencies) {
18108
+ const when = dependency.requiredWhen?.property;
18109
+ const need = dependency.required ? "required" : when === void 0 ? "optional" : `required when ${when} is ${(dependency.requiredWhen?.anyOf ?? []).join(" or ")}`;
18110
+ lines.push(` ${dependency.key ?? "(no key)"} ${need}`);
18111
+ const capability = dependency.interface?.id;
18112
+ if (capability !== void 0) {
18113
+ lines.push(
18114
+ ` ${capability}${dependency.interface?.version ? `/${dependency.interface.version}` : ""}`
18115
+ );
18116
+ } else if (dependency.productId !== void 0) {
18117
+ lines.push(` ${dependency.productId}`);
18118
+ }
18093
18119
  if (dependency.reason) lines.push(` ${dependency.reason}`);
18094
18120
  }
18095
18121
  }
18122
+ const outputs = product.outputs ?? [];
18123
+ if (outputs.length > 0) {
18124
+ lines.push("", " Publishes");
18125
+ for (const output of outputs) {
18126
+ lines.push(
18127
+ ` ${output.key ?? ""} ${output.secret === true ? "secret" : output.type ?? ""}`.trimEnd()
18128
+ );
18129
+ }
18130
+ }
18131
+ const conditions = product.conditions ?? [];
18132
+ if (conditions.length > 0) {
18133
+ lines.push("", " Needs setting up outside the platform");
18134
+ for (const condition of conditions) {
18135
+ lines.push(` ${condition.key ?? ""} ${condition.displayName ?? ""}`.trimEnd());
18136
+ }
18137
+ }
18138
+ lines.push("", " `--output json` carries the whole declaration, this is a readable subset.");
18096
18139
  return lines.join("\n");
18097
18140
  }
18098
18141
  function wrap(text, width) {
@@ -18441,6 +18484,12 @@ Run \`vfac get product-instance pri_\u2026\` to see which ones this credential m
18441
18484
  `);
18442
18485
  return 2;
18443
18486
  }
18487
+ if (parsed.booleans.has("wait")) {
18488
+ process.stderr.write(
18489
+ "`--wait` belongs to `vfac apply`; `vfac lifecycle` does not support it.\nThis command answers with an operation id. Follow it with:\n vfac get operation op_\u2026\nand branch on the status it reports: SUCCEEDED, FAILED or CANCELLED. That\ncommand's exit code says whether the READ worked, never whether the operation\ndid \u2014 so a script that only checks the exit code reports a failed deploy as a\nsuccess.\n"
18490
+ );
18491
+ return 2;
18492
+ }
18444
18493
  if (command === "delete" && !parsed.booleans.has("yes")) {
18445
18494
  process.stderr.write(
18446
18495
  `delete removes ${instanceId} and everything it is serving. This cannot be undone.
@@ -18547,7 +18596,7 @@ var USAGE = `vfac \u2014 deploy Varde Flyt resources from a manifest
18547
18596
  vfac manifest init --product <productId> --name <name> [--key <key>]
18548
18597
  [--region <region>] [--profile <profile>]
18549
18598
  vfac validate -f resource.yaml
18550
- vfac plan -f resource.yaml
18599
+ vfac plan -f resource.yaml [--secret NAME=env:VAR]
18551
18600
  vfac apply -f resource.yaml [--secret NAME=env:VAR] [--adopt pri_\u2026] [--wait]
18552
18601
  vfac get product-instances
18553
18602
  vfac get product-instance pri_\u2026 what it is, and its outputs
@@ -18564,9 +18613,12 @@ Options
18564
18613
  --project the Project to act in. Defaults to the stored context
18565
18614
  --endpoint your organization's portal. Defaults to VFAC_ENDPOINT, then the
18566
18615
  stored context
18567
- --secret NAME=env:VARIABLE \u2014 the value is read from the environment.
18568
- A value on the command line is in your shell history and in the
18569
- process table, so it is refused
18616
+ --secret plan and apply: NAME=env:VARIABLE \u2014 the value is read from the
18617
+ environment. Plan a run that sets a secret with the same
18618
+ --secret inputs the apply will carry; without them the plan
18619
+ answers about a different request. A value on the command line
18620
+ is in your shell history and in the process table, so it is
18621
+ refused
18570
18622
  --product which Product to write a starting manifest for
18571
18623
  --name what a person will call the resource
18572
18624
  --key its stable identity. Derived from --name when omitted
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@varde-flyt/vfac",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "Deploy and manage Varde Flyt Products from a manifest.",
5
5
  "repository": {
6
6
  "type": "git",