Add credential profiles for working with multiple Fly.io accounts - #5087
Draft
mahdi-salmanzade wants to merge 1 commit into
Draft
Add credential profiles for working with multiple Fly.io accounts#5087mahdi-salmanzade wants to merge 1 commit into
mahdi-salmanzade wants to merge 1 commit into
Conversation
flyctl holds one login at a time, so working across several accounts means logging out and back in, and there is no way to tell which account a command is about to touch. FLY_CONFIG_DIR already provides the isolation; what is missing is the layer that decides which config directory to point at. This adds that layer. A profile is a whole config directory rather than just a token, so each carries its own access token, WireGuard peer state and agent socket. The default profile is ~/.fly itself, leaving existing installations unchanged. Resolution runs flag > FLY_PROFILE > .fly-profile file > active pointer > default, with FLY_CONFIG_DIR still pinning a directory outright. A named profile that does not exist is an error rather than a silent fallback, since reaching a different account than the one asked for is worse than not running. The profile commands are exempt so a dangling reference stays repairable. flyctl.InitConfig is resolved the same way so lock files and agent logs stay per-account. It runs before cobra parses anything, hence reading --profile from os.Args there. Two existing import cycles shaped the wiring: preparers cannot import internal/flag (flag -> completion -> preparers) or internal/command (command -> preparers), so this uses the flagctx and command_context leaf packages, adding nil-safe accessors to each.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Change Summary
What and Why:
flyctl holds one login at a time. Working across several Fly.io accounts means
fly auth logout/fly auth loginon every context switch, and there is no way to ask which account a command is about to touch. This has come up a few times (#1641, #22869).This adds credential profiles: several accounts on one machine, selected per shell, per project or per command.
How:
FLY_CONFIG_DIRalready does the isolating, and it is already maintained here. What was missing is the layer that decides which directory to point at, so that is all this adds.A profile is a complete config directory rather than just a token, so each one carries its own access token, metrics token, WireGuard peer state,
fly-agent.sockand lock files.$HOMEis untouched, so Docker and SSH credentials keep working.The
defaultprofile is~/.fly. An installation that never runsfly profileis unaffected — no migration, no new files.Resolution, highest precedence first:
FLY_CONFIG_DIR--profile <name>FLY_PROFILE=<name>.fly-profile, nearest at or above the cwdfly profile use <name>default~/.flyFLY_ACCESS_TOKEN/FLY_API_TOKENstill override the resolved config exactly as today, so existing CI is unaffected.Two decisions I would most like review on:
.fly-profilenaming a deleted profile broke every command, including the ones needed to fix it. Thefly profilecommands now carry an annotation that lets them fall back to the default profile and report the cause, whilefly deployand friends still refuse.fly profile showdiagnoses the dangling reference.Two existing import cycles shaped the wiring.
preparerscannot importinternal/flag(flag → completion → preparers) orinternal/command(command → preparers), so resolution hangs off theflagctxandcommand_contextleaf packages already added for that reason; each gets a nil-safe accessor.flyctl.InitConfig()runs before cobra parses anything, which is why--profileis read fromos.Argsthere — it is what keeps lock files and agent logs per-account, and it falls back silently since the preparer re-resolves and raises a much better error.Files touched:
internal/profile/internal/command/profile/fly profilecommand groupinternal/cmdutil/preparers/preparers.goDetermineConfigDirconsults the resolverinternal/command/root/root.go--profileand the command groupflyctl/flyctl.gointernal/flag/flagctx/helpers.gointernal/command_context/context.goKept deliberately small and additive: 2 new packages, 5 touched files, ~1950 lines including tests and docs.
Testing:
internal/profilecovering the precedence chain, directory walk-up, name validation against path traversal, dangling-reference errors, and the active-pointer lifecycle across remove/rename.make testpasses with zero failures across the whole suite;go vet ./...is clean.--refreshtoken verification,addrolling back cleanly and leaving no orphan directory when handed a bad token, and shell completion not panicking under an unresolvable profile.fly auth whoamireturns the same result as a stock v0.4.73 build against an untouched~/.fly.I have not written a preflight test — this is all local config resolution with no platform interaction, so unit tests seemed the right fit, but happy to add one if you would prefer.
Related to:
Opened as a draft per CONTRIBUTING's guidance for outside contributors. Happy to reshape any of this — naming, the hard-error decision, or where the docs live.
Documentation
This adds user-facing commands, so it needs docs I have not filed.
profiles.mdin this PR follows the existingbuilding.mdconvention at the repo root, but if this belongs insuperfly/docsinstead, tell me where and I will move it.