From 1dfc46f3b025d2cdf326bf5edc33264cfd95b919 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Sat, 25 Jun 2022 15:10:43 +0200 Subject: [PATCH] cmd: implement MigrateFlags as App.Before hook This makes it unnecessary to wrap each action function with utils.MigrateFlags in source code. --- cmd/abigen/main.go | 2 +- cmd/checkpoint-admin/exec.go | 6 ++-- cmd/checkpoint-admin/status.go | 3 +- cmd/clef/main.go | 10 +++--- cmd/devp2p/main.go | 2 ++ cmd/geth/accountcmd.go | 10 +++--- cmd/geth/chaincmd.go | 14 ++++---- cmd/geth/config.go | 2 +- cmd/geth/consolecmd.go | 6 ++-- cmd/geth/dbcmd.go | 28 +++++++-------- cmd/geth/main.go | 7 ++-- cmd/geth/misccmd.go | 10 +++--- cmd/geth/snapshot.go | 14 ++++---- cmd/utils/flags.go | 27 -------------- internal/flags/helpers.go | 64 +++++++++++++++++++++++++++++++--- 15 files changed, 119 insertions(+), 86 deletions(-) diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index cea3fb52f423e..85fdae03105a0 100644 --- a/cmd/abigen/main.go +++ b/cmd/abigen/main.go @@ -96,7 +96,7 @@ func init() { langFlag, aliasFlag, } - app.Action = utils.MigrateFlags(abigen) + app.Action = abigen } func abigen(c *cli.Context) error { diff --git a/cmd/checkpoint-admin/exec.go b/cmd/checkpoint-admin/exec.go index 768b2c620fb1e..cb67d0306d43a 100644 --- a/cmd/checkpoint-admin/exec.go +++ b/cmd/checkpoint-admin/exec.go @@ -49,7 +49,7 @@ var commandDeploy = &cli.Command{ signersFlag, thresholdFlag, }, - Action: utils.MigrateFlags(deploy), + Action: deploy, } var commandSign = &cli.Command{ @@ -63,7 +63,7 @@ var commandSign = &cli.Command{ hashFlag, oracleFlag, }, - Action: utils.MigrateFlags(sign), + Action: sign, } var commandPublish = &cli.Command{ @@ -76,7 +76,7 @@ var commandPublish = &cli.Command{ indexFlag, signaturesFlag, }, - Action: utils.MigrateFlags(publish), + Action: publish, } // deploy deploys the checkpoint registrar contract. diff --git a/cmd/checkpoint-admin/status.go b/cmd/checkpoint-admin/status.go index e0998b45e49a3..bec97aed12bd5 100644 --- a/cmd/checkpoint-admin/status.go +++ b/cmd/checkpoint-admin/status.go @@ -19,7 +19,6 @@ package main import ( "fmt" - "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/urfave/cli/v2" ) @@ -30,7 +29,7 @@ var commandStatus = &cli.Command{ Flags: []cli.Flag{ nodeURLFlag, }, - Action: utils.MigrateFlags(status), + Action: status, } // status fetches the admin list of specified registrar contract. diff --git a/cmd/clef/main.go b/cmd/clef/main.go index 1ba5f7e0d0586..182a67064cc0a 100644 --- a/cmd/clef/main.go +++ b/cmd/clef/main.go @@ -135,7 +135,7 @@ var ( Usage: "Mechanism to test interface between Clef and UI. Requires 'stdio-ui'.", } initCommand = &cli.Command{ - Action: utils.MigrateFlags(initializeSecrets), + Action: initializeSecrets, Name: "init", Usage: "Initialize the signer, generate secret storage", ArgsUsage: "", @@ -148,7 +148,7 @@ The init command generates a master seed which Clef can use to store credentials the rule-engine to work.`, } attestCommand = &cli.Command{ - Action: utils.MigrateFlags(attestFile), + Action: attestFile, Name: "attest", Usage: "Attest that a js-file is to be used", ArgsUsage: "", @@ -165,7 +165,7 @@ Whenever you make an edit to the rule file, you need to use attestation to tell Clef that the file is 'safe' to execute.`, } setCredentialCommand = &cli.Command{ - Action: utils.MigrateFlags(setCredential), + Action: setCredential, Name: "setpw", Usage: "Store a credential for a keystore file", ArgsUsage: "
", @@ -178,7 +178,7 @@ Clef that the file is 'safe' to execute.`, The setpw command stores a password for a given address (keyfile). `} delCredentialCommand = &cli.Command{ - Action: utils.MigrateFlags(removeCredential), + Action: removeCredential, Name: "delpw", Usage: "Remove a credential for a keystore file", ArgsUsage: "
", @@ -191,7 +191,7 @@ The setpw command stores a password for a given address (keyfile). The delpw command removes a password for a given address (keyfile). `} newAccountCommand = &cli.Command{ - Action: utils.MigrateFlags(newAccount), + Action: newAccount, Name: "newaccount", Usage: "Create a new account", ArgsUsage: "", diff --git a/cmd/devp2p/main.go b/cmd/devp2p/main.go index 08de142d55600..51b9fdb76119e 100644 --- a/cmd/devp2p/main.go +++ b/cmd/devp2p/main.go @@ -22,6 +22,7 @@ import ( "path/filepath" "github.com/ethereum/go-ethereum/internal/debug" + "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/params" "github.com/urfave/cli/v2" @@ -44,6 +45,7 @@ func init() { // Set up the CLI app. app.Flags = append(app.Flags, debug.Flags...) app.Before = func(ctx *cli.Context) error { + flags.MigrateGlobalFlags(ctx) return debug.Setup(ctx) } app.After = func(ctx *cli.Context) error { diff --git a/cmd/geth/accountcmd.go b/cmd/geth/accountcmd.go index 55c1c18c3e624..5158b7606cdef 100644 --- a/cmd/geth/accountcmd.go +++ b/cmd/geth/accountcmd.go @@ -45,7 +45,7 @@ passwordfile as argument containing the wallet password in plaintext.`, Name: "import", Usage: "Import Ethereum presale wallet", ArgsUsage: "", - Action: utils.MigrateFlags(importWallet), + Action: importWallet, Flags: []cli.Flag{ utils.DataDirFlag, utils.KeyStoreDirFlag, @@ -89,7 +89,7 @@ Make sure you backup your keys regularly.`, { Name: "list", Usage: "Print summary of existing accounts", - Action: utils.MigrateFlags(accountList), + Action: accountList, Flags: []cli.Flag{ utils.DataDirFlag, utils.KeyStoreDirFlag, @@ -100,7 +100,7 @@ Print a short summary of all accounts`, { Name: "new", Usage: "Create a new account", - Action: utils.MigrateFlags(accountCreate), + Action: accountCreate, Flags: []cli.Flag{ utils.DataDirFlag, utils.KeyStoreDirFlag, @@ -125,7 +125,7 @@ password to file or expose in any other way. { Name: "update", Usage: "Update an existing account", - Action: utils.MigrateFlags(accountUpdate), + Action: accountUpdate, ArgsUsage: "
", Flags: []cli.Flag{ utils.DataDirFlag, @@ -154,7 +154,7 @@ changing your password is only possible interactively. { Name: "import", Usage: "Import a private key into a new account", - Action: utils.MigrateFlags(accountImport), + Action: accountImport, Flags: []cli.Flag{ utils.DataDirFlag, utils.KeyStoreDirFlag, diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 754ad6245f3a0..6914e1aa2da90 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -43,7 +43,7 @@ import ( var ( initCommand = &cli.Command{ - Action: utils.MigrateFlags(initGenesis), + Action: initGenesis, Name: "init", Usage: "Bootstrap and initialize a new genesis block", ArgsUsage: "", @@ -56,7 +56,7 @@ participating. It expects the genesis file as argument.`, } dumpGenesisCommand = &cli.Command{ - Action: utils.MigrateFlags(dumpGenesis), + Action: dumpGenesis, Name: "dumpgenesis", Usage: "Dumps genesis block JSON configuration to stdout", ArgsUsage: "", @@ -65,7 +65,7 @@ It expects the genesis file as argument.`, The dumpgenesis command dumps the genesis block configuration in JSON format to stdout.`, } importCommand = &cli.Command{ - Action: utils.MigrateFlags(importChain), + Action: importChain, Name: "import", Usage: "Import a blockchain file", ArgsUsage: " ( ... ) ", @@ -100,7 +100,7 @@ If only one file is used, import error will result in failure. If several files processing will proceed even if an individual RLP-file import failure occurs.`, } exportCommand = &cli.Command{ - Action: utils.MigrateFlags(exportChain), + Action: exportChain, Name: "export", Usage: "Export blockchain into file", ArgsUsage: " [ ]", @@ -116,7 +116,7 @@ if already existing. If the file ends with .gz, the output will be gzipped.`, } importPreimagesCommand = &cli.Command{ - Action: utils.MigrateFlags(importPreimages), + Action: importPreimages, Name: "import-preimages", Usage: "Import the preimage database from an RLP stream", ArgsUsage: "", @@ -130,7 +130,7 @@ It's deprecated, please use "geth db import" instead. `, } exportPreimagesCommand = &cli.Command{ - Action: utils.MigrateFlags(exportPreimages), + Action: exportPreimages, Name: "export-preimages", Usage: "Export the preimage database into an RLP stream", ArgsUsage: "", @@ -144,7 +144,7 @@ It's deprecated, please use "geth db export" instead. `, } dumpCommand = &cli.Command{ - Action: utils.MigrateFlags(dump), + Action: dump, Name: "dump", Usage: "Dump a specific block from storage", ArgsUsage: "[? | ]", diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 2c3e68845fda3..a415aeabd2e9e 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -45,7 +45,7 @@ import ( var ( dumpConfigCommand = &cli.Command{ - Action: utils.MigrateFlags(dumpConfig), + Action: dumpConfig, Name: "dumpconfig", Usage: "Show configuration values", ArgsUsage: "", diff --git a/cmd/geth/consolecmd.go b/cmd/geth/consolecmd.go index 5a9233b794dde..a62b6a6ad5928 100644 --- a/cmd/geth/consolecmd.go +++ b/cmd/geth/consolecmd.go @@ -31,7 +31,7 @@ var ( consoleFlags = []cli.Flag{utils.JSpathFlag, utils.ExecFlag, utils.PreloadJSFlag} consoleCommand = &cli.Command{ - Action: utils.MigrateFlags(localConsole), + Action: localConsole, Name: "console", Usage: "Start an interactive JavaScript environment", Flags: utils.GroupFlags(nodeFlags, rpcFlags, consoleFlags), @@ -42,7 +42,7 @@ See https://geth.ethereum.org/docs/interface/javascript-console.`, } attachCommand = &cli.Command{ - Action: utils.MigrateFlags(remoteConsole), + Action: remoteConsole, Name: "attach", Usage: "Start an interactive JavaScript environment (connect to node)", ArgsUsage: "[endpoint]", @@ -55,7 +55,7 @@ This command allows to open a console on a running geth node.`, } javascriptCommand = &cli.Command{ - Action: utils.MigrateFlags(ephemeralConsole), + Action: ephemeralConsole, Name: "js", Usage: "(DEPRECATED) Execute the specified JavaScript files", ArgsUsage: " [jsfile...]", diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go index 88da460c0c075..be994def34d74 100644 --- a/cmd/geth/dbcmd.go +++ b/cmd/geth/dbcmd.go @@ -45,7 +45,7 @@ import ( var ( removedbCommand = &cli.Command{ - Action: utils.MigrateFlags(removeDB), + Action: removeDB, Name: "removedb", Usage: "Remove blockchain and state databases", ArgsUsage: "", @@ -74,7 +74,7 @@ Remove blockchain and state databases`, }, } dbInspectCmd = &cli.Command{ - Action: utils.MigrateFlags(inspect), + Action: inspect, Name: "inspect", ArgsUsage: " ", Flags: utils.GroupFlags([]cli.Flag{ @@ -84,7 +84,7 @@ Remove blockchain and state databases`, Description: `This commands iterates the entire database. If the optional 'prefix' and 'start' arguments are provided, then the iteration is limited to the given subset of data.`, } dbCheckStateContentCmd = &cli.Command{ - Action: utils.MigrateFlags(checkStateContent), + Action: checkStateContent, Name: "check-state-content", ArgsUsage: "", Flags: utils.GroupFlags(utils.NetworkFlags, utils.DatabasePathFlags), @@ -94,7 +94,7 @@ For each trie node encountered, it checks that the key corresponds to the keccak a data corruption.`, } dbStatCmd = &cli.Command{ - Action: utils.MigrateFlags(dbStats), + Action: dbStats, Name: "stats", Usage: "Print leveldb statistics", Flags: utils.GroupFlags([]cli.Flag{ @@ -102,7 +102,7 @@ a data corruption.`, }, utils.NetworkFlags, utils.DatabasePathFlags), } dbCompactCmd = &cli.Command{ - Action: utils.MigrateFlags(dbCompact), + Action: dbCompact, Name: "compact", Usage: "Compact leveldb database. WARNING: May take a very long time", Flags: utils.GroupFlags([]cli.Flag{ @@ -115,7 +115,7 @@ WARNING: This operation may take a very long time to finish, and may cause datab corruption if it is aborted during execution'!`, } dbGetCmd = &cli.Command{ - Action: utils.MigrateFlags(dbGet), + Action: dbGet, Name: "get", Usage: "Show the value of a database key", ArgsUsage: "", @@ -125,7 +125,7 @@ corruption if it is aborted during execution'!`, Description: "This command looks up the specified database key from the database.", } dbDeleteCmd = &cli.Command{ - Action: utils.MigrateFlags(dbDelete), + Action: dbDelete, Name: "delete", Usage: "Delete a database key (WARNING: may corrupt your database)", ArgsUsage: "", @@ -136,7 +136,7 @@ corruption if it is aborted during execution'!`, WARNING: This is a low-level operation which may cause database corruption!`, } dbPutCmd = &cli.Command{ - Action: utils.MigrateFlags(dbPut), + Action: dbPut, Name: "put", Usage: "Set the value of a database key (WARNING: may corrupt your database)", ArgsUsage: " ", @@ -147,7 +147,7 @@ WARNING: This is a low-level operation which may cause database corruption!`, WARNING: This is a low-level operation which may cause database corruption!`, } dbGetSlotsCmd = &cli.Command{ - Action: utils.MigrateFlags(dbDumpTrie), + Action: dbDumpTrie, Name: "dumptrie", Usage: "Show the storage key/values of a given storage trie", ArgsUsage: " ", @@ -157,7 +157,7 @@ WARNING: This is a low-level operation which may cause database corruption!`, Description: "This command looks up the specified database key from the database.", } dbDumpFreezerIndex = &cli.Command{ - Action: utils.MigrateFlags(freezerInspect), + Action: freezerInspect, Name: "freezer-index", Usage: "Dump out the index of a given freezer type", ArgsUsage: " ", @@ -167,7 +167,7 @@ WARNING: This is a low-level operation which may cause database corruption!`, Description: "This command displays information about the freezer index.", } dbImportCmd = &cli.Command{ - Action: utils.MigrateFlags(importLDBdata), + Action: importLDBdata, Name: "import", Usage: "Imports leveldb-data from an exported RLP dump.", ArgsUsage: " has .gz suffix, gzip compression will be used.", ArgsUsage: " ", @@ -187,7 +187,7 @@ WARNING: This is a low-level operation which may cause database corruption!`, Description: "Exports the specified chain data to an RLP encoded stream, optionally gzip-compressed.", } dbMetadataCmd = &cli.Command{ - Action: utils.MigrateFlags(showMetaData), + Action: showMetaData, Name: "metadata", Usage: "Shows metadata about the chain status.", Flags: utils.GroupFlags([]cli.Flag{ @@ -196,7 +196,7 @@ WARNING: This is a low-level operation which may cause database corruption!`, Description: "Shows metadata about the chain status.", } dbMigrateFreezerCmd = &cli.Command{ - Action: utils.MigrateFlags(freezerMigrate), + Action: freezerMigrate, Name: "freezer-migrate", Usage: "Migrate legacy parts of the freezer. (WARNING: may take a long time)", ArgsUsage: "", diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 414b0593e1d01..3c7e1a311d9d0 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -241,13 +241,16 @@ func init() { } sort.Sort(cli.CommandsByName(app.Commands)) - app.Flags = utils.GroupFlags(nodeFlags, + app.Flags = utils.GroupFlags( + nodeFlags, rpcFlags, consoleFlags, debug.Flags, - metricsFlags) + metricsFlags, + ) app.Before = func(ctx *cli.Context) error { + flags.MigrateGlobalFlags(ctx) return debug.Setup(ctx) } app.After = func(ctx *cli.Context) error { diff --git a/cmd/geth/misccmd.go b/cmd/geth/misccmd.go index 24c3f57367283..efbd74d2d1ea2 100644 --- a/cmd/geth/misccmd.go +++ b/cmd/geth/misccmd.go @@ -43,7 +43,7 @@ var ( runtime.GOOS, runtime.GOARCH, runtime.Version()), } makecacheCommand = &cli.Command{ - Action: utils.MigrateFlags(makecache), + Action: makecache, Name: "makecache", Usage: "Generate ethash verification cache (for testing)", ArgsUsage: " ", @@ -55,7 +55,7 @@ Regular users do not need to execute it. `, } makedagCommand = &cli.Command{ - Action: utils.MigrateFlags(makedag), + Action: makedag, Name: "makedag", Usage: "Generate ethash mining DAG (for testing)", ArgsUsage: " ", @@ -67,7 +67,7 @@ Regular users do not need to execute it. `, } versionCommand = &cli.Command{ - Action: utils.MigrateFlags(version), + Action: version, Name: "version", Usage: "Print version numbers", ArgsUsage: " ", @@ -76,7 +76,7 @@ The output of this command is supposed to be machine-readable. `, } versionCheckCommand = &cli.Command{ - Action: utils.MigrateFlags(versionCheck), + Action: versionCheck, Flags: []cli.Flag{ VersionCheckUrlFlag, VersionCheckVersionFlag, @@ -90,7 +90,7 @@ and displays information about any security vulnerabilities that affect the curr `, } licenseCommand = &cli.Command{ - Action: utils.MigrateFlags(license), + Action: license, Name: "license", Usage: "Display license information", ArgsUsage: " ", diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index 4a04019067834..82206b58b8eac 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -55,7 +55,7 @@ var ( Name: "prune-state", Usage: "Prune stale ethereum state data based on the snapshot", ArgsUsage: "", - Action: utils.MigrateFlags(pruneState), + Action: pruneState, Flags: utils.GroupFlags([]cli.Flag{ utils.CacheTrieJournalFlag, utils.BloomFilterSizeFlag, @@ -79,7 +79,7 @@ the trie clean cache with default directory will be deleted. Name: "verify-state", Usage: "Recalculate state hash based on the snapshot for verification", ArgsUsage: "", - Action: utils.MigrateFlags(verifyState), + Action: verifyState, Flags: utils.GroupFlags(utils.NetworkFlags, utils.DatabasePathFlags), Description: ` geth snapshot verify-state @@ -92,7 +92,7 @@ In other words, this command does the snapshot to trie conversion. Name: "check-dangling-storage", Usage: "Check that there is no 'dangling' snap storage", ArgsUsage: "", - Action: utils.MigrateFlags(checkDanglingStorage), + Action: checkDanglingStorage, Flags: utils.GroupFlags(utils.NetworkFlags, utils.DatabasePathFlags), Description: ` geth snapshot check-dangling-storage traverses the snap storage @@ -103,7 +103,7 @@ data, and verifies that all snapshot storage data has a corresponding account. Name: "inspect-account", Usage: "Check all snapshot layers for the a specific account", ArgsUsage: "
", - Action: utils.MigrateFlags(checkAccount), + Action: checkAccount, Flags: utils.GroupFlags(utils.NetworkFlags, utils.DatabasePathFlags), Description: ` geth snapshot inspect-account
checks all snapshot layers and prints out @@ -114,7 +114,7 @@ information about the specified address. Name: "traverse-state", Usage: "Traverse the state with given root hash and perform quick verification", ArgsUsage: "", - Action: utils.MigrateFlags(traverseState), + Action: traverseState, Flags: utils.GroupFlags(utils.NetworkFlags, utils.DatabasePathFlags), Description: ` geth snapshot traverse-state @@ -129,7 +129,7 @@ It's also usable without snapshot enabled. Name: "traverse-rawstate", Usage: "Traverse the state with given root hash and perform detailed verification", ArgsUsage: "", - Action: utils.MigrateFlags(traverseRawState), + Action: traverseRawState, Flags: utils.GroupFlags(utils.NetworkFlags, utils.DatabasePathFlags), Description: ` geth snapshot traverse-rawstate @@ -145,7 +145,7 @@ It's also usable without snapshot enabled. Name: "dump", Usage: "Dump a specific block from storage (same as 'geth dump' but using snapshots)", ArgsUsage: "[? | ]", - Action: utils.MigrateFlags(dumpState), + Action: dumpState, Flags: utils.GroupFlags([]cli.Flag{ utils.ExcludeCodeFlag, utils.ExcludeStorageFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 6feef7b099bf7..5cebaba43ecfd 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -2209,30 +2209,3 @@ func MakeConsolePreloads(ctx *cli.Context) []string { } return preloads } - -// MigrateFlags sets the global flag from a local flag when it's set. -// This is a temporary function used for migrating old command/flags to the -// new format. -// -// e.g. geth account new --keystore /tmp/mykeystore --lightkdf -// -// is equivalent after calling this method with: -// -// geth --keystore /tmp/mykeystore --lightkdf account new -// -// This allows the use of the existing configuration functionality. -// When all flags are migrated this function can be removed and the existing -// configuration functionality must be changed that is uses local flags -func MigrateFlags(action func(ctx *cli.Context) error) func(*cli.Context) error { - return func(ctx *cli.Context) error { - for _, name := range ctx.FlagNames() { - for _, parent := range ctx.Lineage()[1:] { - if parent.IsSet(name) { - ctx.Set(name, parent.String(name)) - break - } - } - } - return action(ctx) - } -} diff --git a/internal/flags/helpers.go b/internal/flags/helpers.go index 1ce73f0c91061..4f907734c8c30 100644 --- a/internal/flags/helpers.go +++ b/internal/flags/helpers.go @@ -24,10 +24,6 @@ import ( "github.com/urfave/cli/v2" ) -func init() { - cli.FlagStringer = FlagString -} - // NewApp creates an app with sane defaults. func NewApp(gitCommit, gitDate, usage string) *cli.App { app := cli.NewApp() @@ -35,9 +31,69 @@ func NewApp(gitCommit, gitDate, usage string) *cli.App { app.Version = params.VersionWithCommit(gitCommit, gitDate) app.Usage = usage app.Copyright = "Copyright 2013-2022 The go-ethereum Authors" + app.Before = func(ctx *cli.Context) error { + MigrateGlobalFlags(ctx) + return nil + } return app } +var migrationApplied = map[*cli.Command]struct{}{} + +// MigrateGlobalFlags makes all global flag values available in the +// context. This should be called as early as possible in app.Before. +// +// Example: +// +// geth account new --keystore /tmp/mykeystore --lightkdf +// +// is equivalent after calling this method with: +// +// geth --keystore /tmp/mykeystore --lightkdf account new +// +// i.e. in the subcommand Action function of 'account new', ctx.Bool("lightkdf) +// will return true even if --lightkdf is set as a global option. +// +// This function may become unnecessary when https://github.com/urfave/cli/pull/1245 is merged. +func MigrateGlobalFlags(ctx *cli.Context) { + var iterate func(cs []*cli.Command, fn func(*cli.Command)) + iterate = func(cs []*cli.Command, fn func(*cli.Command)) { + for _, cmd := range cs { + if _, ok := migrationApplied[cmd]; ok { + continue + } + migrationApplied[cmd] = struct{}{} + fn(cmd) + iterate(cmd.Subcommands, fn) + } + } + + // This iterates over all commands and wraps their action function. + iterate(ctx.App.Commands, func(cmd *cli.Command) { + action := cmd.Action + cmd.Action = func(ctx *cli.Context) error { + doMigrateFlags(ctx) + return action(ctx) + } + }) +} + +func doMigrateFlags(ctx *cli.Context) { + for _, name := range ctx.FlagNames() { + for _, parent := range ctx.Lineage()[1:] { + if parent.IsSet(name) { + fmt.Println("set in parent:", name) + ctx.Set(name, parent.String(name)) + break + } + } + } +} + +func init() { + cli.FlagStringer = FlagString +} + // FlagString prints a single flag in help. func FlagString(f cli.Flag) string { df, ok := f.(cli.DocGenerationFlag)