Skip to content

Feat/genspec cli - #111

Merged
fredbi merged 8 commits into
go-openapi:masterfrom
fredbi:feat/genspec-cli
Aug 14, 2026
Merged

fredbi merged 8 commits into
go-openapi:masterfrom
fredbi:feat/genspec-cli

Conversation

@fredbi

@fredbi fredbi commented Aug 14, 2026

Copy link
Copy Markdown
Member

Change type

Please select: 🆕 New feature or enhancement|🔧 Bug fix'|📃 Documentation update

Short description

Fixes

Full description

Checklist

  • I have signed all my commits with my name and email (see DCO. This does not require a PGP-signed commit
  • I have rebased and squashed my work, so only one commit remains
  • I have added tests to cover my changes.
  • I have properly enriched go doc comments in code.
  • I have properly documented any breaking change.

fredbi and others added 8 commits August 14, 2026 19:06
The flag-to-option mapping was written per command, and was partial in a
different way each time: genspec-wasi covered every boolean knob but no
filter and no naming option, genspec-tui covered a hand-picked subset,
and neither guard could see what the other was missing. An option added
to the library reached whichever command somebody remembered.

It now lives in internal/cliopts, as tables keyed by the field each entry
writes to, with one guard asserting that no value-typed field of the
options is left unreachable from a command line. Commands register the
whole surface and add only what is their own: where to write, in what
format, how loud.

genspec-wasi consequently gains -include, -exclude, -include-tags,
-exclude-tags, -name-from-tags, -name-concat-budget, -goflags, -gowork
and -goexperiment, which it could not set before. Nothing is renamed and
nothing is dropped, and the document it produces for an unchanged
invocation is byte-identical.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
genspec scans annotated Go source and writes the Swagger 2.0 document it
describes. It is the ordinary native command: every knob the library
takes is a flag, the document goes to standard output or to -output, and
what the scan observed goes to standard error.

Beyond what genspec-wasi already does headlessly, it writes YAML as
readily as JSON, merges its discoveries into an existing document, and
checks what it produced:

- -format json|yaml|auto, where auto reads the extension of -output, so
  -output swagger.yaml is enough to get YAML;
- -input merges into an existing document, for everything a scanner
  cannot know - the host, the security definitions, a hand-written path;
- -validate reports what is wrong with the document against the Swagger
  2.0 schema, after writing it, so an invalid one is still there to read;
- diagnostics are colored when stderr is a terminal, honouring NO_COLOR
  and TERM=dumb, with -quiet, -verbose and -color to say otherwise;
- -fail-on gives the exit status a threshold, covering what -validate
  found as well as what the scan observed, since a reader meets them as
  one stream. It defaults to never.

The exit status distinguishes a failed scan, an unusable command line, a
tripped threshold and an invalid document, so a pipeline can tell them
apart. Its own module, as genspec-tui is, so that a colored logger and a
YAML writer stay out of the library's dependencies.

The library is required at a version that will carry internal/cliopts,
where the shared flag surface lives; until that release the module builds
through the workspace, which is how CI tests it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
The note lives in cmd/genspec/README.md; a reader arriving through
pkg.go.dev, `go doc`, or the repository's front page meets none of it.
Says the same thing in the package documentation and in the section of
the top-level README that introduces the command.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
Presets a command line from a .codescan.yaml, found by searching upwards
from where the command runs. Keys are grouped into sections, and inside a
section a key is the flag it sets, spelled as on the command line, so the
help text doubles as the file's reference:

    scan:
      workdir: ./api
    emit:
      scan-models: true

Anything typed wins, decided by asking the flag set what it actually saw
- so a flag typed with the value it already had wins too. Everything the
file decides lands through flag.Set, the same path an argument takes, so
a value is parsed and validated once and the file can mean nothing an
argument could not.

The values arrive as a plain map rather than being read here, which is
what lets one contract serve commands with very different dependency
budgets: cliconf.Parse needs only the yaml package the library already
has, and cliconf.YAML satisfies koanf's parser interface structurally,
so a command wanting koanf plugs it in and this package still imports
nothing new.

One file serves the whole family. A section a command does not know is
skipped and reported rather than refused, so another command's settings
may sit beside it; a key inside a section it does know must name one of
its flags, which is what makes a typo an error rather than a setting
that quietly never applied.

Each flag in the shared table now declares the section it is addressed
in, guarded the same way its name is: a flag reachable from no
configuration key is a flag nobody can configure, and nothing else would
have noticed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
Wires the command to the shared configuration contract, through koanf as
its loader. genspec adds two sections of its own to the ones the library
brings - document, for what is read and written, and diagnostics, for how
loud it is - with the same guard behind them: a flag addressed in no
section is one nobody can configure.

The file is read after parsing and before anything reads a flag, so
everything downstream sees one settled command line and needs to know
nothing about where a value came from. Which file was read, and which of
its keys belonged to another command, are reported under -verbose:
reading a configuration file is not news, but a section nobody claimed
is the only sign of a misspelt one.

koanf loads it for what it will be asked for next - environment
variables, a second format, several sources merged - rather than for
reading one YAML file. It is given the bytes directly, since its file
provider carries a filesystem watcher for a file this command reads once.

Also drops the position from a diagnostic that has none: a route omitted
by a tag rule was reported at "file= line=0 column=0", which is nowhere.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
-c is the short form of -config, for the thing done often enough to be
worth two keystrokes. -no-config reads none, whatever is lying around,
which is what a run that has to be reproducible says.

It replaces the "off" value -config used to take. A switch reads as what
it is at a glance where a magic value has to be known, and there is now
one way to say each thing rather than two that could disagree.

The two spellings of the file are stored apart rather than sharing a
variable, so -config and -c naming different files is refused instead of
resolved by whichever the parser read last. Naming a file and asking for
none is refused the same way: there is no reading of it the caller can
have meant, and picking one would be inventing an intention.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
@fredbi
fredbi merged commit 0ff4101 into go-openapi:master Aug 14, 2026
30 checks passed
@fredbi
fredbi deleted the feat/genspec-cli branch August 14, 2026 17:33
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.

1 participant