Cli: Create a v1 => v2 migration guide

Created on 9 Nov 2019  ·  22Comments  ·  Source: urfave/cli

View the release notes for guidance on migrating => https://github.com/urfave/cli/releases/tag/v2.0.0

kindocumentation statuconfirmed

Most helpful comment

I kept notes as I did a recent conversion. I think these are going to be the typical "first steps" for people (well... people like me!)

  1. Import string changed
  • OLD: import "github.com/urfave/cli"
  • NEW: import "github.com/urfave/cli/v2"
  1. Flag aliases are done differently.

Change Name: "foo, f to "Name: "foo", Aliases: []string{"f"}`

(Steal the examples in
https://github.com/urfave/cli/issues/921#issuecomment-570763084
...they are better)

  1. Occurrences of []Command have been changed to []*Command

What this means to you:

a. Look for []cli.Command{} and change it to []*cli.Command{}

Example:

  • OLD: var commands = []cli.Command{}
  • NEW: var commands = []*cli.Command{}

Compiler messages you might see:

commands/commands.go:56:30: cannot convert commands (type []cli.Command) to type cli.CommandsByName commands/commands.go:57:15: cannot use commands (type []cli.Command) as type []*cli.Command in assignment

b. If you are building a list of commands, change to pointers

  • OLD: cli.Command{
  • NEW: &cli.Command{

Compiler messages you might see:

./commands.go:32:34: cannot use cli.Command literal (type cli.Command) as type *cli.Command in argument to

  1. cli.Flag is now a list of pointers

What this means to you:

If you make a list of flags, add a & in front of each
item. cli.BoolFlag, cli.StringFlag, etc

  • OLD:
    app.Flags = []cli.Flag{ cli.BoolFlag{

  • NEW:
    app.Flags = []cli.Flag{ &cli.BoolFlag{

Compiler messages you might see:

cli.StringFlag does not implement cli.Flag (Apply method has pointer receiver)

b. appending to a list of commands needs pointers:

  • OLD: commands = append(commands, *c)
  • NEW: commands = append(commands, c)

Compiler messages you might see:

commands/commands.go:28:19: cannot use c (type *cli.Command) as type cli.Command in append

  1. A command's Action: now returns an error
  • OLD: Action: func(c *cli.Context) {
  • NEW: Action: func(c *cli.Context) error {

Compiler messages you might see:

`
./commands.go:35:2: cannot use func literal (type func(*cli.Context)) as type cli.ActionFunc in field value

All 22 comments

Here's a PR where @ffrank and @purpleidea make the v1 => v2 migration https://github.com/purpleidea/mgmt/pull/571

Here's a quirky example of someone moving from the _very very old_ v2 branch, to the latest v1 release https://github.com/sourcegraph/godockerize/pull/10

☝️ I don't think that's the type of migration where's really looking to write documentation for? but it's interesting to look at

Here's another example migration PR https://github.com/google/cloud-print-connector/pull/470

I'm going to work on this as a part of https://github.com/urfave/cli/issues/952

In addition to working on this as a part of https://github.com/urfave/cli/issues/952, I've been progressively updating the v2 release notes => https://github.com/urfave/cli/releases/tag/v2.0.0 to include fine grain migration instructions. I'll add more as I find them!

952 was closed, so I no longer intend on working on this

command line flag migration steps also need to be updated, at the very least to document the way command-line flag aliases now behave.

Previously you could define aliases liek:

cli.StringFlag{
  Name: "config, cfg"
}

however you now need to do

cli.StringFlag{
    Name: "config",
    Aliases: []string{"cfg"},
}

Here is a minimal repro

package main

import (
    "fmt"
    "os"

    // auto register pprof handlers

    _ "net/http/pprof"

    au "github.com/logrusorgru/aurora"
    "github.com/urfave/cli/v2"
)

func main() {
    // generate the actual cli app
    app := newApp()
    // runthe cli app
    if err := app.Run(os.Args); err != nil {
        fmt.Printf(
            "%s %s\n",
            au.Bold(au.Red("error encountered:")),
            au.Red(err.Error()),
        )
        os.Exit(1)
    }
}

func newApp() *cli.App {
    app := cli.NewApp()
    app.Flags = loadFlags()
    return app
}

func loadFlags() []cli.Flag {
    return []cli.Flag{
        &cli.BoolFlag{
            Name:  "bootstrap, bp",
            Usage: "bootstrap against public ipfs",
        },
        &cli.StringFlag{
            Name:  "config, cfg",
            Usage: "load the configuration file at `PATH`",
            Value: "./config.yml",
        },
    }
}

@bonedaddy 👍 I added these lines

  • Removed the ability to specify multiple entries in the Command.Name field

    • when updating, replace Name: "a, b, c" with Name: "a", Aliases: []string{"b", "c"}

@bonedaddy I added these lines

  • Removed the ability to specify multiple entries in the Command.Name field

    • when updating, replace Name: "a, b, c" with Name: "a", Aliases: []string{"b", "c"}

sweet, thank you :rocket:

I kept notes as I did a recent conversion. I think these are going to be the typical "first steps" for people (well... people like me!)

  1. Import string changed
  • OLD: import "github.com/urfave/cli"
  • NEW: import "github.com/urfave/cli/v2"
  1. Flag aliases are done differently.

Change Name: "foo, f to "Name: "foo", Aliases: []string{"f"}`

(Steal the examples in
https://github.com/urfave/cli/issues/921#issuecomment-570763084
...they are better)

  1. Occurrences of []Command have been changed to []*Command

What this means to you:

a. Look for []cli.Command{} and change it to []*cli.Command{}

Example:

  • OLD: var commands = []cli.Command{}
  • NEW: var commands = []*cli.Command{}

Compiler messages you might see:

commands/commands.go:56:30: cannot convert commands (type []cli.Command) to type cli.CommandsByName commands/commands.go:57:15: cannot use commands (type []cli.Command) as type []*cli.Command in assignment

b. If you are building a list of commands, change to pointers

  • OLD: cli.Command{
  • NEW: &cli.Command{

Compiler messages you might see:

./commands.go:32:34: cannot use cli.Command literal (type cli.Command) as type *cli.Command in argument to

  1. cli.Flag is now a list of pointers

What this means to you:

If you make a list of flags, add a & in front of each
item. cli.BoolFlag, cli.StringFlag, etc

  • OLD:
    app.Flags = []cli.Flag{ cli.BoolFlag{

  • NEW:
    app.Flags = []cli.Flag{ &cli.BoolFlag{

Compiler messages you might see:

cli.StringFlag does not implement cli.Flag (Apply method has pointer receiver)

b. appending to a list of commands needs pointers:

  • OLD: commands = append(commands, *c)
  • NEW: commands = append(commands, c)

Compiler messages you might see:

commands/commands.go:28:19: cannot use c (type *cli.Command) as type cli.Command in append

  1. A command's Action: now returns an error
  • OLD: Action: func(c *cli.Context) {
  • NEW: Action: func(c *cli.Context) error {

Compiler messages you might see:

`
./commands.go:35:2: cannot use func literal (type func(*cli.Context)) as type cli.ActionFunc in field value

Those are good notes, thanks @TomOnTime

Those are good notes, thanks @TomOnTime

Thanks!

If I may be so bold: I think the typical user of urfave/cli would rather have a few conversion tips now than the perfect document in the future. I would be honored if you published my notes as the starting point; people could send PRs with improvements over time.

It turns out that the conversion process is pretty easy, but I had been avoiding it because I didn't know how hard or difficult it would be. Luckily I had some free time this weekend and decided to guess my way through the compiler errors and see if it would "just work". Other people might not be so willing to "just try it". A nudge in the docs would empower people to give it a try.

If I may be so bold: I think the typical user of urfave/cli would rather have a few conversion tips now than the perfect document in the future.

I'll follow up on this sometime soon 👍

I'm not sure if this is intentional, a side effect or something I have done wrong but after moving from v1 to v2, flags must come before args ie:

This will work

cli hello --shout rick

This will not

cli hello rick --shout

If this is intentional i couldn't find it documented anywhere and is a major gotcha for migrating.

I took a swing at this. See https://github.com/urfave/cli/pull/1098

@nodefortytwo that change isn't specific to V2, it's an issue where the behavior flipped back and forth in V1 too. Here's an example of me changing some V1 code for this problem => https://github.com/urfave/cli/pull/872

@nodefortytwo if you have capacity, I would welcome contributors for more test cases here! It's never been 100% clear what the ideal behavior should be, which is why the behavior has shifted over time.

In cli.StringFlag type,
V1 expression is EnvVar: "XXXXX"
V2 expression is EnvVars: []string{"XXXXX"}

@guo1017138 ... please take a look at https://github.com/urfave/cli/pull/1137

Another change to document - c.GlobalBool(), c.GlobalString() and friends seem to have gone.

cli.Context.Parent() is gone, and seems to be replaced by cli.Context.Lineage

Was this page helpful?
0 / 5 - 0 ratings

Related issues

VirrageS picture VirrageS  ·  17Comments

nikkolasg picture nikkolasg  ·  16Comments

slantview picture slantview  ·  44Comments

skillful-alex picture skillful-alex  ·  15Comments

ansrivas picture ansrivas  ·  17Comments