Skip to content

fix(completion): return suggestions instead of the help page - #135

Open
vrobert78 wants to merge 1 commit into
mainfrom
fix/shell-completion-no-suggestions
Open

fix(completion): return suggestions instead of the help page#135
vrobert78 wants to merge 1 commit into
mainfrom
fix/shell-completion-no-suggestions

Conversation

@vrobert78

@vrobert78 vrobert78 commented Jul 29, 2026

Copy link
Copy Markdown

Problem

Tab completion returns nothing in zsh, bash and fish, out of the box and via eval "$(upsun completion)". The completion function is registered correctly; the request it makes comes back empty.

The generated scripts fetch suggestions from the hidden _complete command of the legacy CLI, passing the shell as a glued short option:

_complete --no-interaction -szsh  -a1 -c$((CURRENT-1)) ...   # zsh
_complete --no-interaction -sbash -c$cword -a1 ...           # bash
_complete --no-interaction -sfish -a1 ...                    # fish

The Go layer parses these with Cobra before handing them to the legacy CLI. Cobra's root command uses FParseErrWhitelist{UnknownFlags: true}, which makes pflag walk a glued short option letter by letter (-s, -z, -s, -h). Every supported shell name contains an h, so the bundle always ends with -h, and the root help function rewrites the invocation as help _complete …:

$ upsun _complete --no-interaction -szsh -c1 -iupsun -ienv
Command: _complete
Description: Internal command to provide shell completion suggestions
...                              # <- help page, not completions

The completion function silently discards that. The legacy PHP CLI parses the glued form correctly on its own (php legacy/bin/platform _complete … -szsh returns suggestions), so the Go layer was the only thing at fault.

The same applies to the input tokens the scripts pass, which is why fixing only the templates is not enough: completing upsun ssh --pro<TAB> sends -issh, and that h gets bundled in too.

Fix

  • Proxy _complete through a hidden Cobra command with DisableFlagParsing, so completion requests reach the legacy CLI unchanged. This also repairs completion files users already have installed, and covers input tokens containing an h.
  • Emit --shell=<shell> in the generated scripts, so a request no longer depends on how glued short options are parsed.
  • Skip the update check and the shell-config-leftovers notice for completion requests: they run on every Tab, and the bash script captures stderr along with stdout, so any message would land in the suggestion list.

Verification

Real Tab presses driven through a pty in zsh, bash and fish (Debian container, plus zsh on macOS), for each install method — 27/27 pass with the fix, and the table below shows why the template change alone would not have been sufficient:

binary + installed script upsun env⇥ … --for⇥ upsun ssh --pro⇥
fixed + newly generated script
fixed + previously installed (glued) script
fixed + eval "$(upsun completion)"
before + long-option script (templates only)
before + its own script (status quo)

OEM (vendorized) CLIs

Same bug, same fix. Verified with the Akeneo Extension Platform CLI, installed in a container with its own installer (curl -fsSL https://cli.extension.akeneo.cloud/installer | sh), which downloads the standard upsun release binary and points it at the vendor config.yaml through a wrapper script:

configuration env⇥ … --for⇥ ssh --pro⇥
released binary 5.10.8, as installed by the vendor installer
fixed binary swapped in, newly generated script
fixed binary, previously installed (glued) script
-tags vendor build with the Akeneo config embedded

Identical results in zsh, bash and fish for every row above. OEM users get the fix through the normal release, since the installer pulls the standard release binary.

Tests added: commands/completion_test.go (script rewrite, and completion arguments reaching the command unparsed) and integration-tests/completion_test.go (generated scripts, and a real _complete request in short form, long form, and with an input token containing an h). All fail on main and pass here.

🤖 Generated with Claude Code

Tab completion produced nothing in zsh, bash and fish. The completion
scripts fetch suggestions by calling the hidden `_complete` command of
the legacy CLI, passing the shell as a glued short option (-szsh, -sbash,
-sfish). The Go layer parses those arguments with Cobra, which splits a
glued short option into single-letter flags; as every supported shell
name contains an "h", the bundle always ended with -h, so the CLI
answered with its help page:

    $ upsun _complete --no-interaction -szsh -c1 -iupsun -ienv
    Command: _complete
    Description: Internal command to provide shell completion suggestions

The completion function silently discarded that output. The legacy CLI
itself parses the glued form correctly, so only the Go layer was at
fault.

Proxy `_complete` through a hidden Cobra command with flag parsing
disabled, so the request reaches the legacy CLI unchanged. This also
fixes completion files that users already have installed, and it covers
the input tokens, which have the same problem: completing
"upsun ssh --pro<TAB>" passes -issh, whose "h" was bundled in too.

Additionally, emit the shell as --shell=<shell> in the generated scripts,
so a completion request no longer depends on how glued short options are
parsed.

Completion requests now also skip the update check and the shell config
leftovers notice: they run on every Tab, and the bash script captures
stderr along with stdout, so any message would end up in the suggestions.

Verified with real Tab presses in zsh, bash and fish (newly generated
scripts, previously installed scripts, and eval "$(upsun completion)").

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 29, 2026 14:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes shell tab-completion for the Upsun CLI by preventing Cobra/pflag from mis-parsing legacy _complete requests (notably the glued -s<shell> form that previously produced a bundled -h and triggered help output instead of suggestions). It does so by proxying _complete through a hidden Cobra command with flag parsing disabled, updating generated completion scripts to use --shell=<shell>, and adding regression coverage via unit + integration tests.

Changes:

  • Add a hidden _complete Cobra command with DisableFlagParsing to pass completion arguments through to the legacy CLI unchanged.
  • Rewrite generated completion scripts to emit --shell=<shell> instead of -s<shell>.
  • Skip update checks and shell-config-leftovers notices for completion requests; add unit and integration tests to cover both short/long forms.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
commands/root.go Skips pre/post-run update/notice behaviors for completion requests; registers the new hidden _complete proxy command.
commands/completion.go Rewrites generated completion scripts’ shell option; implements the hidden _complete proxy and completion-request detection.
commands/completion_test.go Unit tests for script shell-option rewriting and for ensuring _complete args reach the command unparsed.
integration-tests/completion_test.go Integration tests validating generated scripts and end-to-end _complete suggestion output for short/long option forms.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread commands/root.go
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.

2 participants