Skip to content

refactor: split utility.sh into per-verb modules in lib/#44

Merged
vitali87 merged 8 commits intomainfrom
refactor/split-modules
Mar 27, 2026
Merged

refactor: split utility.sh into per-verb modules in lib/#44
vitali87 merged 8 commits intomainfrom
refactor/split-modules

Conversation

@vitali87
Copy link
Owner

Summary

  • Split the 1480-line utility.sh into 9 focused module files in lib/
  • utility.sh is now 75 lines: just the loader, dispatcher, and top-level help
  • Each verb has its own file: lib/show.sh, lib/make.sh, lib/drop.sh, etc.
  • Core utilities (require, escape_sed, exec) in lib/core.sh
  • Tab completions in lib/completions.sh
  • Removed lib/ from .gitignore (was a Python template artifact)
  • No functional changes — all code preserved

File structure

utility.sh          (75 lines — loader + dispatcher)
lib/core.sh         (37 lines — shared utilities)
lib/show.sh         (308 lines)
lib/make.sh         (159 lines)
lib/drop.sh         (164 lines)
lib/convert.sh      (294 lines)
lib/move.sh         (44 lines)
lib/set.sh          (155 lines)
lib/run.sh          (125 lines)
lib/completions.sh  (122 lines)

Test plan

  • Run full test suite — all tests should pass unchanged
  • u7 --help works
  • Tab completion still works

@vitali87
Copy link
Owner Author

@greptile

@vitali87
Copy link
Owner Author

/gemini review

@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refactors the utility.sh script by breaking down its extensive functionality into a modular structure. The primary goal is to improve maintainability and readability by separating concerns into dedicated files based on their command verbs and core utilities. This change streamlines the main script, making it easier to navigate and extend, without altering any existing behavior.

Highlights

  • Codebase Modularization: The monolithic utility.sh script was refactored into nine smaller, focused modules, significantly improving code organization.
  • Core Utilities Extraction: Common utility functions like command requirement checks, SED escaping, and dry-run execution were moved into a new lib/core.sh file.
  • Verb-Specific Modules: Logic for each command verb (e.g., show, make, drop, convert, move, set, run) was extracted into its own dedicated file within the lib/ directory.
  • Tab Completion Isolation: The tab completion logic was separated into a new lib/completions.sh file for better maintainability.
  • Reduced Main Script Size: The main utility.sh file was drastically reduced in size, now primarily serving as a loader and dispatcher for the new modular components.
  • .gitignore Update: The lib/ directory was removed from .gitignore to ensure the newly created module files are tracked by version control.
  • No Functional Changes: This refactoring focused solely on improving code structure and maintainability, with all existing functionality preserved.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@greptile-apps
Copy link

greptile-apps bot commented Mar 24, 2026

Greptile Summary

This PR refactors the monolithic 1480-line utility.sh into 9 focused modules under lib/, reducing utility.sh to 75 lines (loader + dispatcher + top-level help). The split is clean: lib/core.sh holds shared utilities, verb handlers are in their own files, and lib/completions.sh isolates tab-completion logic.\n\nKey improvements in this PR:\n- The previously-nested _get_output_file helper is now a proper module-level function (_u7_archive_output_file) at the top of lib/convert.sh, consistent with the _u7_ naming convention.\n- source calls in the loader now have || { … return 1; } guards — load failures are loud and explicit.\n- The loop variable _u7_mod and _U7_DIR are both unset in both the success path and the error path, preventing shell-environment pollution.\n- .gitignore no longer excludes lib/ (was a Python-template artifact).\n\nMinor note on "no functional changes": u7 sh json did gain a behaviour change — the jq filter was updated from .[:$limit] (which errors on non-array JSON) to if type == \"array\" then .[:$limit] else . end (which handles any JSON value gracefully). This is a net improvement but is a functional delta.\n\nPrior review threads identified several open issues in lib/completions.sh (phantom completion entries for unimplemented entities, entity-pattern mismatches, _filedir clobbering COMPREPLY, and $(compgen …) word-splitting) that are not yet addressed in this PR and are worth resolving before merge.

Confidence Score: 4/5

Safe to merge structurally, but open P1-level completion bugs from prior review threads should be addressed first.

The refactoring itself is clean and well-executed — module boundaries are clear, the loader correctly guards every source call and cleans up variables in both success and failure paths, and the nested helper function was properly promoted to module level. However, prior review threads identified several real defects in lib/completions.sh (phantom completions that produce runtime errors, entity-pattern mismatches, _filedir clobbering COMPREPLY) that are still unresolved. These are P1-level issues in changed/new code, warranting a 4/5 rather than 5/5.

lib/completions.sh requires the most attention due to unresolved issues from prior review threads.

Important Files Changed

Filename Overview
utility.sh Clean loader: sources 9 modules in order, guards each source call, and unsets loop vars in both success and error paths.
lib/core.sh Shared utilities (_u7_require, _u7_escape_sed, _u7_exec, _U7_DRY_RUN) correctly extracted with no issues.
lib/completions.sh Multiple open issues flagged in prior review threads: phantom completions, entity-pattern mismatches for image/video/json/csv, _filedir clobbering COMPREPLY for check, and $(compgen …) word-splitting throughout.
lib/convert.sh Previously-nested _get_output_file is now _u7_archive_output_file at module level; all entity branches are intact and the cross-module call to _u7_make is safe given load order.
lib/show.sh All original entities preserved; jq query improved to handle non-array JSON gracefully (functional delta vs original); system entity appears in completions but has no handler here.
lib/make.sh All entities (dir, file, password, user, copy, link, archive, clone, sequence) cleanly extracted; template appears in completions but has no handler here.
lib/drop.sh Entities correctly extracted; docker appears in the drop completion list but has no handler in this file.
lib/move.sh Small and clean; file and sync entities correctly extracted.
lib/set.sh All five entities (text, slashes, tabs, perms, owner) correctly extracted; mapfile used safely for directory-wide text replacement.
lib/run.sh All entities (job, script, check, terminal, background/priority passthrough) correctly extracted; no new issues.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["source utility.sh"] --> B["Compute _U7_DIR"]
    B --> C{"for each module in load order"}
    C -->|core| D["lib/core.sh"]
    C -->|show| E["lib/show.sh"]
    C -->|make| F["lib/make.sh"]
    C -->|drop| G["lib/drop.sh"]
    C -->|convert| H["lib/convert.sh"]
    C -->|move| I["lib/move.sh"]
    C -->|set| J["lib/set.sh"]
    C -->|run| K["lib/run.sh"]
    C -->|completions| L["lib/completions.sh"]
    C -->|load failure| M["echo error >&2 / unset / return 1"]
    D & E & F & G & H & I & J & K & L --> N["unset _u7_mod _U7_DIR / define u7"]
    N --> O["u7 verb entity ..."]
    O --> P{"verb dispatch"}
    P -->|sh| E
    P -->|mk| F
    P -->|dr| G
    P -->|cv| H
    P -->|mv| I
    P -->|st| J
    P -->|rn| K
Loading

Reviews (8): Last reviewed commit: "fix: address Greptile review feedback fo..." | Re-trigger Greptile

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

The pull request successfully refactors the utility.sh script by splitting its functionality into several smaller, verb-specific modules within the lib/ directory. This significantly improves modularity and maintainability, making the codebase easier to navigate and extend. The .gitignore update correctly reflects the new file structure. While the refactoring is well-executed, several areas require attention regarding security, portability, and best practices in shell scripting. Specifically, direct use of sudo within functions, command injection vulnerabilities due to eval and unquoted variables, and reliance on Bash 4+ features and GNU sed syntax introduce critical and high-severity issues that should be addressed.

local value="${time//[^0-9]/}"

if [[ "$_U7_DRY_RUN" == "1" ]]; then
case "$unit" in

Choose a reason for hiding this comment

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

critical

Using eval "$cmd" is a critical security vulnerability. If $cmd contains any user-controlled input, it allows for arbitrary code execution. This should be avoided. Instead, consider using bash -c "$cmd" or carefully sanitizing the input if eval is absolutely necessary, though it's rarely the safest option.

if [[ "$_U7_DRY_RUN" == "1" ]]; then
echo "[dry-run] $cmd &"
else
eval "$cmd" &

Choose a reason for hiding this comment

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

critical

Using eval "$cmd" & is another critical security vulnerability. Similar to the job entity, if $cmd contains user-controlled input, it allows for arbitrary code execution. This should be avoided. If running a command in the background is needed, ensure the command is constructed safely without eval.

_u7_exec tar -cJvf "$output" "$@"
else
if [[ "$_U7_DRY_RUN" == "1" ]]; then
echo "[dry-run] xz -c $* > $output"

Choose a reason for hiding this comment

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

high

Using unquoted $* can lead to word splitting and globbing issues if filenames contain spaces or special characters. It's safer to use "$@" to ensure each argument is passed as a separate, properly quoted word to the command.

Suggested change
echo "[dry-run] xz -c $* > $output"
echo "[dry-run] xz -c \"$@\" > $output"

fi
;;
*.iso)
_u7_exec sudo mount -o loop "$archive" "$dest" ;;

Choose a reason for hiding this comment

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

high

Directly calling sudo mount within the script can lead to unexpected behavior or security prompts if the user does not have NOPASSWD configured for this command. It's generally better to let the user invoke the entire script with sudo if elevated privileges are required for its operation, or to provide clear instructions for setting up sudoers entries if specific commands must be run with sudo non-interactively.

echo "Usage: u7 dr user <username>"
return 1
fi
_u7_exec sudo deluser "$1"

Choose a reason for hiding this comment

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

high

Similar to lib/convert.sh, directly calling sudo deluser within the script can lead to unexpected behavior or security prompts. It's generally better to let the user invoke the entire script with sudo if elevated privileges are required for its operation, or to provide clear instructions for setting up sudoers entries if specific commands must be run with sudo non-interactively.

fi

for file in "${matched_files[@]}"; do
sed -i'' "s/$old_escaped/$new_escaped/g" "$file"

Choose a reason for hiding this comment

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

high

The sed -i'' syntax for in-place editing without a backup file is specific to GNU sed. On systems using BSD sed (like macOS), this syntax will treat '' as the backup file extension, potentially leading to unexpected behavior or errors. For cross-platform compatibility, consider using sed -i.bak (which creates a backup) or sed -i '' (with a space, for BSD sed to mean no backup) and handling the backup file if necessary.

Suggested change
sed -i'' "s/$old_escaped/$new_escaped/g" "$file"
sed -i '' "s/$old_escaped/$new_escaped/g" "$file"

sed -i'' "s/$old_escaped/$new_escaped/g" "$file"
done
else
sed -i'' "s/$old_escaped/$new_escaped/g" "$target"

Choose a reason for hiding this comment

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

high

The sed -i'' syntax for in-place editing without a backup file is specific to GNU sed. On systems using BSD sed (like macOS), this syntax will treat '' as the backup file extension, potentially leading to unexpected behavior or errors. For cross-platform compatibility, consider using sed -i.bak (which creates a backup) or sed -i '' (with a space, for BSD sed to mean no backup) and handling the backup file if necessary.

Suggested change
sed -i'' "s/$old_escaped/$new_escaped/g" "$target"
sed -i '' "s/$old_escaped/$new_escaped/g" "$target"

count="$2"
fi
local term_cmd
term_cmd=$(ps -o comm= -p "$(($(ps -o ppid= -p "$(($(ps -o sid= -p "$$")))")))")

Choose a reason for hiding this comment

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

medium

The command ps -o comm= -p "$(($(ps -o ppid= -p "$(($(ps -o sid= -p "$$")))")))" is overly complex and relies heavily on specific ps output formats and process tree traversal, which can be fragile and non-portable across different Unix-like systems or ps versions. This could lead to unexpected failures or incorrect terminal detection.

return 1
fi
# Convert only basename to lowercase, preserve extension case
_u7_exec rename 's/^(.*)(\.\w+)$/lc($1) . $2/e' "${@:5}"

Choose a reason for hiding this comment

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

medium

The rename command with the /e flag executes Perl code. While the current usage appears to construct the regex and replacement string internally, using eval-like behavior with potentially user-controlled input (even if indirectly through arguments) can be a security risk. Ensure that all inputs to the rename command are thoroughly sanitized to prevent arbitrary code execution.

lib/convert.sh Outdated
return 1
fi

local lowercase=$(echo "$archive" | tr '[:upper:]' '[:lower:]')

Choose a reason for hiding this comment

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

medium

Using echo "$archive" | tr '[:upper:]' '[:lower:]' creates an unnecessary subshell and external process for a simple string transformation. Bash has built-in parameter expansion for case conversion which is more efficient and idiomatic.

Consider using lowercase=${archive,,} for Bash 4+ or lowercase=$(echo "$archive" | tr '[:upper:]' '[:lower:]') if Bash 3 compatibility is strictly required, but the current approach is less efficient.

Suggested change
local lowercase=$(echo "$archive" | tr '[:upper:]' '[:lower:]')
local lowercase=${archive,,}

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request is an excellent refactoring effort, splitting a large shell script into smaller, more manageable modules. This greatly improves the code's readability and maintainability. The changes are well-structured, and the modularization is logical. I've identified a few areas for improvement, primarily related to shell scripting best practices. These include enhancing the robustness of dry-run outputs by correctly quoting variables, preventing potential issues with filenames containing spaces, and replacing a potentially unsafe command with a more secure alternative. Addressing these points will make the script more robust and reliable.

Comment on lines +259 to +263
if [[ -n "$4" ]]; then
_u7_exec rename 'y/ /_/' "$4"
else
_u7_exec rename 'y/ /_/' -- *
fi

Choose a reason for hiding this comment

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

high

The current implementation for converting spaces to underscores in filenames defaults to operating on all files in the current directory (*) if no file is specified. This is potentially destructive and contradicts the usage string u7 cv spaces to underscores on <file>, which implies a file argument is mandatory. It would be safer to enforce that a file path is provided.

Suggested change
if [[ -n "$4" ]]; then
_u7_exec rename 'y/ /_/' "$4"
else
_u7_exec rename 'y/ /_/' -- *
fi
if [[ -z "$4" ]]; then
echo "Usage: u7 cv spaces to underscores on <file>"
return 1
fi
_u7_exec rename 'y/ /_/' "$4"

if [[ "$1" == "but" ]]; then
local pattern="$2"
if [[ "$_U7_DRY_RUN" == "1" ]]; then
echo "[dry-run] find . -type f ! -name $pattern -delete"

Choose a reason for hiding this comment

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

high

The $pattern variable is not quoted in the dry-run output. If the pattern contains spaces or other special shell characters, the output will be misleading or incorrect. Quoting the variable ensures the dry-run log accurately reflects the command that would be run. This issue is present in several dry-run commands throughout the scripts.

Suggested change
echo "[dry-run] find . -type f ! -name $pattern -delete"
echo "[dry-run] find . -type f ! -name '$pattern' -delete"

_u7_exec tar -czvf "$output" "$@"
else
if [[ "$_U7_DRY_RUN" == "1" ]]; then
echo "[dry-run] gzip -c $* > $output"

Choose a reason for hiding this comment

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

high

The dry-run output for creating an archive uses $* which will not handle filenames with spaces correctly. Using $(printf '%q ' "$@") will ensure that all file arguments are properly quoted and displayed, giving an accurate representation of the command.

Suggested change
echo "[dry-run] gzip -c $* > $output"
echo "[dry-run] gzip -c $(printf '%q ' "$@") > $output"


if [[ "$_U7_DRY_RUN" == "1" ]]; then
if [[ -d "$target" ]]; then
echo "[dry-run] grep -rlF '$old' $target | xargs sed -i'' 's/$old_escaped/$new_escaped/g'"

Choose a reason for hiding this comment

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

high

The $target variable is unquoted in the dry-run output. This can be misleading if the path contains spaces. Quoting it will ensure the dry-run output is accurate.

Suggested change
echo "[dry-run] grep -rlF '$old' $target | xargs sed -i'' 's/$old_escaped/$new_escaped/g'"
echo "[dry-run] grep -rlF '$old' '$target' | xargs sed -i'' 's/$old_escaped/$new_escaped/g'"

Comment on lines +171 to +173
for k in $(git branch | sed 's/^..//'); do
echo -e "$(git show --pretty=format:"%ci %cr" "$k" -- | head -n 1)\t$k"
done | sort -r

Choose a reason for hiding this comment

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

high

The for k in $(...) construct will break on branch names that contain spaces due to word splitting. Using a while read loop is a more robust way to process command output line by line and will correctly handle such cases.

Suggested change
for k in $(git branch | sed 's/^..//'); do
echo -e "$(git show --pretty=format:"%ci %cr" "$k" -- | head -n 1)\t$k"
done | sort -r
git branch | sed 's/^..//' | while IFS= read -r k; do
echo -e "$(git show --pretty=format:\"%ci %cr\" \"$k\" -- | head -n 1)\t$k"
done | sort -r

lib/convert.sh Outdated
return 1
fi

local lowercase=$(echo "$archive" | tr '[:upper:]' '[:lower:]')

Choose a reason for hiding this comment

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

medium

Using echo and tr to convert a string to lowercase creates an unnecessary subshell. Bash 4.0+ provides a more performant and idiomatic way to do this with parameter expansion.

Suggested change
local lowercase=$(echo "$archive" | tr '[:upper:]' '[:lower:]')
local lowercase="${archive,,}"


_u7_exec() {
if [[ "$_U7_DRY_RUN" == "1" ]]; then
echo "[dry-run] $*"

Choose a reason for hiding this comment

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

medium

The dry-run output using echo "[dry-run] $*" doesn't correctly represent arguments with spaces or special characters, which can be misleading. Using printf with the %q format specifier will produce a shell-quoted, unambiguous representation of the command and its arguments, making the dry-run output much more reliable.

Suggested change
echo "[dry-run] $*"
echo "[dry-run] $(printf '%q ' "$@")"

Comment on lines +25 to +27
s) sleep "$value" && eval "$cmd" & ;;
m) sleep "$((value * 60))" && eval "$cmd" & ;;
h) sleep "$((value * 3600))" && eval "$cmd" & ;;

Choose a reason for hiding this comment

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

medium

Using eval can be risky, especially with user-provided input. For consistency with other parts of the script (like the with priority case) and for better security, it's safer to execute the command within a new shell using bash -c.

Suggested change
s) sleep "$value" && eval "$cmd" & ;;
m) sleep "$((value * 60))" && eval "$cmd" & ;;
h) sleep "$((value * 3600))" && eval "$cmd" & ;;
s) sleep "$value" && bash -c "$cmd" & ;;
m) sleep "$((value * 60))" && bash -c "$cmd" & ;;
h) sleep "$((value * 3600))" && bash -c "$cmd" & ;;

if [[ "$_U7_DRY_RUN" == "1" ]]; then
echo "[dry-run] $cmd &"
else
eval "$cmd" &

Choose a reason for hiding this comment

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

medium

Using eval can be risky. To improve security and maintain consistency with other commands in this script, consider using bash -c to execute the command in a separate shell process.

Suggested change
eval "$cmd" &
bash -c "$cmd" &

@vitali87
Copy link
Owner Author

@greptile

@vitali87
Copy link
Owner Author

@greptile

case "$entity" in
check) COMPREPLY=($(compgen -W "syntax" -- "$cur")) ; _filedir ;;
script) _filedir ;;
esac
Copy link

Choose a reason for hiding this comment

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

P2 image/video/json/csv entities never matched by completion rules

The png|jpg|jpeg|gif pattern matches the entity word (the second argument after the verb), but _u7_convert uses image as the entity name for image conversion — not the file extension. So u7 cv image foo.png <tab> falls through to *) _filedir ;; and never offers to as a completion.

The same gap applies to video, json, and csv which are also real entities in lib/convert.sh but are absent from this case statement.

Suggested change
esac
archive|files|image|video|json|csv) COMPREPLY=($(compgen -W "to" -- "$cur")) ;;

The old png|jpg|jpeg|gif pattern can be removed — those are never used as the entity word.

Prompt To Fix With AI
This is a comment left during a code review.
Path: lib/completions.sh
Line: 80

Comment:
**`image`/`video`/`json`/`csv` entities never matched by completion rules**

The `png|jpg|jpeg|gif` pattern matches the *entity* word (the second argument after the verb), but `_u7_convert` uses `image` as the entity name for image conversion — not the file extension. So `u7 cv image foo.png <tab>` falls through to `*) _filedir ;;` and never offers `to` as a completion.

The same gap applies to `video`, `json`, and `csv` which are also real entities in `lib/convert.sh` but are absent from this case statement.

```suggestion
        archive|files|image|video|json|csv) COMPREPLY=($(compgen -W "to" -- "$cur")) ;;
```

The old `png|jpg|jpeg|gif` pattern can be removed — those are never used as the entity word.

How can I resolve this? If you propose a fix, please make it concise.


local verbs="show sh make mk drop dr convert cv move mv set st run rn --help"
local opts="-n --dry-run"

Copy link

Choose a reason for hiding this comment

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

P2 _filedir overwrites the syntax completion

_filedir replaces COMPREPLY entirely, so the syntax suggestion set on the same line is always discarded. A user typing u7 rn check <tab> will see only filenames, never syntax.

Suggested change
check) COMPREPLY=($(compgen -W "syntax" -- "$cur")) ;;

File-path completion is only needed at a deeper position (after syntax in file), and it already falls through to the *) _filedir ;; fallback there.

Prompt To Fix With AI
This is a comment left during a code review.
Path: lib/completions.sh
Line: 96

Comment:
**`_filedir` overwrites the `syntax` completion**

`_filedir` replaces `COMPREPLY` entirely, so the `syntax` suggestion set on the same line is always discarded. A user typing `u7 rn check <tab>` will see only filenames, never `syntax`.

```suggestion
        check) COMPREPLY=($(compgen -W "syntax" -- "$cur")) ;;
```

File-path completion is only needed at a deeper position (after `syntax in file`), and it already falls through to the `*) _filedir ;;` fallback there.

How can I resolve this? If you propose a fix, please make it concise.

@vitali87
Copy link
Owner Author

@greptile

Comment on lines +21 to +43
;;
run|rn)
COMPREPLY=($(compgen -W "job script check terminal --help" -- "$cur"))
;;
esac
}

_u7_complete_args() {
local verb="$1" entity="$2" cur="$3"
case "$verb" in
show|sh)
case "$entity" in
ip) COMPREPLY=($(compgen -W "external internal connected" -- "$cur")) ;;
processes) COMPREPLY=($(compgen -W "running by" -- "$cur")) ;;
files) COMPREPLY=($(compgen -W "match by" -- "$cur")) ;;
usage) COMPREPLY=($(compgen -W "disk directories" -- "$cur")) ;;
git) COMPREPLY=($(compgen -W "authors branches tags log status diff remotes" -- "$cur")) ;;
env) COMPREPLY=($(compgen -W "match" -- "$cur")) ;;
http) COMPREPLY=($(compgen -W "get head headers" -- "$cur")) ;;
docker) COMPREPLY=($(compgen -W "containers images volumes networks all" -- "$cur")) ;;
*) _filedir ;;
esac
;;
Copy link

Choose a reason for hiding this comment

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

P2 COMPREPLY word-splitting on special characters in $cur

Every COMPREPLY=($(compgen -W "..." -- "$cur")) assignment is subject to word-splitting and glob expansion on the result. If $cur contains spaces, tabs, or glob metacharacters the array can silently grow extra elements or collapse. The safe idiom is to use mapfile:

mapfile -t COMPREPLY < <(compgen -W "ip csv json line ssl ..." -- "$cur")

This applies throughout both _u7_complete_entities (every compgen call on lines 23, 26, 29, 32, 35, 38, 41) and _u7_complete_args (lines 51–98).

Prompt To Fix With AI
This is a comment left during a code review.
Path: lib/completions.sh
Line: 21-43

Comment:
**`COMPREPLY` word-splitting on special characters in `$cur`**

Every `COMPREPLY=($(compgen -W "..." -- "$cur"))` assignment is subject to word-splitting and glob expansion on the result. If `$cur` contains spaces, tabs, or glob metacharacters the array can silently grow extra elements or collapse. The safe idiom is to use `mapfile`:

```bash
mapfile -t COMPREPLY < <(compgen -W "ip csv json line ssl ..." -- "$cur")
```

This applies throughout both `_u7_complete_entities` (every `compgen` call on lines 23, 26, 29, 32, 35, 38, 41) and `_u7_complete_args` (lines 51–98).

How can I resolve this? If you propose a fix, please make it concise.

@vitali87
Copy link
Owner Author

@greptile

2 similar comments
@vitali87
Copy link
Owner Author

@greptile

@vitali87
Copy link
Owner Author

@greptile

@vitali87
Copy link
Owner Author

@greptile

@vitali87 vitali87 merged commit 8e5f16d into main Mar 27, 2026
2 checks passed
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.

1 participant