Skip to content

feat: add sh log entity for viewing/searching log files#50

Merged
vitali87 merged 1 commit into
mainfrom
feat/sh-log
Mar 27, 2026
Merged

feat: add sh log entity for viewing/searching log files#50
vitali87 merged 1 commit into
mainfrom
feat/sh-log

Conversation

@vitali87

Copy link
Copy Markdown
Owner

Summary

  • Add log entity to u7 sh for viewing and searching log files
  • Supports u7 sh log <file> (tail -20), limit N, match <pattern>, and follow (tail -f)
  • Includes file existence check and input validation
  • Add tab completion for log subcommands (limit, match, follow)
  • Add 9 tests covering default, limit, match, missing file, no args, invalid limit, missing pattern, and unknown subcommand

Test plan

  • bash test.sh passes all 147 tests (0 failures)
  • Manual test: u7 sh log /var/log/syslog
  • Manual test: u7 sh log /var/log/syslog limit 5
  • Manual test: u7 sh log /var/log/syslog match error
  • Manual test: u7 sh log /var/log/syslog follow

@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 new log entity to the u7 sh command, enabling users to view, limit, match, or follow log files. The changes include shell completion support in lib/completions.sh, the core implementation in lib/show.sh, and a suite of new tests in test.sh. Review feedback suggests refactoring the usage string into a variable for better maintainability, making the numeric argument mandatory for the limit subcommand to ensure consistency with the match subcommand, and improving test isolation by using dedicated log files for specific test cases.

Comment thread lib/show.sh
Comment on lines +252 to +256
local file="$1"
if [[ -z "$file" ]]; then
echo "Usage: u7 sh log <file> [limit N|match <pattern>|follow]"
return 1
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.

medium

To improve maintainability and avoid repeating the usage string, you can store it in a variable at the beginning of the log block. This is consistent with how other entities like http are handled in this file. You can then reuse this variable in the default case of the case statement as well (line 284).

Suggested change
local file="$1"
if [[ -z "$file" ]]; then
echo "Usage: u7 sh log <file> [limit N|match <pattern>|follow]"
return 1
fi
local file="$1"
local usage="Usage: u7 sh log <file> [limit N|match <pattern>|follow]"
if [[ -z "$file" ]]; then
echo "$usage"
return 1
fi

Comment thread lib/show.sh
Comment on lines +263 to +267
local n="${3:-20}"
if ! [[ "$n" =~ ^[0-9]+$ ]]; then
echo "Error: limit must be a positive integer, got '$n'"
return 1
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.

medium

When limit is used without a number (e.g., u7 sh log file.log limit), it currently defaults to 20 lines, which is the same as not providing any subcommand. This can be confusing. It's better to require a number when limit is specified and show an error if it's missing, for consistency with how match requires a pattern.

Suggested change
local n="${3:-20}"
if ! [[ "$n" =~ ^[0-9]+$ ]]; then
echo "Error: limit must be a positive integer, got '$n'"
return 1
fi
if [[ -z "$3" ]]; then
echo "Error: The 'limit' subcommand requires a number."
echo "Usage: u7 sh log <file> limit <N>"
return 1
fi
local n="$3"
if ! [[ "$n" =~ ^[0-9]+$ ]]; then
echo "Error: limit must be a positive integer, got '$n'"
return 1
fi

Comment thread test.sh
Comment on lines +1012 to +1015
echo "ERROR something broke" >> "$LOG_FILE"
echo "INFO all good" >> "$LOG_FILE"
echo "ERROR another failure" >> "$LOG_FILE"
result=$(u7 sh log "$LOG_FILE" match ERROR 2>&1)

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

Modifying the shared $LOG_FILE in the middle of tests can lead to test inter-dependencies and make them harder to reason about. It's a good practice to ensure tests are independent. For this match test, consider creating a separate, dedicated log file instead of appending to the existing one.

Suggested change
echo "ERROR something broke" >> "$LOG_FILE"
echo "INFO all good" >> "$LOG_FILE"
echo "ERROR another failure" >> "$LOG_FILE"
result=$(u7 sh log "$LOG_FILE" match ERROR 2>&1)
MATCH_LOG_FILE="$TEST_DIR/match_test.log"
echo "ERROR something broke" > "$MATCH_LOG_FILE"
echo "INFO all good" >> "$MATCH_LOG_FILE"
echo "ERROR another failure" >> "$MATCH_LOG_FILE"
result=$(u7 sh log "$MATCH_LOG_FILE" match ERROR 2>&1)

@greptile-apps

greptile-apps Bot commented Mar 27, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds a log entity to u7 sh, allowing users to view and search log files via tail and grep. The implementation follows the existing pattern of the csv and json entities (file + optional subcommand) and integrates cleanly into the codebase.

Key changes:

  • lib/show.sh: New log) case supporting default (last 20 lines), limit N, match <pattern>, and follow (tail -f) subcommands, with file-existence and input validation.
  • lib/completions.sh: log added to the entity list and its three subcommands registered for tab completion.
  • test.sh: 9 new automated tests covering all interactive code paths (the follow subcommand is intentionally excluded since it is an infinite process).

Two minor issues found: the limit validation regex (^[0-9]+$) accepts 0 which contradicts the "positive integer" wording in the error message, and grep propagates exit code 1 as a function failure when there are simply no matching lines.

Confidence Score: 5/5

Safe to merge — all findings are minor style/validation P2 items that do not affect correctness of the primary user path.

All three issues identified are P2: a validation edge-case (limit 0), a grep exit-code style concern, and a cosmetic completion ordering note. None block correct behaviour for the documented use cases, and the test suite is comprehensive for all interactive paths.

lib/show.sh — the limit validation regex and grep exit code handling are worth addressing before a broader rollout.

Important Files Changed

Filename Overview
lib/show.sh Adds the log entity with tail/grep subcommands; minor issues with limit 0 validation and grep exit code propagation on no-match.
lib/completions.sh Adds log to entity list and its subcommand completions; keywords and file completions are mixed at all argument positions (consistent with existing codebase pattern).
test.sh Adds 9 well-structured tests covering default view, limit, match, missing file, no-args, invalid limit, missing pattern, and unknown subcommand; follow is intentionally untested due to its infinite nature.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["u7 sh log <file> [subcommand]"] --> B{file provided?}
    B -- No --> C["echo Usage; return 1"]
    B -- Yes --> D{file exists?}
    D -- No --> E["echo File not found; return 1"]
    D -- Yes --> F{subcommand}
    F -- empty --> G["tail -n 20 <file>"]
    F -- limit --> H{n provided?}
    H -- No --> I["n=20 (default)"]
    H -- Yes --> J{valid integer?}
    J -- No --> K["echo Error; return 1"]
    J -- Yes --> L["tail -n n <file>"]
    I --> L
    F -- match --> M{pattern provided?}
    M -- No --> N["echo Usage; return 1"]
    M -- Yes --> O["grep -- pattern <file>"]
    F -- follow --> P["tail -f <file>"]
    F -- unknown --> Q["echo Usage; return 1"]
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: lib/show.sh
Line: 264

Comment:
**`limit 0` bypasses the "positive integer" contract**

The regex `^[0-9]+$` matches `0`, so `u7 sh log <file> limit 0` silently runs `tail -n 0` and returns nothing with exit 0. The error message on the following line explicitly says "positive integer", but `0` is accepted. Users seeing silent empty output for `limit 0` are likely to be confused.

```suggestion
          if ! [[ "$n" =~ ^[1-9][0-9]*$ ]]; then
```

This changes the regex to require at least one digit 1-9 optionally followed by more digits, which correctly excludes `0`.

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: 270-276

Comment:
**`grep` exit code propagates as function failure on no-match**

When `grep` finds no matching lines it exits with code `1`, which becomes the return value of `_u7_show`. A caller checking `$?` (or a script using `set -e`) will see this as a command failure even though "no results" is not an error. Consider suppressing or distinguishing the no-match exit code:

```suggestion
        match)
          if [[ -z "$3" ]]; then
            echo "Usage: u7 sh log <file> match <pattern>"
            return 1
          fi
          grep -- "$3" "$file" || true
          ;;
```

Appending `|| true` keeps the function exit code at `0` when there are simply no matches, while still showing any matched lines.

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/completions.sh
Line: 41

Comment:
**Completion offers keywords at every argument position**

`_u7_complete_args` is called for any argument beyond the entity, so this line runs for both position 3 (where the user should type a `<file>`) and position 4+ (where a keyword is expected). The result is that `limit`, `match`, and `follow` appear alongside file-name completions even when typing the file path.

This is consistent with the existing `check` pattern at line 79, so it may be intentional. If you want position-aware completion you would need to inspect `$cword` relative to `$entity_idx`, but that is a larger refactor. As-is, it is a minor UX roughness rather than a functional bug.

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

Reviews (1): Last reviewed commit: "feat: add sh log entity for viewing/sear..." | Re-trigger Greptile

Comment thread lib/show.sh
case "$2" in
limit)
local n="${3:-20}"
if ! [[ "$n" =~ ^[0-9]+$ ]]; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 limit 0 bypasses the "positive integer" contract

The regex ^[0-9]+$ matches 0, so u7 sh log <file> limit 0 silently runs tail -n 0 and returns nothing with exit 0. The error message on the following line explicitly says "positive integer", but 0 is accepted. Users seeing silent empty output for limit 0 are likely to be confused.

Suggested change
if ! [[ "$n" =~ ^[0-9]+$ ]]; then
if ! [[ "$n" =~ ^[1-9][0-9]*$ ]]; then

This changes the regex to require at least one digit 1-9 optionally followed by more digits, which correctly excludes 0.

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

Comment:
**`limit 0` bypasses the "positive integer" contract**

The regex `^[0-9]+$` matches `0`, so `u7 sh log <file> limit 0` silently runs `tail -n 0` and returns nothing with exit 0. The error message on the following line explicitly says "positive integer", but `0` is accepted. Users seeing silent empty output for `limit 0` are likely to be confused.

```suggestion
          if ! [[ "$n" =~ ^[1-9][0-9]*$ ]]; then
```

This changes the regex to require at least one digit 1-9 optionally followed by more digits, which correctly excludes `0`.

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

Comment thread lib/show.sh
Comment on lines +270 to +276
match)
if [[ -z "$3" ]]; then
echo "Usage: u7 sh log <file> match <pattern>"
return 1
fi
grep -- "$3" "$file"
;;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 grep exit code propagates as function failure on no-match

When grep finds no matching lines it exits with code 1, which becomes the return value of _u7_show. A caller checking $? (or a script using set -e) will see this as a command failure even though "no results" is not an error. Consider suppressing or distinguishing the no-match exit code:

Suggested change
match)
if [[ -z "$3" ]]; then
echo "Usage: u7 sh log <file> match <pattern>"
return 1
fi
grep -- "$3" "$file"
;;
match)
if [[ -z "$3" ]]; then
echo "Usage: u7 sh log <file> match <pattern>"
return 1
fi
grep -- "$3" "$file" || true
;;

Appending || true keeps the function exit code at 0 when there are simply no matches, while still showing any matched lines.

Prompt To Fix With AI
This is a comment left during a code review.
Path: lib/show.sh
Line: 270-276

Comment:
**`grep` exit code propagates as function failure on no-match**

When `grep` finds no matching lines it exits with code `1`, which becomes the return value of `_u7_show`. A caller checking `$?` (or a script using `set -e`) will see this as a command failure even though "no results" is not an error. Consider suppressing or distinguishing the no-match exit code:

```suggestion
        match)
          if [[ -z "$3" ]]; then
            echo "Usage: u7 sh log <file> match <pattern>"
            return 1
          fi
          grep -- "$3" "$file" || true
          ;;
```

Appending `|| true` keeps the function exit code at `0` when there are simply no matches, while still showing any matched lines.

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")) ;;
log) COMPREPLY=($(compgen -W "limit match follow" -- "$cur")) ; _filedir ;;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Completion offers keywords at every argument position

_u7_complete_args is called for any argument beyond the entity, so this line runs for both position 3 (where the user should type a <file>) and position 4+ (where a keyword is expected). The result is that limit, match, and follow appear alongside file-name completions even when typing the file path.

This is consistent with the existing check pattern at line 79, so it may be intentional. If you want position-aware completion you would need to inspect $cword relative to $entity_idx, but that is a larger refactor. As-is, it is a minor UX roughness rather than a functional bug.

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

Comment:
**Completion offers keywords at every argument position**

`_u7_complete_args` is called for any argument beyond the entity, so this line runs for both position 3 (where the user should type a `<file>`) and position 4+ (where a keyword is expected). The result is that `limit`, `match`, and `follow` appear alongside file-name completions even when typing the file path.

This is consistent with the existing `check` pattern at line 79, so it may be intentional. If you want position-aware completion you would need to inspect `$cword` relative to `$entity_idx`, but that is a larger refactor. As-is, it is a minor UX roughness rather than a functional bug.

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

@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 new log subcommand to the show entity, enabling users to view, follow, or search log files with optional line limits. The changes include shell completion updates in lib/completions.sh, the core implementation in lib/show.sh, and comprehensive test coverage in test.sh. Review feedback suggests refactoring the log logic to improve maintainability by centralizing the usage string and consolidating the limit and default cases to reduce duplication.

Comment thread lib/show.sh
Comment on lines +251 to +288
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

To improve maintainability and reduce code duplication, you can define the usage string in a variable and reuse it. Also, the limit and default ("") cases can be combined since they have similar logic.

    log)
      local file="$1"
      local usage="Usage: u7 sh log <file> [limit N|match <pattern>|follow]"
      if [[ -z "$file" ]]; then
        echo "$usage"
        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"
          ;;
        *)
          echo "$usage"
          return 1
          ;;
      esac
      ;;

@vitali87 vitali87 merged commit d943a92 into main Mar 27, 2026
3 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