bump-cli 2.7.3 → 2.8.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/README.md CHANGED
@@ -87,7 +87,7 @@ $ bump --help
87
87
  The Bump.sh CLI is used to interact with your API documentation hosted on Bump.sh by using the API of developers.bump.sh
88
88
 
89
89
  VERSION
90
- bump-cli/2.7.2 linux-x64 node-v16.17.0
90
+ bump-cli/2.8.0 linux-x64 node-v16.19.0
91
91
 
92
92
  USAGE
93
93
  $ bump [COMMAND]
@@ -96,6 +96,7 @@ COMMANDS
96
96
  deploy Create a new version of your documentation from the given file or URL.
97
97
  diff Get a comparison diff with your documentation from the given file or URL.
98
98
  help Display help for bump.
99
+ overlay Apply an OpenAPI specified overlay to your API definition.
99
100
  preview Create a documentation preview from the given file or URL.
100
101
  ```
101
102
 
@@ -112,6 +113,7 @@ Head over to your Documentation settings in the “CI deployment” section or y
112
113
  * [`bump deploy [FILE]`](#bump-deploy-file)
113
114
  * [`bump diff [FILE]`](#bump-diff-file)
114
115
  * [`bump preview [FILE]`](#bump-preview-file)
116
+ * [`bump overlay [DEFINITION_FILE] [OVERLAY_FILE]`](#bump-overlay-definition_file-overlay_file)
115
117
 
116
118
  ### `bump deploy [FILE]`
117
119
 
@@ -245,6 +247,26 @@ _Note: the additional `--open` flag helps to automatically open the preview URL
245
247
 
246
248
  Please check `bump preview --help` for more usage details
247
249
 
250
+ ### `bump overlay [DEFINITION_FILE] [OVERLAY_FILE]`
251
+
252
+ > This feature implements the [OpenAPI Overlay specification](https://github.com/OAI/Overlay-Specification). It is possible to apply an Overlay to any kind of document, be it an OpenAPI or AsyncAPI definition file.
253
+
254
+ The Overlay specification of OpenAPI makes it possible to modify the content of an API definition file by adding a layer on top of it. That layer helps adding, removing or changing some or all of the content of the original definition.
255
+
256
+ Technically, the `bump overlay` command will output a modified version of the `[DEFINITION_FILE]` (an OpenAPI or AsyncAPI document) by applying the operations described in the `[OVERLAY_FILE]` Overlay file to the original API document.
257
+
258
+ To redirect the output of the command to a new file you can run:
259
+
260
+ ```shell
261
+ bump overlay api-document.yaml overlay-file.yaml > api-overlayed-document.yaml
262
+ ```
263
+
264
+ _Note: you can also apply the overlay during the [`bump deploy` command]((#bump-deploy-file)) with the new `--overlay` flag:_
265
+
266
+ ```shell
267
+ bump deploy api-document.yaml --doc my-doc --token my-token --overlay overlay-file.yaml
268
+ ```
269
+
248
270
  ## Development
249
271
 
250
272
  Make sure to have Node.js (At least v14) installed on your machine.
@@ -283,6 +305,10 @@ We currently support [OpenAPI](https://github.com/OAI/OpenAPI-Specification) fro
283
305
 
284
306
  Bug reports and pull requests are welcome on GitHub at <https://github.com/bump-sh/cli>. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
285
307
 
308
+ ## Thanks
309
+
310
+ - [Lorna Mitchel](https://github.com/lornajane/) for [openapi-overlay-js](https://github.com/lornajane/openapi-overlays-js)
311
+
286
312
  ## License
287
313
 
288
314
  The Bump CLI project is released under the [MIT License](http://opensource.org/licenses/MIT).
package/lib/args.d.ts CHANGED
@@ -7,4 +7,9 @@ declare const otherFileArg: {
7
7
  name: string;
8
8
  description: string;
9
9
  };
10
- export { fileArg, otherFileArg };
10
+ declare const overlayFileArg: {
11
+ name: string;
12
+ required: boolean;
13
+ description: string;
14
+ };
15
+ export { fileArg, otherFileArg, overlayFileArg };
package/lib/args.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.otherFileArg = exports.fileArg = void 0;
3
+ exports.overlayFileArg = exports.otherFileArg = exports.fileArg = void 0;
4
4
  const fileArg = {
5
5
  name: 'FILE',
6
6
  required: true,
@@ -12,3 +12,9 @@ const otherFileArg = {
12
12
  description: 'Path or URL to a second API documentation file to compute its diff',
13
13
  };
14
14
  exports.otherFileArg = otherFileArg;
15
+ const overlayFileArg = {
16
+ name: 'OVERLAY_FILE',
17
+ required: true,
18
+ description: 'Path or URL to an overlay file',
19
+ };
20
+ exports.overlayFileArg = overlayFileArg;
@@ -14,6 +14,7 @@ export default class Deploy extends Command {
14
14
  interactive: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
15
15
  'filename-pattern': flagsBuilder.IOptionFlag<string | undefined>;
16
16
  'dry-run': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
17
+ overlay: flagsBuilder.IOptionFlag<string | undefined>;
17
18
  };
18
19
  static args: {
19
20
  name: string;
@@ -22,7 +22,7 @@ class Deploy extends command_1.default {
22
22
  */
23
23
  async run() {
24
24
  const { args, flags } = this.parse(Deploy);
25
- const [dryRun, documentation, token, hub, autoCreate, interactive, filenamePattern, documentationName, branch,] = [
25
+ const [dryRun, documentation, token, hub, autoCreate, interactive, filenamePattern, documentationName, branch, overlay,] = [
26
26
  flags['dry-run'],
27
27
  flags.doc,
28
28
  /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
@@ -36,6 +36,7 @@ class Deploy extends command_1.default {
36
36
  flags['filename-pattern'],
37
37
  flags['doc-name'],
38
38
  flags.branch,
39
+ flags.overlay,
39
40
  ];
40
41
  if ((0, file_1.isDir)(args.FILE)) {
41
42
  if (hub) {
@@ -49,7 +50,7 @@ class Deploy extends command_1.default {
49
50
  if (documentation) {
50
51
  const api = await definition_1.API.load(args.FILE);
51
52
  this.d(`${args.FILE} looks like an ${api.specName} spec version ${api.version}`);
52
- await this.deploySingleFile(api, dryRun, documentation, token, hub, autoCreate, documentationName, branch);
53
+ await this.deploySingleFile(api, dryRun, documentation, token, hub, autoCreate, documentationName, branch, overlay);
53
54
  }
54
55
  else {
55
56
  throw new errors_1.RequiredFlagError({ flag: Deploy.flags.doc, parse: {} });
@@ -86,14 +87,14 @@ class Deploy extends command_1.default {
86
87
  });
87
88
  }
88
89
  else {
89
- throw new errors_2.CLIError(`No documentations found in ${dir}.\nYou should check the ${chalk_1.default.dim('--filename-pattern')} flag to select your files from your naming convention.\nIf you don't have a naming convention we can help naming your API definition files:\nTry the ${chalk_1.default.dim('--interactive')} flag for that.`);
90
+ throw new errors_2.CLIError(`No documentation found in ${dir} with the pattern '${filenamePattern}'.\nYou should check with the ${chalk_1.default.dim('--filename-pattern')} flag to select your files from your naming convention.\nIf you don't have a naming convention we can help naming your API definition files:\nTry the ${chalk_1.default.dim('--interactive')} flag for that.`);
90
91
  }
91
92
  return;
92
93
  }
93
- async deploySingleFile(api, dryRun, documentation, token, hub, autoCreate, documentationName, branch) {
94
+ async deploySingleFile(api, dryRun, documentation, token, hub, autoCreate, documentationName, branch, overlay) {
94
95
  const action = dryRun ? 'validate' : 'deploy';
95
96
  cli_1.cli.action.start(`Let's ${action} a new version to your ${documentation} documentation on Bump.sh`);
96
- const response = await new deploy_1.Deploy(this.config).run(api, dryRun, documentation, token, hub, autoCreate, documentationName, branch);
97
+ const response = await new deploy_1.Deploy(this.config).run(api, dryRun, documentation, token, hub, autoCreate, documentationName, branch, overlay);
97
98
  if (dryRun) {
98
99
  await cli_1.cli.styledSuccess('Definition is valid');
99
100
  }
@@ -158,5 +159,6 @@ Deploy.flags = {
158
159
  interactive: flagsBuilder.interactive(),
159
160
  'filename-pattern': flagsBuilder.filenamePattern(),
160
161
  'dry-run': flagsBuilder.dryRun(),
162
+ overlay: flagsBuilder.overlay(),
161
163
  };
162
164
  Deploy.args = [args_1.fileArg];
@@ -0,0 +1,16 @@
1
+ import Command from '../command';
2
+ import * as flagsBuilder from '../flags';
3
+ export default class Overlay extends Command {
4
+ static description: string;
5
+ static examples: string[];
6
+ static flags: {
7
+ help: import("@oclif/parser/lib/flags").IBooleanFlag<void>;
8
+ out: flagsBuilder.IOptionFlag<string | undefined>;
9
+ };
10
+ static args: {
11
+ name: string;
12
+ required: boolean;
13
+ description: string;
14
+ }[];
15
+ run(): Promise<void>;
16
+ }
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const chalk_1 = (0, tslib_1.__importDefault)(require("chalk"));
5
+ const promises_1 = require("node:fs/promises");
6
+ const node_fs_1 = require("node:fs");
7
+ const path_1 = require("path");
8
+ const definition_1 = require("../definition");
9
+ const prompts_1 = require("../core/utils/prompts");
10
+ const command_1 = (0, tslib_1.__importDefault)(require("../command"));
11
+ const flagsBuilder = (0, tslib_1.__importStar)(require("../flags"));
12
+ const args_1 = require("../args");
13
+ const cli_1 = require("../cli");
14
+ class Overlay extends command_1.default {
15
+ async run() {
16
+ const { args, flags } = this.parse(Overlay);
17
+ const outputPath = flags.out;
18
+ cli_1.cli.action.start("* Let's apply the overlay to the main definition");
19
+ const api = await definition_1.API.load(args.FILE);
20
+ await api.applyOverlay(args.OVERLAY_FILE);
21
+ const [overlayedDefinition] = api.extractDefinition(outputPath);
22
+ cli_1.cli.action.stop();
23
+ if (outputPath) {
24
+ await (0, promises_1.mkdir)((0, path_1.dirname)(outputPath), { recursive: true });
25
+ let confirm = true;
26
+ if ((0, node_fs_1.existsSync)(outputPath)) {
27
+ await (0, prompts_1.confirm)(`Do you want to override the existing destination file? (${outputPath})`).catch(() => {
28
+ confirm = false;
29
+ });
30
+ }
31
+ if (confirm) {
32
+ await (0, promises_1.writeFile)(outputPath, overlayedDefinition);
33
+ }
34
+ }
35
+ else {
36
+ cli_1.cli.log(overlayedDefinition);
37
+ }
38
+ return;
39
+ }
40
+ }
41
+ exports.default = Overlay;
42
+ Overlay.description = 'Apply an OpenAPI specified overlay to your API definition.';
43
+ Overlay.examples = [
44
+ `Apply the OVERLAY_FILE to the existing DEFINITION_FILE. The resulting
45
+ definition is output on stdout meaning you can redirect it to a new
46
+ file.
47
+
48
+ ${chalk_1.default.dim('$ bump overlay DEFINITION_FILE OVERLAY_FILE > destination/file.json')}
49
+ * Let's apply the overlay to the main definition... done
50
+ `,
51
+ ];
52
+ Overlay.flags = {
53
+ help: flagsBuilder.help({ char: 'h' }),
54
+ out: flagsBuilder.out(),
55
+ };
56
+ Overlay.args = [args_1.fileArg, args_1.overlayFileArg];
@@ -6,7 +6,7 @@ export declare class Deploy {
6
6
  _bump: BumpApi;
7
7
  _config: Config.IConfig;
8
8
  constructor(config: Config.IConfig);
9
- run(api: API, dryRun: boolean, documentation: string, token: string, hub: string | undefined, autoCreate: boolean, documentationName: string | undefined, branch: string | undefined): Promise<VersionResponse | undefined>;
9
+ run(api: API, dryRun: boolean, documentation: string, token: string, hub: string | undefined, autoCreate: boolean, documentationName: string | undefined, branch: string | undefined, overlay?: string | undefined): Promise<VersionResponse | undefined>;
10
10
  get bumpClient(): BumpApi;
11
11
  createVersion(request: VersionRequest, token: string): Promise<VersionResponse | undefined>;
12
12
  validateVersion(version: VersionRequest, token: string): Promise<undefined>;
@@ -8,8 +8,11 @@ class Deploy {
8
8
  constructor(config) {
9
9
  this._config = config;
10
10
  }
11
- async run(api, dryRun, documentation, token, hub, autoCreate, documentationName, branch) {
11
+ async run(api, dryRun, documentation, token, hub, autoCreate, documentationName, branch, overlay) {
12
12
  let version = undefined;
13
+ if (overlay) {
14
+ await api.applyOverlay(overlay);
15
+ }
13
16
  const [definition, references] = api.extractDefinition();
14
17
  const request = {
15
18
  documentation,
@@ -2,6 +2,7 @@ import * as Config from '@oclif/config';
2
2
  import { BumpApi } from '../api';
3
3
  import { VersionResponse, WithDiff, DiffResponse } from '../api/models';
4
4
  export declare class Diff {
5
+ static readonly TIMEOUT = 120;
5
6
  _bump: BumpApi;
6
7
  _config: Config.IConfig;
7
8
  constructor(config: Config.IConfig);
package/lib/core/diff.js CHANGED
@@ -26,7 +26,7 @@ class Diff {
26
26
  }
27
27
  if (diffVersion) {
28
28
  return await this.waitResult(diffVersion, token, {
29
- timeout: 30,
29
+ timeout: Diff.TIMEOUT,
30
30
  format,
31
31
  });
32
32
  }
@@ -98,7 +98,7 @@ class Diff {
98
98
  pollingResponse = await this.bumpClient.getDiff(result.id, opts.format);
99
99
  }
100
100
  if (opts.timeout <= 0) {
101
- throw new errors_1.CLIError('We were unable to compute your documentation diff. Sorry about that. Please try again later');
101
+ throw new errors_1.CLIError('We were unable to compute your documentation diff. Sorry about that. Please try again later. If the error persists, please contact support at https://bump.sh.');
102
102
  }
103
103
  switch (pollingResponse.status) {
104
104
  case 200:
@@ -152,3 +152,5 @@ class Diff {
152
152
  }
153
153
  }
154
154
  exports.Diff = Diff;
155
+ // 120 seconds = 2 minutes
156
+ Diff.TIMEOUT = 120;
@@ -0,0 +1,5 @@
1
+ import { APIDefinition, OpenAPIOverlay } from '../definition';
2
+ export declare class Overlay {
3
+ run(spec: APIDefinition, overlay: OpenAPIOverlay): APIDefinition;
4
+ d(formatter: any, ...args: any[]): void;
5
+ }
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Overlay = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const debug_1 = (0, tslib_1.__importDefault)(require("debug"));
6
+ const jsonpath_1 = (0, tslib_1.__importDefault)(require("jsonpath"));
7
+ const mergician_1 = (0, tslib_1.__importDefault)(require("mergician"));
8
+ class Overlay {
9
+ // WIP @github.com/lornajane/openapi-overlays-js
10
+ //
11
+ // I couldn't get the upstream lib to be imported properly due to
12
+ // some issues with ESM module imports so this is method was copied
13
+ // from github.com/lornajane/openapi-overlays-js and has been
14
+ // adapted to make our Typescript build happy.
15
+ //
16
+ // If you make any changes here, PLEASE ALSO MAKE THEM UPSTREAM.
17
+ run(spec, overlay) {
18
+ // Use jsonpath.apply to do the changes
19
+ if (overlay.actions && overlay.actions.length >= 1)
20
+ overlay.actions.forEach((a) => {
21
+ const action = a;
22
+ if (!action.target) {
23
+ process.stderr.write('Action with a missing target\n');
24
+ return;
25
+ }
26
+ const target = action.target;
27
+ // Is it a remove?
28
+ if (action.hasOwnProperty('remove')) {
29
+ while (true) {
30
+ const path = jsonpath_1.default.paths(spec, target, 1);
31
+ if (path.length == 0) {
32
+ break;
33
+ }
34
+ const parent = jsonpath_1.default.parent(spec, target);
35
+ const thingToRemove = path[0][path[0].length - 1];
36
+ if (Array.isArray(parent)) {
37
+ parent.splice(thingToRemove, 1);
38
+ }
39
+ else {
40
+ delete parent[thingToRemove];
41
+ }
42
+ }
43
+ }
44
+ else {
45
+ try {
46
+ // It must be an update
47
+ // Deep merge objects using a module (built-in spread operator is only shallow)
48
+ const merger = (0, mergician_1.default)({ appendArrays: true });
49
+ if (target === '$') {
50
+ // You can't actually merge an update on a root object
51
+ // target with the jsonpath lib, this is just us merging
52
+ // the given update with the whole spec.
53
+ spec = merger(spec, action.update);
54
+ }
55
+ else {
56
+ jsonpath_1.default.apply(spec, target, (chunk) => {
57
+ if (typeof chunk === 'object' && typeof action.update === 'object') {
58
+ if (Array.isArray(chunk) && Array.isArray(action.update)) {
59
+ return chunk.concat(action.update);
60
+ }
61
+ else {
62
+ return merger(chunk, action.update);
63
+ }
64
+ }
65
+ else {
66
+ return action.update;
67
+ }
68
+ });
69
+ }
70
+ }
71
+ catch (ex) {
72
+ process.stderr.write(`Error applying overlay: ${ex.message}\n`);
73
+ //return chunk
74
+ }
75
+ }
76
+ });
77
+ return spec;
78
+ }
79
+ // Function signature type taken from @types/debug
80
+ // Debugger(formatter: any, ...args: any[]): void;
81
+ /* eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any */
82
+ d(formatter, ...args) {
83
+ return (0, debug_1.default)(`bump-cli:core:overlay`)(formatter, ...args);
84
+ }
85
+ }
86
+ exports.Overlay = Overlay;
@@ -1,5 +1,5 @@
1
1
  import $RefParser from '@apidevtools/json-schema-ref-parser';
2
- import { JSONSchema4, JSONSchema4Object, JSONSchema6, JSONSchema6Object, JSONSchema7 } from 'json-schema';
2
+ import { JSONSchema4, JSONSchema4Object, JSONSchema4Array, JSONSchema6, JSONSchema6Object, JSONSchema7 } from 'json-schema';
3
3
  declare type SpecSchema = JSONSchema4 | JSONSchema6 | JSONSchema7;
4
4
  declare class SupportedFormat {
5
5
  static readonly openapi: Record<string, SpecSchema>;
@@ -9,6 +9,7 @@ declare class API {
9
9
  readonly location: string;
10
10
  readonly rawDefinition: string;
11
11
  readonly definition: APIDefinition;
12
+ overlayedDefinition: APIDefinition | undefined;
12
13
  readonly references: APIReference[];
13
14
  readonly version: string;
14
15
  readonly specName: string;
@@ -17,26 +18,40 @@ declare class API {
17
18
  getSpec(definition: APIDefinition): SpecSchema;
18
19
  getSpecName(definition: APIDefinition): string;
19
20
  getVersion(definition: APIDefinition): string;
21
+ guessFormat(output?: string): string;
20
22
  versionWithoutPatch(): string;
21
23
  resolveRelativeLocation(absPath: string): string;
22
24
  resolveContent($refs: $RefParser.$Refs): [string, APIDefinition];
25
+ serializeDefinition(outputPath?: string): string;
23
26
  static isOpenAPI(definition: JSONSchema4Object | JSONSchema6Object): definition is OpenAPI;
24
27
  static isAsyncAPI(definition: JSONSchema4Object | JSONSchema6Object): definition is AsyncAPI;
25
- extractDefinition(): [string, APIReference[]];
28
+ static isOpenAPIOverlay(definition: JSONSchema4Object | JSONSchema6Object): definition is OpenAPIOverlay;
29
+ extractDefinition(outputPath?: string): [string, APIReference[]];
30
+ applyOverlay(overlayPath: string): Promise<void>;
26
31
  static load(path: string): Promise<API>;
27
32
  }
28
33
  declare type APIReference = {
29
34
  location: string;
30
35
  content: string;
31
36
  };
32
- declare type APIDefinition = OpenAPI | AsyncAPI;
37
+ declare type APIDefinition = OpenAPI | AsyncAPI | OpenAPIOverlay;
38
+ declare type InfoObject = {
39
+ readonly title: string;
40
+ readonly version: string;
41
+ readonly description?: string;
42
+ };
33
43
  declare type OpenAPI = JSONSchema4Object & {
34
44
  readonly openapi?: string;
35
45
  readonly swagger?: string;
36
- readonly info: string;
46
+ readonly info: InfoObject;
47
+ };
48
+ declare type OpenAPIOverlay = JSONSchema4Object & {
49
+ readonly overlay: string;
50
+ readonly info: InfoObject;
51
+ readonly actions: JSONSchema4Array;
37
52
  };
38
53
  declare type AsyncAPI = JSONSchema4Object & {
39
54
  readonly asyncapi: string;
40
- readonly info: string;
55
+ readonly info: InfoObject;
41
56
  };
42
- export { API, SupportedFormat };
57
+ export { API, APIDefinition, OpenAPIOverlay, SupportedFormat };
package/lib/definition.js CHANGED
@@ -7,6 +7,8 @@ const json_schema_ref_parser_1 = (0, tslib_1.__importDefault)(require("@apidevto
7
7
  const options_1 = require("@apidevtools/json-schema-ref-parser/lib/options");
8
8
  const specs_1 = (0, tslib_1.__importDefault)(require("@asyncapi/specs"));
9
9
  const path_1 = (0, tslib_1.__importDefault)(require("path"));
10
+ const yaml_1 = require("@stoplight/yaml");
11
+ const overlay_1 = require("./core/overlay");
10
12
  class SupportedFormat {
11
13
  }
12
14
  exports.SupportedFormat = SupportedFormat;
@@ -53,6 +55,9 @@ class API {
53
55
  if (API.isAsyncAPI(definition)) {
54
56
  return SupportedFormat.asyncapi[this.versionWithoutPatch()];
55
57
  }
58
+ else if (API.isOpenAPIOverlay(definition)) {
59
+ return { overlay: { type: 'string' } };
60
+ }
56
61
  else {
57
62
  return SupportedFormat.openapi[this.versionWithoutPatch()];
58
63
  }
@@ -73,6 +78,9 @@ class API {
73
78
  return (definition.openapi || definition.swagger);
74
79
  }
75
80
  }
81
+ guessFormat(output) {
82
+ return (output || this.location).endsWith('.json') ? 'json' : 'yaml';
83
+ }
76
84
  versionWithoutPatch() {
77
85
  const [major, minor] = this.version.split('.', 3);
78
86
  return `${major}.${minor}`;
@@ -125,18 +133,38 @@ class API {
125
133
  if (!parsed || !(parsed instanceof Object) || !('info' in parsed)) {
126
134
  throw new UnsupportedFormat("Definition needs to be an object with at least an 'info' key");
127
135
  }
128
- if (!API.isOpenAPI(parsed) && !API.isAsyncAPI(parsed)) {
136
+ if (!API.isOpenAPI(parsed) &&
137
+ !API.isAsyncAPI(parsed) &&
138
+ !API.isOpenAPIOverlay(parsed)) {
129
139
  throw new UnsupportedFormat();
130
140
  }
131
141
  return [raw, parsed];
132
142
  }
143
+ serializeDefinition(outputPath) {
144
+ if (this.overlayedDefinition) {
145
+ let serializedDefinition;
146
+ if (this.guessFormat(outputPath) == 'json') {
147
+ serializedDefinition = JSON.stringify(this.overlayedDefinition);
148
+ }
149
+ else {
150
+ serializedDefinition = (0, yaml_1.safeStringify)(this.overlayedDefinition);
151
+ }
152
+ return serializedDefinition;
153
+ }
154
+ else {
155
+ return this.rawDefinition;
156
+ }
157
+ }
133
158
  static isOpenAPI(definition) {
134
159
  return (typeof definition.openapi === 'string' || typeof definition.swagger === 'string');
135
160
  }
136
161
  static isAsyncAPI(definition) {
137
162
  return 'asyncapi' in definition;
138
163
  }
139
- extractDefinition() {
164
+ static isOpenAPIOverlay(definition) {
165
+ return 'overlay' in definition;
166
+ }
167
+ extractDefinition(outputPath) {
140
168
  const references = [];
141
169
  for (let i = 0; i < this.references.length; i++) {
142
170
  const reference = this.references[i];
@@ -145,7 +173,15 @@ class API {
145
173
  content: reference.content,
146
174
  });
147
175
  }
148
- return [this.rawDefinition, references];
176
+ return [this.serializeDefinition(outputPath), references];
177
+ }
178
+ async applyOverlay(overlayPath) {
179
+ const overlay = await API.load(overlayPath);
180
+ const overlayDefinition = overlay.definition;
181
+ if (!API.isOpenAPIOverlay(overlayDefinition)) {
182
+ throw new Error(`${overlayPath} does not look like an OpenAPI overlay`);
183
+ }
184
+ this.overlayedDefinition = await new overlay_1.Overlay().run(this.definition, overlayDefinition);
149
185
  }
150
186
  static async load(path) {
151
187
  const JSONParser = options_1.defaults.parse.json;
package/lib/flags.d.ts CHANGED
@@ -15,4 +15,6 @@ declare const failOnBreaking: (options?: {}) => Parser.flags.IBooleanFlag<boolea
15
15
  declare const live: (options?: {}) => Parser.flags.IBooleanFlag<boolean>;
16
16
  declare const format: flags.Definition<string>;
17
17
  declare const expires: flags.Definition<string>;
18
- export { doc, docName, hub, branch, token, autoCreate, interactive, filenamePattern, dryRun, open, failOnBreaking, live, format, expires, };
18
+ declare const out: flags.Definition<string>;
19
+ declare const overlay: flags.Definition<string>;
20
+ export { doc, docName, hub, branch, token, autoCreate, interactive, filenamePattern, dryRun, open, failOnBreaking, live, format, expires, out, overlay, };
package/lib/flags.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.expires = exports.format = exports.live = exports.failOnBreaking = exports.open = exports.dryRun = exports.filenamePattern = exports.interactive = exports.autoCreate = exports.token = exports.branch = exports.hub = exports.docName = exports.doc = void 0;
3
+ exports.overlay = exports.out = exports.expires = exports.format = exports.live = exports.failOnBreaking = exports.open = exports.dryRun = exports.filenamePattern = exports.interactive = exports.autoCreate = exports.token = exports.branch = exports.hub = exports.docName = exports.doc = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const command_1 = require("@oclif/command");
6
6
  // Re-export oclif flags https://oclif.io/docs/flags
@@ -104,3 +104,13 @@ const expires = command_1.flags.build({
104
104
  description: "Specify a longer expiration date for public diffs (defaults to 1 day). Use iso8601 format to provide a date, or you can use `--expires 'never'` to keep the result live indefinitely.",
105
105
  });
106
106
  exports.expires = expires;
107
+ const out = command_1.flags.build({
108
+ char: 'o',
109
+ description: 'Output file path',
110
+ });
111
+ exports.out = out;
112
+ const overlay = command_1.flags.build({
113
+ char: 'o',
114
+ description: 'Path or URL of an overlay file to apply before deploying',
115
+ });
116
+ exports.overlay = overlay;
package/lib/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { run } from '@oclif/command';
2
2
  import { Diff } from './core/diff';
3
+ import { Overlay } from './core/overlay';
3
4
  import Deploy from './commands/deploy';
4
5
  import Preview from './commands/preview';
5
6
  export { VersionResponse, PreviewResponse, DiffResponse, WithDiff } from './api/models';
6
- export { run, Deploy, Diff, Preview };
7
+ export { run, Deploy, Diff, Preview, Overlay };
package/lib/index.js CHANGED
@@ -1,11 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Preview = exports.Diff = exports.Deploy = exports.run = void 0;
3
+ exports.Overlay = exports.Preview = exports.Diff = exports.Deploy = exports.run = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const command_1 = require("@oclif/command");
6
6
  Object.defineProperty(exports, "run", { enumerable: true, get: function () { return command_1.run; } });
7
7
  const diff_1 = require("./core/diff");
8
8
  Object.defineProperty(exports, "Diff", { enumerable: true, get: function () { return diff_1.Diff; } });
9
+ const overlay_1 = require("./core/overlay");
10
+ Object.defineProperty(exports, "Overlay", { enumerable: true, get: function () { return overlay_1.Overlay; } });
9
11
  const deploy_1 = (0, tslib_1.__importDefault)(require("./commands/deploy"));
10
12
  exports.Deploy = deploy_1.default;
11
13
  const preview_1 = (0, tslib_1.__importDefault)(require("./commands/preview"));
@@ -1 +1 @@
1
- {"version":"2.7.3","commands":{"deploy":{"id":"deploy","description":"Create a new version of your documentation from the given file or URL.","pluginName":"bump-cli","pluginType":"core","aliases":[],"examples":["Deploy a new version of an existing documentation\n\n$ bump deploy FILE --doc <your_doc_id_or_slug> --token <your_doc_token>\n* Let's deploy a new documentation version on Bump... done\n* Your new documentation version will soon be ready\n","Deploy a new version of an existing documentation attached to a hub\n\n$ bump deploy FILE --doc <doc_slug> --hub <your_hub_id_or_slug> --token <your_doc_token>\n* Let's deploy a new documentation version on Bump... done\n* Your new documentation version will soon be ready\n","Deploy a whole directory of API definitions files to a hub\n\n$ bump deploy DIR --filename-pattern *-{slug}-api --hub <hub_slug> --token <hub_token>\nWe've found 2 valid API definitions to deploy\n└─ DIR\n └─ source-my-service-api.yml (OpenAPI spec version 3.1.0)\n └─ source-my-jobs-service-api.yml (AsyncAPI spec version 2.6.0)\n\nLet's deploy those documentations to your <hub_slug> hub on Bump.sh\n\n* Your new documentation version will soon be ready\nLet's deploy a new version to your my-service documentation on Bump.sh... done\n\n* Your new documentation version will soon be ready\nLet's deploy a new version to your my-jobs-service documentation on Bump.sh... done\n","Validate a new documentation version before deploying it\n\n$ bump deploy FILE --dry-run --doc <doc_slug> --token <your_doc_token>\n* Let's validate a new documentation version on Bump... done\n* Definition is valid\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"doc":{"name":"doc","type":"option","char":"d","description":"Documentation public id or slug. Can be provided via BUMP_ID environment variable"},"doc-name":{"name":"doc-name","type":"option","char":"n","description":"Documentation name. Used with --auto-create flag."},"hub":{"name":"hub","type":"option","char":"b","description":"Hub id or slug. Can be provided via BUMP_HUB_ID environment variable"},"branch":{"name":"branch","type":"option","char":"B","description":"Branch name. Can be provided via BUMP_BRANCH_NAME environment variable"},"token":{"name":"token","type":"option","char":"t","description":"Documentation or Hub token. Can be provided via BUMP_TOKEN environment variable","required":true},"auto-create":{"name":"auto-create","type":"boolean","description":"Automatically create the documentation if needed (only available with a --hub flag). Documentation name can be provided with --doc-name flag. Default: false","allowNo":false},"interactive":{"name":"interactive","type":"boolean","description":"Interactively create a configuration file to deploy a Hub (only available with a --hub flag). This will start an interactive process if you don't have a CLI configuration file. Default: false","allowNo":false},"filename-pattern":{"name":"filename-pattern","type":"option","description":"Pattern to extract the documentation slug from filenames when deploying a DIRECTORY. Pattern uses only '*' and '{slug}' as special characters to extract the slug from a filename without extension. Used with --hub flag only.","default":"{slug}-api"},"dry-run":{"name":"dry-run","type":"boolean","description":"Validate a new documentation version. Does everything a normal deploy would do except publishing the new version. Useful in automated environments such as test platforms or continuous integration. Default: false","allowNo":false}},"args":[{"name":"FILE","description":"Path or URL to your API documentation file. OpenAPI (2.0 to 3.1.0) and AsyncAPI (2.x) specifications are currently supported.\nPath can also be a directory when deploying to a Hub.","required":true}]},"diff":{"id":"diff","description":"Get a comparison diff with your documentation from the given file or URL.","pluginName":"bump-cli","pluginType":"core","aliases":[],"examples":["Compare a potential new version with the currently published one:\n\n $ bump diff FILE --doc <your_doc_id_or_slug> --token <your_doc_token>\n * Comparing the given definition file with the currently deployed one... done\n Removed: GET /compare\n Added: GET /versions/{versionId}\n","Store the diff in a dedicated file:\n\n $ bump diff FILE --doc <doc_slug> --token <doc_token> > /tmp/my-saved-diff\n * Comparing the given definition file with the currently deployed one... done\n\n $ cat /tmp/my-saved-diff\n Removed: GET /compare\n Added: GET /versions/{versionId}\n","In case of a non modified definition FILE compared to your existing documentation, no changes are output:\n\n $ bump diff FILE --doc <doc_slug> --token <your_doc_token>\n * Comparing the given definition file with the currently deployed one... done\n › Warning: Your documentation has not changed\n","Compare two different input files or URL independently to the one published on bump.sh\n\n $ bump diff FILE FILE2 --doc <doc_slug> --token <your_doc_token>\n * Comparing the two given definition files... done\n Updated: POST /versions\n Body attribute added: previous_version_id\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"doc":{"name":"doc","type":"option","char":"d","description":"Documentation public id or slug. Can be provided via BUMP_ID environment variable"},"hub":{"name":"hub","type":"option","char":"b","description":"Hub id or slug. Can be provided via BUMP_HUB_ID environment variable"},"branch":{"name":"branch","type":"option","char":"B","description":"Branch name. Can be provided via BUMP_BRANCH_NAME environment variable"},"token":{"name":"token","type":"option","char":"t","description":"Documentation or Hub token. Can be provided via BUMP_TOKEN environment variable","required":false},"open":{"name":"open","type":"boolean","char":"o","description":"Open the visual diff in your browser","allowNo":false},"fail-on-breaking":{"name":"fail-on-breaking","type":"boolean","char":"F","description":"Fail when diff contains a breaking change","allowNo":false},"format":{"name":"format","type":"option","char":"f","description":"Format in which to provide the diff result","options":["text","markdown","json","html"],"default":"text"},"expires":{"name":"expires","type":"option","char":"e","description":"Specify a longer expiration date for public diffs (defaults to 1 day). Use iso8601 format to provide a date, or you can use `--expires 'never'` to keep the result live indefinitely."}},"args":[{"name":"FILE","description":"Path or URL to your API documentation file. OpenAPI (2.0 to 3.1.0) and AsyncAPI (2.x) specifications are currently supported.\nPath can also be a directory when deploying to a Hub.","required":true},{"name":"FILE2","description":"Path or URL to a second API documentation file to compute its diff"}]},"preview":{"id":"preview","description":"Create a documentation preview from the given file or URL.","pluginName":"bump-cli","pluginType":"core","aliases":[],"examples":["$ bump preview FILE\n* Your preview is visible at: https://bump.sh/preview/45807371-9a32-48a7-b6e4-1cb7088b5b9b\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"live":{"name":"live","type":"boolean","char":"l","description":"Generate a preview each time you save the given file","allowNo":false},"open":{"name":"open","type":"boolean","char":"o","description":"Open the generated preview URL in your browser","allowNo":false}},"args":[{"name":"FILE","description":"Path or URL to your API documentation file. OpenAPI (2.0 to 3.1.0) and AsyncAPI (2.x) specifications are currently supported.\nPath can also be a directory when deploying to a Hub.","required":true}]}}}
1
+ {"version":"2.8.1","commands":{"deploy":{"id":"deploy","description":"Create a new version of your documentation from the given file or URL.","pluginName":"bump-cli","pluginType":"core","aliases":[],"examples":["Deploy a new version of an existing documentation\n\n$ bump deploy FILE --doc <your_doc_id_or_slug> --token <your_doc_token>\n* Let's deploy a new documentation version on Bump... done\n* Your new documentation version will soon be ready\n","Deploy a new version of an existing documentation attached to a hub\n\n$ bump deploy FILE --doc <doc_slug> --hub <your_hub_id_or_slug> --token <your_doc_token>\n* Let's deploy a new documentation version on Bump... done\n* Your new documentation version will soon be ready\n","Deploy a whole directory of API definitions files to a hub\n\n$ bump deploy DIR --filename-pattern *-{slug}-api --hub <hub_slug> --token <hub_token>\nWe've found 2 valid API definitions to deploy\n└─ DIR\n └─ source-my-service-api.yml (OpenAPI spec version 3.1.0)\n └─ source-my-jobs-service-api.yml (AsyncAPI spec version 2.6.0)\n\nLet's deploy those documentations to your <hub_slug> hub on Bump.sh\n\n* Your new documentation version will soon be ready\nLet's deploy a new version to your my-service documentation on Bump.sh... done\n\n* Your new documentation version will soon be ready\nLet's deploy a new version to your my-jobs-service documentation on Bump.sh... done\n","Validate a new documentation version before deploying it\n\n$ bump deploy FILE --dry-run --doc <doc_slug> --token <your_doc_token>\n* Let's validate a new documentation version on Bump... done\n* Definition is valid\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"doc":{"name":"doc","type":"option","char":"d","description":"Documentation public id or slug. Can be provided via BUMP_ID environment variable"},"doc-name":{"name":"doc-name","type":"option","char":"n","description":"Documentation name. Used with --auto-create flag."},"hub":{"name":"hub","type":"option","char":"b","description":"Hub id or slug. Can be provided via BUMP_HUB_ID environment variable"},"branch":{"name":"branch","type":"option","char":"B","description":"Branch name. Can be provided via BUMP_BRANCH_NAME environment variable"},"token":{"name":"token","type":"option","char":"t","description":"Documentation or Hub token. Can be provided via BUMP_TOKEN environment variable","required":true},"auto-create":{"name":"auto-create","type":"boolean","description":"Automatically create the documentation if needed (only available with a --hub flag). Documentation name can be provided with --doc-name flag. Default: false","allowNo":false},"interactive":{"name":"interactive","type":"boolean","description":"Interactively create a configuration file to deploy a Hub (only available with a --hub flag). This will start an interactive process if you don't have a CLI configuration file. Default: false","allowNo":false},"filename-pattern":{"name":"filename-pattern","type":"option","description":"Pattern to extract the documentation slug from filenames when deploying a DIRECTORY. Pattern uses only '*' and '{slug}' as special characters to extract the slug from a filename without extension. Used with --hub flag only.","default":"{slug}-api"},"dry-run":{"name":"dry-run","type":"boolean","description":"Validate a new documentation version. Does everything a normal deploy would do except publishing the new version. Useful in automated environments such as test platforms or continuous integration. Default: false","allowNo":false},"overlay":{"name":"overlay","type":"option","char":"o","description":"Path or URL of an overlay file to apply before deploying"}},"args":[{"name":"FILE","description":"Path or URL to your API documentation file. OpenAPI (2.0 to 3.1.0) and AsyncAPI (2.x) specifications are currently supported.\nPath can also be a directory when deploying to a Hub.","required":true}]},"diff":{"id":"diff","description":"Get a comparison diff with your documentation from the given file or URL.","pluginName":"bump-cli","pluginType":"core","aliases":[],"examples":["Compare a potential new version with the currently published one:\n\n $ bump diff FILE --doc <your_doc_id_or_slug> --token <your_doc_token>\n * Comparing the given definition file with the currently deployed one... done\n Removed: GET /compare\n Added: GET /versions/{versionId}\n","Store the diff in a dedicated file:\n\n $ bump diff FILE --doc <doc_slug> --token <doc_token> > /tmp/my-saved-diff\n * Comparing the given definition file with the currently deployed one... done\n\n $ cat /tmp/my-saved-diff\n Removed: GET /compare\n Added: GET /versions/{versionId}\n","In case of a non modified definition FILE compared to your existing documentation, no changes are output:\n\n $ bump diff FILE --doc <doc_slug> --token <your_doc_token>\n * Comparing the given definition file with the currently deployed one... done\n › Warning: Your documentation has not changed\n","Compare two different input files or URL independently to the one published on bump.sh\n\n $ bump diff FILE FILE2 --doc <doc_slug> --token <your_doc_token>\n * Comparing the two given definition files... done\n Updated: POST /versions\n Body attribute added: previous_version_id\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"doc":{"name":"doc","type":"option","char":"d","description":"Documentation public id or slug. Can be provided via BUMP_ID environment variable"},"hub":{"name":"hub","type":"option","char":"b","description":"Hub id or slug. Can be provided via BUMP_HUB_ID environment variable"},"branch":{"name":"branch","type":"option","char":"B","description":"Branch name. Can be provided via BUMP_BRANCH_NAME environment variable"},"token":{"name":"token","type":"option","char":"t","description":"Documentation or Hub token. Can be provided via BUMP_TOKEN environment variable","required":false},"open":{"name":"open","type":"boolean","char":"o","description":"Open the visual diff in your browser","allowNo":false},"fail-on-breaking":{"name":"fail-on-breaking","type":"boolean","char":"F","description":"Fail when diff contains a breaking change","allowNo":false},"format":{"name":"format","type":"option","char":"f","description":"Format in which to provide the diff result","options":["text","markdown","json","html"],"default":"text"},"expires":{"name":"expires","type":"option","char":"e","description":"Specify a longer expiration date for public diffs (defaults to 1 day). Use iso8601 format to provide a date, or you can use `--expires 'never'` to keep the result live indefinitely."}},"args":[{"name":"FILE","description":"Path or URL to your API documentation file. OpenAPI (2.0 to 3.1.0) and AsyncAPI (2.x) specifications are currently supported.\nPath can also be a directory when deploying to a Hub.","required":true},{"name":"FILE2","description":"Path or URL to a second API documentation file to compute its diff"}]},"overlay":{"id":"overlay","description":"Apply an OpenAPI specified overlay to your API definition.","pluginName":"bump-cli","pluginType":"core","aliases":[],"examples":["Apply the OVERLAY_FILE to the existing DEFINITION_FILE. The resulting\ndefinition is output on stdout meaning you can redirect it to a new\nfile.\n\n$ bump overlay DEFINITION_FILE OVERLAY_FILE > destination/file.json\n* Let's apply the overlay to the main definition... done\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"out":{"name":"out","type":"option","char":"o","description":"Output file path"}},"args":[{"name":"FILE","description":"Path or URL to your API documentation file. OpenAPI (2.0 to 3.1.0) and AsyncAPI (2.x) specifications are currently supported.\nPath can also be a directory when deploying to a Hub.","required":true},{"name":"OVERLAY_FILE","description":"Path or URL to an overlay file","required":true}]},"preview":{"id":"preview","description":"Create a documentation preview from the given file or URL.","pluginName":"bump-cli","pluginType":"core","aliases":[],"examples":["$ bump preview FILE\n* Your preview is visible at: https://bump.sh/preview/45807371-9a32-48a7-b6e4-1cb7088b5b9b\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"live":{"name":"live","type":"boolean","char":"l","description":"Generate a preview each time you save the given file","allowNo":false},"open":{"name":"open","type":"boolean","char":"o","description":"Open the generated preview URL in your browser","allowNo":false}},"args":[{"name":"FILE","description":"Path or URL to your API documentation file. OpenAPI (2.0 to 3.1.0) and AsyncAPI (2.x) specifications are currently supported.\nPath can also be a directory when deploying to a Hub.","required":true}]}}}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bump-cli",
3
3
  "description": "The Bump CLI is used to interact with your API documentation hosted on Bump by using the API of developers.bump.sh",
4
- "version": "2.7.3",
4
+ "version": "2.8.1",
5
5
  "author": "Paul Bonaud <paulr@bump.sh>",
6
6
  "bin": {
7
7
  "bump": "./bin/run"
@@ -11,6 +11,7 @@
11
11
  "@oclif/dev-cli": "^1.26.0",
12
12
  "@oclif/test": "^2.0.3",
13
13
  "@types/debug": "^4.1.5",
14
+ "@types/jsonpath": "^0.2.4",
14
15
  "@types/mocha": "^10.0.0",
15
16
  "@types/node": "^20.7.0",
16
17
  "@typescript-eslint/eslint-plugin": "^5.21.0",
@@ -23,7 +24,7 @@
23
24
  "globby": "^11.0.3",
24
25
  "mocha": "^10.0.0",
25
26
  "nock": "^13.0.11",
26
- "np": "^7.6.2",
27
+ "np": "^10.0.5",
27
28
  "nyc": "^15.1.0",
28
29
  "prettier": "^2.2.1",
29
30
  "sinon": "^14.0.0",
@@ -32,7 +33,7 @@
32
33
  "typescript": "4.5.5"
33
34
  },
34
35
  "engines": {
35
- "node": ">=14.0.0"
36
+ "node": ">=16.0.0"
36
37
  },
37
38
  "files": [
38
39
  "/bin",
@@ -89,9 +90,12 @@
89
90
  "@oclif/core": "1.20.4",
90
91
  "@oclif/plugin-help": "^5.1.10",
91
92
  "@oclif/plugin-warn-if-update-available": "^2.0.36",
93
+ "@stoplight/yaml": "^4.2.3",
92
94
  "async-mutex": "^0.4.0",
93
95
  "axios": "^0.27.2",
94
96
  "debug": "^4.3.1",
97
+ "jsonpath": "^1.1.1",
98
+ "mergician": "^1.0.3",
95
99
  "oas-schemas": "git+https://git@github.com/OAI/OpenAPI-Specification.git#0f9d3ec7c033fef184ec54e1ffc201b2d61ce023",
96
100
  "tslib": "^2.3.0"
97
101
  }