Repository navigation
Rust CLI: port completion and _complete (shell completion) - #93
Conversation
A port of Symfony Console 7.4's DumpCompletionCommand, CompleteCommand,
CompletionInput and the bash, zsh and fish outputs, which 2.x uses
unchanged.
- completion [shell] prints Symfony's completion script for bash, zsh or
fish (the shell argument or $SHELL's basename), with {{ COMMAND_NAME }}
and {{ VERSION }} filled, and refuses an unknown or undetected shell on
stderr with exit 2. completion --debug tails the debug log. The three
scripts are vendored from Symfony with its MIT licence.
- _complete reproduces CompletionInput: Symfony's ArgvInput parsing with
every token error suppressed, the relevant-token and previous-token
rules for option names and values, argument binding, and
getFirstArgument's skipping of option values. It answers the
application's command list, a found command's name expansion, option
names (negatable options as --no-x too), and suggested values, in each
shell's format. The API-version check uses PHP truthiness and
version_compare (exit 126); setup errors exit 2.
- 2.x has no input-dependent completers, so every suggested value is a
fixed list. parity/update-completion.sh records them from the pinned
2.x CLI into src/completion.json (91 application entries, 81 commands).
- The SYMFONY_COMPLETION_DEBUG log has the same shape but names commands
rather than PHP classes.
- build.rs skips symlinks under schemas/ (cli#92 review).
53 parity cases against 2.3.5 match byte for byte: the three scripts,
$SHELL detection and refusals, and _complete in bash, zsh and fish for
command lists, partial and ambiguous names, aliases, option names and
values (=, separate, short, prefixed, negatable), global options before
the command, help/list/completion arguments and formats, arguments after
the last one, after --, unknown commands, and every error path. Overall
308/312, 4 intentional, 0 unexpected.
Refs #72
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The previous commit carried only the recorded completion tables; this adds the rest of the port it describes: src/commands/completion.rs (completion, _complete and CompletionInput), the vendored bash, zsh and fish scripts with Symfony's MIT licence, Input::opt_all for the repeated -i option, the build.rs symlink skip, and 53 parity cases (308/312 overall, 4 intentional, 0 unexpected). Refs #72 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Security source review at exact
The |
|
Security exact-head disposition: HOLD at The security direction remains appropriate: unsafe executable basenames are refused before shell text is emitted, and the debug log path is opened through the private state directory with no-follow/nonblocking and descriptor-based validation. However, the current exact head is not terminal green:
I attempted a bounded counterfactual using the exact local Required next action: make the regression report the command name, shell, exit status, stdout, and stderr on failure; run it against all three required shells; then return a normally pushed exact head with terminal required checks. Preserve the current unsafe-name and private-log controls. No merge or release clearance is given for this head. |
|
Security follow-through on the held CI failure is pushed normally as This is test diagnostics only. On either safe-name generation or shell-source failure, the regression now records the command name, shell, exit status, stdout, and stderr. Product source, the unsafe-name refusal, private-log handling, completion templates, and the existing adversarial controls are byte-identical to the held head. Focused local evidence:
Fresh exact-head hosted checks are running. The merge/release HOLD remains until they are terminal and the previously opaque shell-source failure is either absent or now specifically identified. |
|
The instrumented exact-head rerun identified the failure precisely: command The focused correction is pushed normally as Product code, completion templates, unsafe-basename refusal, private debug-log handling, and all adversarial controls remain byte-identical to Focused local evidence at
Fresh exact-head hosted checks are running. HOLD remains until the required Bash/Zsh/Fish job and all other gates are terminal. |
|
|
Security exact-head review: MERGE GO for The security/product implementation remains byte-identical to reviewed Local exact-head This clears a normal merge of exactly this head. It does not authorize another release or change the already accepted CLI 2.3.6 artifacts; any head/base/tree change returns for review. |
|
General merged the Security-cleared exact head |



Summary
This is the second port group (#72): shell completion. It is a byte-for-byte port of the Symfony Console 7.4 completion that 2.x uses unchanged:
DumpCompletionCommand,CompleteCommand,CompletionInputand the bash, zsh and fish outputs.completion [shell]:$SHELL's basename, with{{ COMMAND_NAME }}(frombasename(argv[0])) and{{ VERSION }}filled in.--debugfollows the private debug log through the validated file descriptor.symfony/console7.4.8 with its MIT licence (rust/src/completion/)._complete:CompletionInput: SymfonyArgvInputparsing with every token error suppressed, the relevant-token and previous-token rules for option names and values, argument binding, andgetFirstArgument's skipping of option values.--no-x), and suggested values, in each shell's format.-a/-Sfollow PHP truthiness andversion_compare(exit 126). Setup errors exit 2 with no output, as in Symfony.parity/update-completion.shrecords them from the pinned 2.x CLI intosrc/completion.json: 91 application entries, and values for 81 commands, such as--output,help's command names and format,list's namespaces,completion's shells, andactivity:fail's kinds.build.rsnow skips symlinks underschemas/(the Rust CLI: port schema:list, schema:manifest, schema:show #92 review).Intentional difference
Executable basenames must start with an ASCII letter or underscore and contain only ASCII letters, digits, dots, underscores and hyphens. Ordinary
dwscripts stay byte-identical. Debug logs live in a privatecompletiondirectory alongside the CLI config, replacing Symfony's shared temporary path. Unix opens use no-follow/nonblocking flags, directory/file handle verification, owner-only permissions and a single-link regular-file check. Windows opens the reparse point itself and refuses reparse/nonregular entries.completion --debugfollows the validated descriptor. Unsafe log entries fail closed.Evidence
Rust 1.86: fmt, clippy
-D warnings, 98 unit tests and 5 integration tests pass locally. New security tests exercise crafted-name sourcing, normal-name scripts, symlink/hard-link/FIFO/directory/shared log refusals, permissions and reading the same private log. Bash and Zsh ran locally; hosted CI installs and requires Bash, Zsh and Fish. Existing parser tests cover completion types,getFirstArgument, cursor validation, the scripts andbasename.Parity: 53 new cases match 2.3.5 byte for byte. They cover:
$SHELLdetection, an empty$SHELL, an unsupported shell, and quiet;_completein bash, zsh and fish for the command list, global and command option names, andhelp's command names;=, with a prefix, as a separate token (with and without a prefix), with no suggestions, a short option, a flag, and a negatable name;list/help/completionarguments and formats, a second argument, after the last argument, after--, unknown commands, and a cursor on the first token;-Sflag, a missing or unsupported shell, and a missing, zero, non-numeric or out-of-range current index, plus no tokens.Overall 308/312 match, 4 intentional, 0 unexpected.
The corrected build refuses unsafe executable names with exit 2 and emits zero script bytes. Unsafe-log regressions leave the sentinel unchanged. Fresh hosted checks are running.
Refs #72