specshield 3.4.0 → 3.4.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specshield",
3
- "version": "3.4.0",
3
+ "version": "3.4.1",
4
4
  "description": "Contract compatibility testing for APIs — catch breaking OpenAPI changes before they reach your consumers, with can-i-deploy deploy gating and GitHub PR checks. (a.k.a. bidirectional contract testing.)",
5
5
  "main": "src/cli.js",
6
6
  "bin": {
@@ -92,9 +92,30 @@ const REQUIRED_FIELDS = {
92
92
  'list-consumers': ['org'],
93
93
  };
94
94
 
95
+ /**
96
+ * Option names that CAN be defaulted from `.specshield.yml`.
97
+ *
98
+ * Deliberately NOT every required field: `version`, `consumerVersion` and
99
+ * `providerVersion` are per-invocation values (a git SHA, a release tag) with no
100
+ * sensible project-wide default, so `bdctDefaultFor` has no case for them.
101
+ *
102
+ * This set exists so the "missing required options" error can tell the truth about
103
+ * where each field can be set. Pointing a user at `.specshield.yml` for a field the
104
+ * config can never supply sends them to edit a file, re-run, hit the identical error,
105
+ * and conclude the config file is broken.
106
+ *
107
+ * MUST stay in sync with the cases handled in {@link bdctDefaultFor} — the
108
+ * "config-backed fields" test asserts both directions.
109
+ */
110
+ const CONFIG_BACKED_FIELDS = new Set([
111
+ 'org', 'server', 'env',
112
+ 'provider', 'consumer', 'service',
113
+ 'spec', 'contract', 'format', 'branch',
114
+ ]);
115
+
95
116
  /**
96
117
  * Map a CLI option name to a path inside `.specshield.yml > bdct`.
97
- * Returns null when there is no defaulting rule for that option.
118
+ * Returns undefined when there is no defaulting rule for that option.
98
119
  */
99
120
  function bdctDefaultFor(bdct, name, command) {
100
121
  if (!bdct) return undefined;
@@ -181,14 +202,35 @@ function applyBdctDefaults(opts, command, { cwd = process.cwd() } = {}) {
181
202
  const missing = required.filter(k => !opts[k]);
182
203
  if (missing.length > 0) {
183
204
  const flagFor = (k) => '--' + k.replace(/[A-Z]/g, m => '-' + m.toLowerCase());
205
+ const list = (ks) => ks.map(flagFor).join(', ');
206
+
207
+ // Split the advice: only fields the config can actually supply get pointed at
208
+ // `.specshield.yml`. The rest are flag-only and must say so, or the user edits
209
+ // the config, re-runs, and hits the same error with no idea why.
210
+ const configurable = missing.filter(k => CONFIG_BACKED_FIELDS.has(k));
211
+ const flagOnly = missing.filter(k => !CONFIG_BACKED_FIELDS.has(k));
212
+
184
213
  const msg = [
185
214
  `Missing required ${missing.length === 1 ? 'option' : 'options'} for \`bdct ${command}\`: `
186
- + missing.map(flagFor).join(', '),
215
+ + list(missing),
187
216
  ];
188
- if (cfg._file) {
189
- msg.push(`Set them as CLI flags or add them under \`bdct\` in ${cfg._file}.`);
190
- } else {
191
- msg.push('Pass them as CLI flags, or run `specshield init` to write a `.specshield.yml`.');
217
+
218
+ if (flagOnly.length > 0) {
219
+ msg.push(
220
+ ` ${list(flagOnly)} must be passed as CLI ${flagOnly.length === 1 ? 'flag' : 'flags'} ` +
221
+ `${flagOnly.length === 1 ? 'it has' : 'they have'} no \`.specshield.yml\` equivalent ` +
222
+ '(version values change per run).');
223
+ }
224
+
225
+ if (configurable.length > 0) {
226
+ msg.push(cfg._file
227
+ ? ` ${list(configurable)} can be passed as CLI flags or set under \`bdct\` in ${cfg._file}.`
228
+ : ` ${list(configurable)} can be passed as CLI flags, or run \`specshield init\` to write a \`.specshield.yml\`.`);
229
+ }
230
+
231
+ if (command === 'verify' && flagOnly.length > 0) {
232
+ msg.push(' e.g. specshield bdct verify --consumer <NAME> --provider <NAME> '
233
+ + '--consumer-version <VER> --provider-version <VER>');
192
234
  }
193
235
  const err = new Error(msg.join('\n'));
194
236
  err.code = 'MISSING_REQUIRED_OPTIONS';
@@ -205,4 +247,6 @@ module.exports = {
205
247
  clearCache,
206
248
  applyBdctDefaults,
207
249
  REQUIRED_FIELDS,
250
+ CONFIG_BACKED_FIELDS,
251
+ bdctDefaultFor,
208
252
  };