Skip to content

Bug: --version flag conflicts with custom options in makeConfig #13

@konard

Description

@konard

Summary

The makeConfig function fails to parse custom --version options because yargs' built-in --version flag overrides user-defined options. This causes CLI arguments to return false instead of the provided value.

Environment

  • lino-arguments version: latest (from unpkg)
  • Node.js version: v20.19.6
  • Platform: Linux, macOS, GitHub Actions (CI)

Minimal Reproduction

#!/usr/bin/env node

// Load use-m dynamically
const { use } = eval(
  await (await fetch('https://unpkg.com/use-m/use.js')).text()
);

// Import lino-arguments
const { makeConfig } = await use('lino-arguments');

console.log('process.argv:', process.argv);

// Parse CLI arguments using lino-arguments
const config = makeConfig({
  yargs: ({ yargs, getenv }) =>
    yargs.option('version', {
      type: 'string',
      default: getenv('VERSION', ''),
      describe: 'Version number (e.g., 1.0.0)',
    }),
});

console.log('config.version:', JSON.stringify(config.version));

if (config.version === false) {
  console.error('❌ BUG: --version returned false instead of the value');
  process.exit(1);
} else {
  console.log('✅ --version parsed correctly:', config.version);
}

Run the reproduction:

node minimal-repro.mjs --version "1.0.0"

Expected Output:

process.argv: [ '/usr/bin/node', '/path/to/minimal-repro.mjs', '--version', '1.0.0' ]
config.version: "1.0.0"
✅ --version parsed correctly: 1.0.0

Actual Output:

process.argv: [ '/usr/bin/node', '/path/to/minimal-repro.mjs', '--version', '1.0.0' ]
config.version: false
❌ BUG: --version returned false instead of the value

Root Cause

In src/index.js, the makeConfig function creates two yargs instances:

  1. Initial parse (lines 337-345): Disables built-in help/version with .help(false) and .version(false)
  2. Final parse (lines 360-373): Does NOT disable built-in help/version ⚠️

The second yargs instance (which produces the final result) has yargs' built-in --version flag enabled, which conflicts with custom --version options.

Relevant code (src/index.js:360-373):

// Step 5: Configure yargs with user options + getenv helper
const yargsInstance = yargs(hideBin(argv)).option('configuration', {
  type: 'string',
  describe: 'Path to configuration .lenv file',
  alias: 'c',
});
// ⬆️ Missing .version(false) here (it was present in initial parse at line 344)

// Pass getenv helper if enabled
const getenvHelper = getenvEnabled ? getenv : () => '';
const configuredYargs = yargsConfigFn
  ? yargsConfigFn({ yargs: yargsInstance, getenv: getenvHelper })
  : yargsInstance;

// Step 6: Parse final configuration (CLI args have highest priority)
const parsed = configuredYargs.parseSync();

Impact

This bug affects any script using lino-arguments with a custom --version option:

  • ❌ Release scripts fail in CI (like GitHub Actions)
  • ❌ CLI tools can't accept version arguments
  • ❌ Scripts silently get wrong values (false instead of user input)

Real-world example: In the test-anywhere repository, release scripts failed because makeConfig returned version: false instead of "0.8.34" (see issues #116 and #118).

Proposed Fix

Add .version(false) to the final yargs instance to match the initial parse:

  // Step 5: Configure yargs with user options + getenv helper
  const yargsInstance = yargs(hideBin(argv))
+   .version(false)  // Disable built-in version to allow custom --version option
    .option('configuration', {
      type: 'string',
      describe: 'Path to configuration .lenv file',
      alias: 'c',
    });

Why this fix works:

  1. ✅ Consistency: Both initial and final parse behave the same way
  2. ✅ User control: Users can define their own --version and --help options if needed
  3. ✅ Backwards compatible: Doesn't break existing code
  4. ✅ Simple: No complex detection logic required

Optional: Also add .help(false) to prevent the same issue with --help:

  const yargsInstance = yargs(hideBin(argv))
+   .help(false)     // Disable built-in help to allow custom --help option
+   .version(false)  // Disable built-in version to allow custom --version option
    .option('configuration', {

Workaround

Until this is fixed, avoid using --version and --help as option names. Use alternatives like --ver, --release-version, etc.

Test Case

After applying the fix, this test should pass:

import { makeConfig } from 'lino-arguments';
import assert from 'assert';

const config = makeConfig({
  argv: ['node', 'test.mjs', '--version', '1.0.0'],
  yargs: ({ yargs }) =>
    yargs.option('version', { type: 'string', default: '' }),
});

assert.strictEqual(config.version, '1.0.0');
console.log('✅ Test passed');

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions