Flyctl Versions, Autoupdating, and the CLI Apocalypse

Flyctl has always been more than just a wrapper for API calls. That said, until recently, you could easily get by with very infrequent updates. With the switch from Nomad-based orchestration to Apps V2 – essentially an entire rework of the platform from the ground up – most of the smarts of our PaaS deployments have been moved from the server into the CLI.

This is great! It means we can iterate very fast, it means that our infrastructure can be so much simpler (and therefore more reliable), and it also means that you could theoretically add custom features to the platform – such as deploy strategies – through a fork of the CLI. (by the way: flyctl is open source!)

The problem with this is that if you don’t have an up-to-date flyctl, you could be missing crucial bug fixes and updates to platform features. I think everyone’s clicked the “ignore OS update” button a couple times, but old versions of flyctl are a liability when production services are at stake. We want to make sure that people don’t accidentally break their apps (that’s kind of the nightmare scenario here), and we’ve seen old versions of flyctl subtly break things time and time again.

All of that considered, here’s the gameplan to try to make this situation better:

  1. Flyctl autoupdate. Some have wanted this for ages, some hate the very idea of it! It will be enabled by default, but it will be optional.
    • Done!
  2. You’ll start to get error messages from the API when your flyctl is too old.
  3. The current target is roughly: two weeks after a significant breaking change. We’ll probably adjust this goal over time, but the real goal is to balance reliability with not being a pain.

Please note: none of this will affect people using Fly.io APIs directly. We’ll only check version info for user agents beginning with fly-cli.

For those of you who want full transparency, here’s the long-term roadmap, but it’s still very much subject to change:

  • As of flyctl 0.1.51: Autoupdating
    • This will be configurable with fly settings autoupdate, and will be automatically disabled if it seems like flyctl is running in a CI environment.
  • Monday, Jul 10: the API will start erroring on flyctl versions below 0.1.20, requiring people to update to a more recent build of flyctl. There are a few major bugs present in the 0.1.1x series, for example, which would be quite frustrating to learn about by breaking production services.
  • Long-term: flyctl will start checking against the API to see if it’s significantly outdated, meaning the client itself will determine its status. This will allow us more fine-grained controls, such as yanking versions that have serious issues.
  • Roughly two weeks after: the API will start requiring the version of flyctl that checks its own version. That gets everyone on a version of flyctl that’s smart enough to tell when it is too far out of date.

The goal throughout this process is to prevent frustrating experiences and surprising issues. If you have any concerns or feedback about this effort, please let us know in this thread! We want to make sure that this goes smoothly and doesn’t cause too many headaches.

13 Likes

Quick update: autoupdating just got merged into flyctl.

Flyctl 0.1.51 will have automatic updating. Here’s what this looks like:

  • macOS and Linux

    updates will happen in the background. When you run any command, if flyctl detects that it’s out of date, it’ll run the updater in the background. (this is a separate process, so it won’t hang your terminal or anything!)

  • Windows

    I couldn’t settle on a similar background update system for Windows that didn’t compromise the UX. Updating on Windows ends up causing a UAC prompt, and randomly agreeing to (or asking people to agree to) unexpected UAC prompts is a horrible idea, so I just scrapped it for Windows.
    I don’t think it’s impossible to solve this, but that’s a problem for another time.

  • For all supported platforms

    If your flyctl is severely out of date (currently that’s defined as 5 versions out-of-date), it’ll update before executing your command, then relaunch on the newer flyctl. That way, if you try to do something on an old computer sitting around and we’ve completely changed the internal API or something, it should still Just Work.

If you absolutely hate autoupdating, there’s an escape hatch: fly settings autoupdate disable

If something goes wrong, or if you have any other feedback, please post about it in this thread or on the Github Issues page for flyctl. I hope this makes it a bit easier to keep up with bugfixes and platform features :slight_smile:

4 Likes

Hey there,

It’s a neat feature in terms of functionally although personally I stopped updating solely because there were so many updates. I felt I spent more time upgrading flyctl than actually trying to do the task I was trying to do unlike say; Hugo where I happily upgrade because they’re released in semver batches :sweat_smile:

I can understand the need to continually ship patches given the CLI is evolving alongside the API and all that though.

Apparently I hit this new functionality but it puts my instance of flyctl into an infinite upgrade loop, presumably because some of my PATHing isn’t quite correct.

It’d be nice if the upgrade message had a note of the proper paths to set up. I imagine some users may have installed flyctl a long time ago and have forgotten where it might live or what is expected to be set up.

Thanks!

1 Like

oh no! I’m so sorry it broke like this. I’m going to ship some fixes to prevent this from happening in the future, but in the meantime, could you provide the output of which fly and which flyctl? (feel free to redact anything personal that might show up in these) I’d like to figure out why it detected homebrew at first, then switched to updating using our installer script (which is what caused the loop in this case).

You should be able to disable autoupdate with FLY_NO_UPDATE_CHECK=1 fly settings autoupdate disable while we work on a fix here. Sorry for the trouble!

2 Likes

Hey Allison,

It’s no biggie! Having attempted to write some auto updater stuff myself, it’s never as straight forward as it seems from the outside :sweat:

Here’s the commands you were after as well as the output of my PATH variable

Invoking ~/.fly/bin/flyctl directly works as expected but without it in my PATH, the Homebrew version presumably just sees the version has failed to change and keeps redownloading.

It’s worth noting that the first call is to brew upgrade flyctl but I’m guessing brew update is missing here as (to my knowledge) brew upgrade doesn’t fetch new manifest files. It can only upgrade to versions that it currently knows about so it believes that the latest version is whatever is installed.

Manually running brew update and then invoking fly means the upgrade process completes as expected however but not all users may be aware that these things are related of course.

Personally I’d be a bit wary of running a brew update for the user given this can spiral into minutes long upgrade processes with older Homebrew versions “helpfully” running a full upgrade. That said, I think they changed that behaviour in newer versions?

I dunno, none of these problems are easy so it’s all tradeoffs really

Thanks!

2 Likes

you’re telling me! relaunching running executables is always a bit of a nightmare haha

I’m pretty sure (but I could be wrong, for sure!) that the homebrew updater only detects updates based on the local homebrew cache. (I was wrong, lol) There’s a whole rube goldberg machine of subtle things that all had to go just right to cause this to happen.
I think what happened in this case is:

  1. we call into homebrew to upgrade
  2. then relaunch the executable we get from realpath(argv[0])
  3. so I think we ended up with the resolved binary path, which for homebrew would be /opt/homebrew/Cellar/flyctl/<version>/bin/flyctl
    • which is outside of the path /opt/homebrew/bin
  4. that means our updater no longer realizes we’re running from homebrew, so it uses the script-based update method
  5. we’re still running from a different path than ~/.fly, though, so those updates never apply either.

My “quick fix” here is to just detect if we’re running from $(brew --prefix)/bin, and if so, our relaunch path will just be $(brew --prefix)/bin/flyctl. Hopefully that should be enough to fix this specific issue.

It’s difficult to test the Homebrew update code (because it relies on there actually being updates through homebrew!), so sometimes things like this break through. Hopefully the updater can stabilize soon and we can stop touching it :slight_smile:

1 Like

This thread has already helped me out! I ran into the same symptom without homebrew involved; my team uses nixpkgs to keep our dependencies in sync (currently pinned to 2de8efefb6ce7f5e4e75bdf57376a96555986841), and we encountered the same self-update loop in 0.1.53.

Setting the environment variable to skip self-updating worked like a charm to unblock us in this case, and if it will eventually error out when our CLI version is incompatible, I think that’s perfectly acceptable for us.

We should definitely add “flyctl not in homebrew prefix or ~/.fly” to our criteria for disabling autoupdate. Great catch!

Glad the environment variable worked in the meantime :smile:

What if you make breaking API/CLI changes? How do we know about this?

Ideally I’m fine with autoupdate only if there’s no major/breaking changes to the CLI commands. Because getting breaking changes suddenly can break apps that use it in their CI pipeline

1 Like

Sorry for the delay, I was out of office for a bit!

As far as I know, we don’t like to commit to maintaining command compatibility between versions. We try not to break existing commands, of course, but the flexibility is really important for shipping things as quickly as we do :slight_smile:

If this is something you need, we encourage you to disable autoupdating via fly settings autoupdate disable (just make sure to update every now and then!)

Note, however, that if you’re specifically concerned about how this affects CI, it shouldn’t affect that at all. We disable autoupdating if we detect any of a few common CI environment variables, but if you want to be extra sure, you can set FLY_NO_UPDATE_CHECK=1 in your CI config.

2 Likes

Hm, how about flyctl changelog (if something like that does not exist already) shows changelog / bug fixes per version?

1 Like

@allison Not sure if I maybe should create a new topic / feature request about this, but:

I would really like the flyctl release log to be a bit more sorted, and also adhere to something resembling semver (major, minor/feature, patch/fix).

In the best of worlds the CLI update would just aggregate what has changed from version A to version B in these categories but that may be wishful thinking. But a release log where not all things are just mixed should be doable imho.

1 Like

Flyctl’s versioning system is being worked on behind the scenes, although I can’t guarantee any improvements to the update logs

The best way to learn about new changes in Fly.io is to follow the Fresh Produce category - flyctl is just one piece of the puzzle.

1 Like

fwiw:

I needed to get a hotfix out and flyctl chose this moment to update. Also, I couldn’t even run the disable command – fly settings autoupdate disable w/o it waiting for fly to update.

I don’t know what to say besides a surprise “you can’t run fly commands for 5 minutes” is sometimes very unwelcome.

2 Likes