Skip to content

feat(config): add global -c/--config option and OPENAPI_GENERATOR_CLI_CONFIG env var for the openapitools.json location - #1296

Open
Inf1mum wants to merge 2 commits into
OpenAPITools:masterfrom
Inf1mum:feat/782-config-flag
Open

Inf1mum wants to merge 2 commits into
OpenAPITools:masterfrom
Inf1mum:feat/782-config-flag

Conversation

@Inf1mum

@Inf1mum Inf1mum commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #782

What

Adds a way to point openapi-generator-cli at an openapitools.json outside the current working directory (monorepo use-case):

  • new global option -c, --config <path> on the root command — the existing --openapitools <openapitools.json> option is kept and works as an alias
  • new OPENAPI_GENERATOR_CLI_CONFIG environment variable as a fallback, so the path can be supplied without changing the command line (e.g. in CI)

Resolution order: explicit flag > OPENAPI_GENERATOR_CLI_CONFIG > ./openapitools.json (default).

Behavior guarantees:

  • when neither the flag nor the env var is set, nothing changes — ./openapitools.json is used and created on first run, exactly as before
  • relative paths are resolved against the current working directory
  • when an explicit path (flag or env var) is given but the file does not exist, the CLI fails fast with a clear error message and exit code 1 instead of silently creating/using a default config (the silent-failure mode also reported in cli#386)

Design note: design comment on #782.

How

  • ConfigService.configFileOrDefault() resolves the location (flag > env var > default). It remains the single source of truth for every read/write of openapitools.json, so all commands (version-manager list/set/resolve, use, generate, pass-through) pick it up without further changes.
  • AppModule registers the new option on the root program, and isVersionManagerResolveCommand() now also skips the new option forms (-c <path>, --config <path>, --config=<path>, -c<path>) when scanning argv.

Tests

Unit tests (config.service.spec.ts, app.module.spec.ts):

  • explicit path used via -c/--config and via --openapitools (existing behavior kept)
  • explicit path used via OPENAPI_GENERATOR_CLI_CONFIG
  • relative explicit paths (flag and env var) resolved against the cwd
  • flag takes precedence over the env var
  • default ./openapitools.json unchanged when neither is set
  • missing explicit file (flag and env var) → error message contains the resolved path and process.exit(1)
  • missing default file → no error, no exit (backwards compatible)
  • version-manager resolve detection skips the new option forms

Full suite: 263 tests / 7 suites passing, plus lint and build, run in a node:20 Docker container. Manually smoke-tested the built CLI: valid flag, valid env var, precedence (flag wins), missing-path error paths, and the untouched default flow.


Summary by cubic

Lets the CLI point at an openapitools.json outside the current working directory, for monorepos and CI.

  • Adds a global -c, --config <path> option before the command name, with --openapitools kept as a working alias; it is not registered with commander to avoid colliding with the Java generator's own generate -c/--config option, but is listed in help and stripped from the argv handed to commander.
  • Adds the OPENAPI_GENERATOR_CLI_CONFIG env var as a fallback; resolution order is flag, env var, then the default ./openapitools.json (unchanged when neither is set).
  • Relative paths resolve against the working directory, and a missing explicitly-set config now fails fast with a clear error and exit code 1 instead of creating a default config.
  • Only occurrences placed before the command name count; -c/--config after it belong to the invoked command and pass through untouched.

Written for commit 584f43f. Summary will update on new commits.

View guided diff

…env var to locate openapitools.json

- resolution order: explicit flag (-c/--config or --openapitools) >
  OPENAPI_GENERATOR_CLI_CONFIG env var > ./openapitools.json (default,
  unchanged when neither is set)
- relative paths are resolved against the cwd
- fail fast with a clear error when an explicitly set config path does
  not exist
- version-manager resolve argv detection now also skips the new option
  forms

Fixes OpenAPITools#782

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 5 files

Reply with feedback, questions, or to request a fix.

View guided diff | Re-trigger cubic

…mand name

The global '-c, --config <path>' option collided with the Java generator's
own 'generate -c, --config <config file>' option (upstream Generate.java
defines name = {"-c", "--config"} for its generator configuration file).
Commander consumes registered root options from the whole argv - in both
parseOptions (used by ConfigService) and parse - so '-c' after 'generate'
was captured as the openapitools.json path: the CLI exited with 1 when
that path did not exist, and the generator config never reached the Java
process because commander had removed it from the subcommand arguments.

Of the two discussed options, excluding the invoked command's argv from
the global -c parsing (option a) is the only correct minimal fix: keeping
the option registered would keep the collision, and dropping only the
short alias would not be enough because the Java generate command also
defines the long '--config' form. The documented UX is preserved:

- the global -c/--config option is no longer registered with commander;
  it is still listed in the root help and only honored BEFORE the command
  name via extractGlobalConfigArgs(), which understands all option forms
  (-c <path>, -c<path>, --config <path>, --config=<path>). Everything
  after the first positional argument belongs to the invoked command and
  is passed through untouched, which also covers commands other than
  'generate'.
- AppModule hands commander an argv without the global config options, so
  an unrecognized option before the command name cannot bury the command
  (commander would otherwise skip its dispatch and print help).
- --openapitools keeps its existing behavior (registered root option,
  honored in any position).

Also clear the shared 'error' mock between tests in
config.service.spec.ts so each missing-file assertion only sees its own
test's log output.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 5 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/generator-cli/src/app/services/config.service.spec.ts">

<violation number="1" location="apps/generator-cli/src/app/services/config.service.spec.ts:25">
P2: The top-level `beforeEach` does not clear `OPENAPI_GENERATOR_CLI_CONFIG`, so a developer or CI shell that exports it makes every `ConfigService` construction in this suite run the not-found path. `fs.existsSync` is automocked and returns `undefined`, so `process.exit(1)` runs for real. Delete the variable in the top-level `beforeEach` as well, the same way the argv is pinned there.</violation>
</file>

<file name="apps/generator-cli/src/app/services/config.service.ts">

<violation number="1" location="apps/generator-cli/src/app/services/config.service.ts:68">
P2: A root-level `--custom-generator` before `-c` stops this scan, so the explicit config path is ignored and the environment/default config is used. Skip recognized root options and their values before treating an argument as the command name.</violation>

<violation number="2" location="apps/generator-cli/src/app/services/config.service.ts:81">
P2: A trailing `-c` or `--config` assigns `undefined`, so the CLI silently falls back to the environment or default config instead of rejecting the missing required path. Reject the missing value here.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Re-trigger cubic

// invocation so the runner's own arguments (e.g. jest's --config) are not
// mistaken for cli options
originalArgv = process.argv;
process.argv = ['node', 'openapi-generator-cli'];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The top-level beforeEach does not clear OPENAPI_GENERATOR_CLI_CONFIG, so a developer or CI shell that exports it makes every ConfigService construction in this suite run the not-found path. fs.existsSync is automocked and returns undefined, so process.exit(1) runs for real. Delete the variable in the top-level beforeEach as well, the same way the argv is pinned there.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/generator-cli/src/app/services/config.service.spec.ts, line 25:

<comment>The top-level `beforeEach` does not clear `OPENAPI_GENERATOR_CLI_CONFIG`, so a developer or CI shell that exports it makes every `ConfigService` construction in this suite run the not-found path. `fs.existsSync` is automocked and returns `undefined`, so `process.exit(1)` runs for real. Delete the variable in the top-level `beforeEach` as well, the same way the argv is pinned there.</comment>

<file context>
@@ -15,7 +15,15 @@ describe('ConfigService', () => {
+    // invocation so the runner's own arguments (e.g. jest's --config) are not
+    // mistaken for cli options
+    originalArgv = process.argv;
+    process.argv = ['node', 'openapi-generator-cli'];
+
     program = createCommand();
</file context>
Suggested change
process.argv = ['node', 'openapi-generator-cli'];
process.argv = ['node', 'openapi-generator-cli'];
delete process.env[ConfigService.envConfigVariable];

configArgs.add(index);
if (hasSeparateValue) {
configArgs.add(index + 1);
configPath = args[index + 1];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: A trailing -c or --config assigns undefined, so the CLI silently falls back to the environment or default config instead of rejecting the missing required path. Reject the missing value here.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/generator-cli/src/app/services/config.service.ts, line 81:

<comment>A trailing `-c` or `--config` assigns `undefined`, so the CLI silently falls back to the environment or default config instead of rejecting the missing required path. Reject the missing value here.</comment>

<file context>
@@ -4,6 +4,97 @@ import { COMMANDER_PROGRAM, LOGGER } from '../constants';
+    configArgs.add(index);
+    if (hasSeparateValue) {
+      configArgs.add(index + 1);
+      configPath = args[index + 1];
+    } else if (args[index].startsWith('--config=')) {
+      configPath = args[index].slice('--config='.length);
</file context>
Suggested change
configPath = args[index + 1];
configPath = args[index + 1];
if (!configPath) {
throw new Error('The -c/--config option requires a path');
}


// the command name or an option of the invoked command:
// everything from here on belongs to the command
break;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: A root-level --custom-generator before -c stops this scan, so the explicit config path is ignored and the environment/default config is used. Skip recognized root options and their values before treating an argument as the command name.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/generator-cli/src/app/services/config.service.ts, line 68:

<comment>A root-level `--custom-generator` before `-c` stops this scan, so the explicit config path is ignored and the environment/default config is used. Skip recognized root options and their values before treating an argument as the command name.</comment>

<file context>
@@ -4,6 +4,97 @@ import { COMMANDER_PROGRAM, LOGGER } from '../constants';
+
+    // the command name or an option of the invoked command:
+    // everything from here on belongs to the command
+    break;
+  }
+
</file context>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add option to choose the location of openapitools.json

1 participant