Skip to content

feat: add sh ports entity for listing listening ports#49

Merged
vitali87 merged 3 commits into
mainfrom
feat/sh-ports
Mar 27, 2026
Merged

feat: add sh ports entity for listing listening ports#49
vitali87 merged 3 commits into
mainfrom
feat/sh-ports

Conversation

@vitali87

Copy link
Copy Markdown
Owner

Summary

  • Add u7 sh ports entity to list all listening TCP ports using ss -tlnp with fallback to netstat -tlnp
  • Add u7 sh ports match <pattern> to filter listening ports by pattern (grep)
  • Add tab completion for ports entity and its match subcommand
  • Add tests: runs without error, rejects invalid args, match requires pattern, match runs without crash

Test plan

  • bash test.sh passes all 141 tests (0 failures)
  • Manual: u7 sh ports lists listening ports
  • Manual: u7 sh ports match ssh filters output
  • Manual: u7 sh ports badarg shows usage

@vitali87

Copy link
Copy Markdown
Owner Author

@greptile

@vitali87

Copy link
Copy Markdown
Owner Author

/gemini review

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

Copy link
Copy Markdown

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 introduces a ports command to list listening ports using ss or netstat and a log command for file monitoring. Feedback suggests refactoring the ports logic to eliminate code duplication and moving the log feature to a separate PR as it is currently out of scope and lacks tests and shell completions.

Comment thread lib/show.sh
Comment on lines +151 to +173
ports)
case "$1" in
match)
if [[ -z "$2" ]]; then
echo "Usage: u7 sh ports match <pattern>"
return 1
fi
if command -v ss &>/dev/null; then
ss -tlnp | grep -i "$2"
else
netstat -tlnp 2>/dev/null | grep -i "$2"
fi
;;
"")
if command -v ss &>/dev/null; then
ss -tlnp
else
netstat -tlnp 2>/dev/null
fi
;;
*) echo "Usage: u7 sh ports [match <pattern>]" ; return 1 ;;
esac
;;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The logic for choosing between ss and netstat is duplicated for the match case and the default case. You can refactor this to be more DRY (Don't Repeat Yourself) by extracting this logic into a helper function. This will improve the maintainability of the code. I've also added -- to grep as a good practice to prevent patterns starting with a hyphen from being interpreted as options.

    ports)
      _list_ports() {
        if command -v ss &>/dev/null; then
          ss -tlnp
        else
          netstat -tlnp 2>/dev/null
        fi
      }
      case "$1" in
        match)
          if [[ -z "$2" ]]; then
            echo "Usage: u7 sh ports match <pattern>"
            return 1
          fi
          _list_ports | grep -i -- "$2"
          ;;
        "")
          _list_ports
          ;;
        *) echo "Usage: u7 sh ports [match <pattern>]" ; return 1 ;;
      esac
      ;;

Comment thread lib/show.sh
Comment on lines +275 to +312
log)
local file="$1"
if [[ -z "$file" ]]; then
echo "Usage: u7 sh log <file> [limit N|match <pattern>|follow]"
return 1
fi
if [[ ! -f "$file" ]]; then
echo "File not found: $file"
return 1
fi
case "$2" in
limit)
local n="${3:-20}"
if ! [[ "$n" =~ ^[0-9]+$ ]]; then
echo "Error: limit must be a positive integer, got '$n'"
return 1
fi
tail -n "$n" "$file"
;;
match)
if [[ -z "$3" ]]; then
echo "Usage: u7 sh log <file> match <pattern>"
return 1
fi
grep -- "$3" "$file"
;;
follow)
tail -f "$file"
;;
"")
tail -n 20 "$file"
;;
*)
echo "Usage: u7 sh log <file> [limit N|match <pattern>|follow]"
return 1
;;
esac
;;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This pull request introduces the log entity, which seems unrelated to the main feature of adding the ports entity as described in the PR title and summary. Bundling unrelated changes in a single PR makes it harder to review and can complicate the git history.

The log feature also appears to be incomplete, as it's missing shell completions and tests. It's a best practice to keep pull requests focused on a single, cohesive change.

Please consider moving the log entity implementation and its related changes (e.g., help text) to a separate pull request where it can be completed and reviewed independently.

@greptile-apps

greptile-apps Bot commented Mar 27, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds two new show entities — ports (listing listening TCP ports via ss/netstat) and log (a thin tail/grep wrapper for log files) — along with tab-completion support for ports and four new tests.

The implementation is straightforward and fits the existing codebase patterns well. A few minor polish items were found:

  • ports is not listed in u7 sh --help, making it undiscoverable without reading source or completion hints.
  • When neither ss nor netstat is installed the code silently suppresses the "command not found" error and returns a bare exit 127, giving the user no actionable feedback.
  • ports match uses case-insensitive grep -i while the log match command added in the same PR uses case-sensitive grep, creating an unexplained inconsistency in search behaviour.

Confidence Score: 5/5

Safe to merge — all remaining findings are P2 style/usability suggestions with no correctness or data-integrity impact.

All three new findings are P2: a missing help-text entry, a silent-failure UX gap, and a grep case-sensitivity inconsistency. None affect correctness or security. Prior P1 issues (grep flag injection, unconditional test pass) were flagged in previous review rounds and are the author's call to address. The core feature works as intended.

lib/show.sh — help text omission and silent fallback behaviour worth a quick fix before release.

Important Files Changed

Filename Overview
lib/show.sh Adds ports entity (ss/netstat with fallback) and log entity (tail/grep wrapper); ports is missing from --help, silent failure when neither ss nor netstat is present, and grep case-sensitivity is inconsistent between the two new commands.
lib/completions.sh Adds ports to entity completion list and its match subcommand completion; log entity and its subcommands are absent from completion (flagged in prior review).
test.sh Adds four tests for sh ports; the final ports match test unconditionally increments PASSED without checking exit status (flagged in prior review).

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["u7 sh ports"] --> B{subcommand?}
    B -- "match &lt;pattern&gt;" --> C{pattern provided?}
    C -- No --> D["echo Usage; return 1"]
    C -- Yes --> E{ss available?}
    E -- Yes --> F["ss -tlnp | grep -i &lt;pattern&gt;"]
    E -- No --> G["netstat -tlnp 2>/dev/null | grep -i &lt;pattern&gt;"]
    B -- "(empty)" --> H{ss available?}
    H -- Yes --> I["ss -tlnp"]
    H -- No --> J["netstat -tlnp 2>/dev/null"]
    B -- "other" --> K["echo Usage; return 1"]
    G -.->|"neither tool installed: silent exit 127"| L["no output, no error message"]
    J -.->|"neither tool installed: silent exit 127"| L
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: lib/show.sh
Line: 363

Comment:
**`ports` entity missing from `--help` output**

The new `ports` entity is not listed in the `--help` help text. `port` (singular, line 363) is there, but the new `ports` (plural) command is absent. Users running `u7 sh --help` will have no indication it exists.

```suggestion
  port <number>
  ports [match <pattern>]
```

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

---

This is a comment left during a code review.
Path: lib/show.sh
Line: 158-162

Comment:
**Silent failure when neither `ss` nor `netstat` is available**

When `ss` is absent and `netstat` is also missing, `netstat -tlnp 2>/dev/null` silently suppresses the "command not found" error and returns exit code 127. The user gets no output and no explanation. The same pattern exists in the empty-string branch (lines 165-169). Consider adding an explicit fallback error message:

```
if command -v ss &>/dev/null; then
  ss -tlnp | grep -i "$2"
elif command -v netstat &>/dev/null; then
  netstat -tlnp | grep -i "$2"
else
  echo "Error: neither 'ss' nor 'netstat' is available" >&2
  return 1
fi
```

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

---

This is a comment left during a code review.
Path: lib/show.sh
Line: 159-162

Comment:
**Case-sensitivity inconsistency between `ports match` and `log match`**

`ports match` uses `grep -i` (case-insensitive), while the `log match` command added in the same PR uses plain `grep` (case-sensitive). It's not clear which is intentional. If case-insensitivity is desirable for ports, it should be consistent, or the difference should be documented.

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

Reviews (2): Last reviewed commit: "fix: guard sh ports tests for CI without..." | Re-trigger Greptile

Comment thread lib/completions.sh Outdated
case "$verb" in
show|sh)
COMPREPLY=($(compgen -W "ip csv json line ssl files diff cpu memory disk processes port usage network git env http docker system definition functions --help" -- "$cur"))
COMPREPLY=($(compgen -W "ip csv json line ssl files diff cpu memory disk processes ports port usage network git env http docker system definition functions --help" -- "$cur"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 log entity missing from tab-completion word list

The log entity was added to show.sh in this PR but was not added to the entity completion word list. Running u7 sh <TAB> will not suggest log, making it effectively undiscoverable via tab completion.

Suggested change
COMPREPLY=($(compgen -W "ip csv json line ssl files diff cpu memory disk processes ports port usage network git env http docker system definition functions --help" -- "$cur"))
COMPREPLY=($(compgen -W "ip csv json line ssl files diff cpu memory disk processes ports port usage network git env log http docker system definition functions --help" -- "$cur"))
Prompt To Fix With AI
This is a comment left during a code review.
Path: lib/completions.sh
Line: 5

Comment:
**`log` entity missing from tab-completion word list**

The `log` entity was added to `show.sh` in this PR but was not added to the entity completion word list. Running `u7 sh <TAB>` will not suggest `log`, making it effectively undiscoverable via tab completion.

```suggestion
      COMPREPLY=($(compgen -W "ip csv json line ssl files diff cpu memory disk processes ports port usage network git env log http docker system definition functions --help" -- "$cur"))
```

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

Comment thread lib/completions.sh
env) COMPREPLY=($(compgen -W "match" -- "$cur")) ;;
http) COMPREPLY=($(compgen -W "get head headers" -- "$cur")) ;;
docker) COMPREPLY=($(compgen -W "containers images volumes networks all" -- "$cur")) ;;
ports) COMPREPLY=($(compgen -W "match" -- "$cur")) ;;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 log subcommand completion missing

ports got argument completion, but log (also added in this PR) has no completion case for its subcommands (limit, match, follow). Add a log case alongside the new ports case:

Suggested change
ports) COMPREPLY=($(compgen -W "match" -- "$cur")) ;;
ports) COMPREPLY=($(compgen -W "match" -- "$cur")) ;;
log) COMPREPLY=($(compgen -W "limit match follow" -- "$cur")) ;;
Prompt To Fix With AI
This is a comment left during a code review.
Path: lib/completions.sh
Line: 41

Comment:
**`log` subcommand completion missing**

`ports` got argument completion, but `log` (also added in this PR) has no completion case for its subcommands (`limit`, `match`, `follow`). Add a `log` case alongside the new `ports` case:

```suggestion
        ports) COMPREPLY=($(compgen -W "match" -- "$cur")) ;;
        log) COMPREPLY=($(compgen -W "limit match follow" -- "$cur")) ;;
```

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

Comment thread lib/show.sh
Comment on lines +158 to +162
if command -v ss &>/dev/null; then
ss -tlnp | grep -i "$2"
else
netstat -tlnp 2>/dev/null | grep -i "$2"
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 grep pattern not protected from flag injection

grep -i "$2" will interpret any pattern beginning with - as a grep flag (e.g. u7 sh ports match -v would invert the match silently). The log match handler added in the same PR correctly uses grep -- "$3" to terminate flag parsing. Apply the same protection here:

Suggested change
if command -v ss &>/dev/null; then
ss -tlnp | grep -i "$2"
else
netstat -tlnp 2>/dev/null | grep -i "$2"
fi
if command -v ss &>/dev/null; then
ss -tlnp | grep -i -- "$2"
else
netstat -tlnp 2>/dev/null | grep -i -- "$2"
fi
Prompt To Fix With AI
This is a comment left during a code review.
Path: lib/show.sh
Line: 158-162

Comment:
**`grep` pattern not protected from flag injection**

`grep -i "$2"` will interpret any pattern beginning with `-` as a grep flag (e.g. `u7 sh ports match -v` would invert the match silently). The `log match` handler added in the same PR correctly uses `grep -- "$3"` to terminate flag parsing. Apply the same protection here:

```suggestion
          if command -v ss &>/dev/null; then
            ss -tlnp | grep -i -- "$2"
          else
            netstat -tlnp 2>/dev/null | grep -i -- "$2"
          fi
```

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

Comment thread test.sh Outdated

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

Copy link
Copy Markdown

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 adds ports and log entities to the tool. Feedback suggests refactoring the ports implementation to reduce logic duplication and moving the log feature to a separate pull request to maintain a focused scope.

Comment thread lib/show.sh
Comment on lines +151 to +173
ports)
case "$1" in
match)
if [[ -z "$2" ]]; then
echo "Usage: u7 sh ports match <pattern>"
return 1
fi
if command -v ss &>/dev/null; then
ss -tlnp | grep -i "$2"
else
netstat -tlnp 2>/dev/null | grep -i "$2"
fi
;;
"")
if command -v ss &>/dev/null; then
ss -tlnp
else
netstat -tlnp 2>/dev/null
fi
;;
*) echo "Usage: u7 sh ports [match <pattern>]" ; return 1 ;;
esac
;;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The logic for choosing between ss and netstat is duplicated for both the plain ports command and ports match. You can refactor this to avoid repetition and improve maintainability by determining the base command first.

    ports)
      local list_cmd
      if command -v ss &>/dev/null; then
        list_cmd="ss -tlnp"
      else
        list_cmd="netstat -tlnp 2>/dev/null"
      fi

      case "$1" in
        match)
          if [[ -z "$2" ]]; then
            echo "Usage: u7 sh ports match <pattern>"
            return 1
          fi
          eval "$list_cmd" | grep -i -- "$2"
          ;;
        "")
          eval "$list_cmd"
          ;;
        *) echo "Usage: u7 sh ports [match <pattern>]" ; return 1 ;;
      esac
      ;;

Comment thread lib/show.sh
Comment on lines +275 to +312
log)
local file="$1"
if [[ -z "$file" ]]; then
echo "Usage: u7 sh log <file> [limit N|match <pattern>|follow]"
return 1
fi
if [[ ! -f "$file" ]]; then
echo "File not found: $file"
return 1
fi
case "$2" in
limit)
local n="${3:-20}"
if ! [[ "$n" =~ ^[0-9]+$ ]]; then
echo "Error: limit must be a positive integer, got '$n'"
return 1
fi
tail -n "$n" "$file"
;;
match)
if [[ -z "$3" ]]; then
echo "Usage: u7 sh log <file> match <pattern>"
return 1
fi
grep -- "$3" "$file"
;;
follow)
tail -f "$file"
;;
"")
tail -n 20 "$file"
;;
*)
echo "Usage: u7 sh log <file> [limit N|match <pattern>|follow]"
return 1
;;
esac
;;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This pull request introduces a new log entity, which is unrelated to the stated goal of adding the ports entity. To maintain a clean and understandable commit history, it's best to keep pull requests focused on a single feature or bug fix. Please consider moving the implementation of the log entity to a separate pull request.

Comment thread lib/show.sh
network
git <authors|branches|tags|log [N]|status|diff|remotes>
env [match <pattern>]
log <file> [limit N|match <pattern>|follow]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This change is related to the log entity, which seems out of scope for this pull request. This should be moved to a separate PR along with the log entity's implementation.

@vitali87

Copy link
Copy Markdown
Owner Author

@greptile

@vitali87 vitali87 merged commit fd5f638 into main Mar 27, 2026
2 checks passed
@vitali87 vitali87 deleted the feat/sh-ports branch March 27, 2026 21:29
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