Repository navigation
feat(completion): answer command and option names in Go - #209
pjcdawkins wants to merge 6 commits into
Conversation
The completion scripts call `_complete` on each Tab, which used to start the legacy PHP CLI every time. Command and option names are now suggested in Go, from the embedded legacy command index and the Cobra commands, in the output format of Symfony's completion for bash, zsh and fish. The installed scripts keep working without being regenerated. PHP is only started for argument and option values (projects, environments, etc.), or for requests that Go does not parse. Other changes: - Native commands (e.g. init, lint) are now suggested, and commands disabled by config are not. - Option descriptions are flattened to one line, as a multi-line description broke zsh and fish suggestions. - Hidden options are not suggested. - Integration tests run the generated scripts in bash, zsh and fish. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The command index was generated with the legacy CLI's default config, so config-dependent commands and options did not match the Upsun or Platform.sh CLIs: for example, resources:get was marked hidden and backup:restore lacked --no-resources in the Upsun CLI. Completion now relies on the index, so an index is generated for each embedded config (commands-upsun.json, commands-platformsh.json, commands-vendor.json) and embedded with the matching build tag. Experiments are no longer enabled when generating it, so that it lists what the default config offers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The embedded index reflects the default config. A user config file, or an API_, APPLICATION_ or EXPERIMENTAL_ environment override, can hide commands, enable experiments or change options, so command and option names are then completed by the legacy CLI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An external config (CLI_CONFIG_FILE, as used by alternative CLIs from config:install) can change the legacy commands and options, so the embedded index only applies to the embedded config. The shell integration test now runs the CLI with its embedded config. Vendor builds also generate the default indexes, which the completion script hook needs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Note
Reviewed — No blocking findings · 🔵 2 minor points
🔍 Full review · 14 files reviewed
🔵 Minor points
Makefile:40—legacy-indexgets the env prefix from the first line in the config that matches^ env_prefix:(2-space indent, value matching[A-Z0-9_]*). The repository configs work becauseapplication.env_prefixcomes beforeservice.env_prefix. The vendorembedded-config.yamlis downloaded at build time, and nothing guarantees its layout. If itsservice:block comes first, the recipe picks the service prefix (e.g.PLATFORM_), soNO_LEGACY_WARNINGandAPPLICATION_VERSIONare set under the wrong names and have no effect when the vendor index is generated. If the file uses another indent width,test -nfails andmake vendor-release/vendor-snapshotstops. A small Go or PHP helper that parses the YAML forapplication.env_prefixwould avoid both cases.Makefile:42— The old index recipe setEXPERIMENTAL_ALL_EXPERIMENTS=1on purpose so that every command was indexed. The new recipe drops it, so experiment-gated commands such asserver:*(ServerCommandBase::isEnabledchecksenable_local_server) are no longer in the index. Completion guards against this withlegacyConfigOverridden, but the index also feedsexpandAbbreviationinroot.goandhelp.go, which run unconditionally. When a user enables experiments in~/.upsun-cli/config.yaml, the legacy CLI has commands that the ambiguity check never sees. An abbreviation that the legacy CLI would reject as ambiguous can then be expanded to a native command. No current native name collides with these commands, so nothing fails today. A future native or experimental command could collide.
Verification
- The legacy
CustomJsonDescriptorwrites empty option maps as{}, leaves out hidden options and global options, and flattens descriptions, so themap[string]Optiondecode andDefinitiondoc comment are correct. - Each
commands_*.gobuild tag pairs with the matchingconfig_*.gotag (!platformsh && !vendor,platformsh && !vendor,vendor), so the embedded index always matches the embedded config. writeSuggestionsmatches Symfony 7.4's output format for each shell: bash prints values only with a trailing newline, zsh and fish add a tab before the description, and fish has no trailing newline.- The Symfony scripts drop empty input words, so
parseCompleteRequestrejecting an empty-ivalue never sends normal requests to PHP; after a space,current == len(tokens), which the code treats as a free cursor. resolveAbbreviationnow returns an index instead of a pointer, and bothexpandAbbreviationcall sites are updated to match.
New unit tests in commands/complete_test.go (parsing, Go completion, output format, override detection) and TestIsEmbedded run in the CI test job (make test). TestShellCompletion runs the generated scripts in bash, zsh and fish in the integration-test job, which now installs those shells. No test covers the Makefile index recipe with a vendor config.
Review details
- Commit: bf54757
- Model: claude-opus-5-5
Review 1 of 10 for this pull request · View the full run
The legacy index recipe took the first "env_prefix:" line of the config, which depends on the layout of the vendor config downloaded at build time. It now parses the config like the CLI does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Note
Reviewed — No new issues found · 1 still open
🔁 Incremental · 2 files reviewed
Outstanding from earlier reviews:
- 🔵
Makefile:41: Abbreviation expansion stops matching the legacy CLI once experiments are enabled. — Thelegacy-indexrecipe still doesn't setEXPERIMENTAL_ALL_EXPERIMENTS, so experiment-gated commands stay out of the index thatexpandAbbreviationuses. (first raised)
Verification
config.FromYAMLvalidatesApplication.EnvPrefixasrequired, soscripts/env-prefixcannot print an empty prefix. That makes up for dropping the oldtest -n "$prefix"guard.- Building
scripts/env-prefixwithout build tags pulls inconfig_upsun.go, which embedsupsun-cli.yaml. That file is checked in, sogo runworks withoutembedded-config.yamleven for the vendor index. - In
legacy-index, a non-zero exit fromgo runinside$$(...)breaks the&&chain. The recipe then exits with that status and still deletes the temp dir.
Nothing in this increment tests scripts/env-prefix. It only runs when the Makefile index targets build, i.e. in make single/snapshot/release and in the CI jobs that call them. config_test.go already covers the required validation of EnvPrefix that the script relies on.
Review 2 of 10 for this pull request · View the full run
Shell completion called the legacy PHP CLI on every Tab.
_completenow suggests command and option names in Go, in the output format of Symfony's completion scripts for bash, zsh and fish, so installed scripts keep working without being regenerated. PHP is only started for argument and option values (projects, environments, etc.).init,lint) and exclude commands disabled by config.The command index is now generated for each embedded config (
commands-upsun.json,commands-platformsh.json,commands-vendor.json) and embedded with the matching build tag. Before, it reflected the legacy CLI's default config, so config-dependent commands and options (e.g.resources:*,--no-resources) did not match the Upsun CLI.Go leaves completion to PHP when the index may not apply: with an external config (
CLI_CONFIG_FILE), a user config file, or anAPI_,APPLICATION_orEXPERIMENTAL_environment override. Project- and OS-dependent visibility (environment:drush,tunnel:openon Windows) is not reflected.Integration tests run the generated scripts in bash, zsh and fish; CI installs those shells.
🤖 Generated with Claude Code