Skip to content

feat: add Deprecated field to flags and commands - #2429

Open
anandghegde wants to merge 1 commit into
urfave:mainfrom
anandghegde:feat/deprecated-flags-commands
Open

anandghegde wants to merge 1 commit into
urfave:mainfrom
anandghegde:feat/deprecated-flags-commands

Conversation

@anandghegde

Copy link
Copy Markdown

What type of PR is this?

  • feature

What this PR does / why we need it:

When a CLI renames a flag or command, there is currently no built-in way to keep the old name working while telling users to switch. Apps have to write their own Action/Before hooks, and those don't cover env-sourced values in a uniform way. cobra has Command.Deprecated and pflag has MarkDeprecated. This PR adds the same thing to urfave/cli, using the same message format:

cmd := &cli.Command{
	Flags: []cli.Flag{
		&cli.StringFlag{Name: "lang", Deprecated: "use --language instead"},
		&cli.StringFlag{Name: "language"},
	},
	Commands: []*cli.Command{
		{Name: "rm", Deprecated: `use "remove" instead`, Action: remove},
		{Name: "remove", Action: remove},
	},
}
$ app --lang es rm task
Command "rm" is deprecated, use "remove" instead
Flag --lang has been deprecated, use --language instead

Changes by file:

  • flag_impl.go, flag_bool_with_inverse.go: new Deprecated string field and a GetDeprecated() method. All FlagBase aliases (StringFlag, IntFlag, TextFlag, ...) get it, and so does BoolWithInverseFlag.
  • flag.go: new optional DeprecatedFlag interface, following the same pattern as RequiredFlag and VisibleFlag, so custom flag types can opt in.
  • command.go: new Command.Deprecated string field, next to Hidden.
  • command_run.go: printDeprecationWarnings runs once for the command that is actually executed, right before ArgValidator/Before/flag actions. It walks the command chain from the root down. For each deprecated command it passes through, it writes a warning to the root ErrWriter. It does the same for each deprecated flag found in that command's set flags. Execution then continues as normal.
  • command_test.go: TestCommand_Deprecated table test that captures ErrWriter, plus a test checking that the deprecated command's action still runs and still sees the flag value. TestJSONExportCommand expectations now include the new deprecated JSON key.
  • docs/v3/examples/flags/advanced.md, docs/v3/examples/subcommands/basics.md: short "Deprecated Flags" / "Deprecated Commands" sections with runnable examples.
  • godoc-current.txt, testdata/godoc-v3.x.txt: regenerated with make generate + make v3approve.

Design choices:

  • Env and other sources warn too. pflag only knows about the command line. In urfave/cli, a value from Sources counts as "set" (IsSet() is true, flag actions run), so a deprecated flag warns whether it was set on the command line or through an env var, file, etc. A flag that just keeps its default value does not warn.
  • Not hidden from help. cobra hides deprecated commands and flags. urfave/cli already has a separate Hidden field, so the two stay independent: set Hidden: true as well if you want the entry hidden. That way setting Deprecated never removes anything from help or completion as a side effect. This is noted in the field docs and in the docs examples. Happy to switch to auto-hiding if maintainers prefer cobra's behavior.
  • When it warns: only when a command actually runs. --help, --version and shell completion print nothing. Parent commands in the chain are checked too, so app old-group sub warns about old-group. A persistent flag that is set on both a parent and a subcommand warns only once.
  • The flag name in the message is the flag's primary Name, with - or -- in front, the same way help output does it (prefixFor). An alias like -o or an inverse form like --no-color reports the primary name.

Which issue(s) this PR fixes:

None filed. This is a feature request, modeled on cobra's Command.Deprecated and pflag's FlagSet.MarkDeprecated.

Special notes for your reviewer:

Deprecated is a new exported field on Command and FlagBase, so the public API grows (see the godoc diff). Nothing changes unless the field is set.

This PR was prepared with help from Claude Code. I reviewed the changes and tested them locally.

Testing

  • make vet passes
  • make test: ok github.com/urfave/cli/v3 coverage: 99.5% of statements. Includes the new TestCommand_Deprecated (12 cases: command, parent command, long/short/alias/inverse-bool flags, env source, persistent flag on a subcommand, set twice warns once, --help does not warn) and TestCommand_DeprecatedStillRuns
  • Checked that the tests catch regressions: they fail with the printDeprecationWarnings call commented out, and the warn-once case fails with the de-duplication removed
  • make lint (goimports) and golangci-lint run ./... (0 issues) pass
  • make check-binary-size: 2.0MB, within the 1.5–2.2MB bounds
  • make gfmrun FLAGS='--walk docs/v3/': error_count=0 example_count=47 (the new examples compile against the local module)
  • make generate, make v3approve: godoc diff contains only the new field, method and interface

Release Notes

Flags and commands can be marked as deprecated by setting `Deprecated` to a message. A warning is printed to `ErrWriter` when a deprecated flag is set or a deprecated command is invoked, and the command still runs.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QBWFWqjbUQGS1c9LsM2bxX

Setting Deprecated on a flag or command prints a one-line warning to the
root ErrWriter when the flag is set (on the command line or from its
Sources) or the command is invoked, and execution continues. The message
format mirrors cobra and pflag:

    Command "old" is deprecated, use "new" instead
    Flag --old-flag has been deprecated, use --new-flag instead

Deprecated flags and commands are not hidden from help; set Hidden for
that.
@anandghegde
anandghegde requested a review from a team as a code owner September 14, 2026 11:29
@anandghegde
anandghegde force-pushed the feat/deprecated-flags-commands branch from 9d64ae6 to a971b40 Compare September 15, 2026 16:40
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