Refactor entrypoint.sh, check.go, and run.go to support positional targets for URL scanning, enhancing flexibility. Update README.md to reflect changes in target handling and provide clearer usage instructions.

This commit is contained in:
Luke Hagar
2025-09-12 15:52:45 +00:00
parent c563c8026f
commit 1085baa766
4 changed files with 84 additions and 42 deletions

View File

@@ -51,14 +51,18 @@ go build -o slinky ./
Usage:
```bash
slinky check . --patterns "docs/**/*.md" --patterns "**/*.go" --md-out results.md --json-out results.json
# Headless: provide one or more targets (files, dirs, or globs)
slinky check **/*
slinky check ./docs/**/* ./markdown/**/*
# TUI mode: same targets
slinky run **/*
```
TUI mode:
```bash
slinky run . --patterns "**/*"
```
Notes:
- Targets can be files, directories, or doublestar globs. Multiple targets are allowed.
- If no targets are provided, the default is `**/*` relative to the current working directory.
- Legacy flags `--glob` and `--patterns` are still supported, but positional targets are preferred.
### Notes

View File

@@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"time"
@@ -29,24 +30,31 @@ type SerializableResult struct {
func init() {
checkCmd := &cobra.Command{
Use: "check [path]",
Short: "Scan a directory for URLs and validate them (headless)",
Args: cobra.MaximumNArgs(1),
Use: "check [targets...]",
Short: "Scan for URLs and validate them (headless)",
Args: cobra.ArbitraryArgs,
RunE: func(cmd *cobra.Command, args []string) error {
path := "."
if len(args) == 1 {
path = args[0]
}
var gl []string
if len(patterns) > 0 {
gl = append(gl, patterns...)
} else if globPat != "" {
gl = strings.Split(globPat, ",")
if len(args) > 0 {
for _, a := range args {
for _, part := range strings.Split(a, ",") {
p := strings.TrimSpace(part)
if p != "" {
gl = append(gl, toSlash(p))
}
}
}
} else {
gl = []string{"**/*"}
}
gl = expandDirectories(path, gl)
// Emit normalized patterns for debugging
fmt.Printf("::debug:: Effective patterns: %s\n", strings.Join(gl, ","))
timeout := time.Duration(timeoutSeconds) * time.Second
cfg := web.Config{MaxConcurrency: maxConcurrency, RequestTimeout: timeout}
@@ -149,8 +157,6 @@ func init() {
},
}
checkCmd.Flags().StringVar(&globPat, "glob", "", "comma-separated glob patterns for files (doublestar); empty = all files")
checkCmd.Flags().StringSliceVar(&patterns, "patterns", nil, "file match patterns (doublestar). Examples: docs/**/*.md,**/*.go; defaults to **/*")
checkCmd.Flags().IntVar(&maxConcurrency, "concurrency", 16, "maximum concurrent requests")
checkCmd.Flags().StringVar(&jsonOut, "json-out", "", "path to write full JSON results (array)")
checkCmd.Flags().StringVar(&mdOut, "md-out", "", "path to write Markdown report for PR comment")
@@ -165,7 +171,43 @@ func init() {
var (
timeoutSeconds int
failOnFailures bool
patterns []string
repoBlobBase string
respectGitignore bool
)
func toSlash(p string) string {
p = strings.TrimSpace(p)
if p == "" {
return p
}
p = filepath.ToSlash(p)
if after, ok := strings.CutPrefix(p, "./"); ok {
p = after
}
return p
}
func hasGlobMeta(s string) bool {
return strings.ContainsAny(s, "*?[")
}
func expandDirectories(root string, pats []string) []string {
var out []string
for _, p := range pats {
pp := strings.TrimSpace(p)
if pp == "" {
continue
}
if hasGlobMeta(pp) {
out = append(out, pp)
continue
}
abs := filepath.Join(root, filepath.FromSlash(pp))
if fi, err := os.Stat(abs); err == nil && fi.IsDir() {
out = append(out, strings.TrimSuffix(pp, "/")+"/**/*")
} else {
out = append(out, pp)
}
}
return out
}

View File

@@ -11,29 +11,28 @@ import (
func init() {
runCmd := &cobra.Command{
Use: "run [path]",
Use: "run [targets...]",
Short: "Scan a directory/repo for URLs in files and validate them (TUI)",
Args: cobra.MaximumNArgs(1),
Args: cobra.ArbitraryArgs,
RunE: func(cmd *cobra.Command, args []string) error {
path := "."
if len(args) == 1 {
path = args[0]
}
cfg := web.Config{MaxConcurrency: maxConcurrency}
var gl []string
if len(patterns) > 0 {
gl = append(gl, patterns...)
} else if globPat != "" {
gl = strings.Split(globPat, ",")
if len(args) > 0 {
for _, a := range args {
for _, part := range strings.Split(a, ",") {
p := strings.TrimSpace(part)
if p != "" {
gl = append(gl, p)
}
}
}
} else {
gl = []string{"**/*"}
}
return tui.Run(path, gl, cfg, jsonOut, mdOut)
return tui.Run(".", gl, cfg, jsonOut, mdOut)
},
}
runCmd.Flags().StringVar(&globPat, "glob", "", "comma-separated glob patterns for files (doublestar); empty = all files")
runCmd.Flags().StringSliceVar(&patterns, "patterns", nil, "file match patterns (doublestar). Examples: docs/**/*.md,**/*.go; defaults to **/*")
runCmd.Flags().IntVar(&maxConcurrency, "concurrency", 16, "maximum concurrent requests")
runCmd.Flags().StringVar(&jsonOut, "json-out", "", "path to write full JSON results (array)")
runCmd.Flags().StringVar(&mdOut, "md-out", "", "path to write Markdown report for PR comment")
@@ -44,6 +43,5 @@ func init() {
var (
maxConcurrency int
jsonOut string
globPat string
mdOut string
)

View File

@@ -14,7 +14,7 @@ COMMENT_PR_ARG="${INPUT_COMMENT_PR:-true}"
STEP_SUMMARY_ARG="${INPUT_STEP_SUMMARY:-true}"
# Build argv safely
set -- check "$PATH_ARG" --concurrency "$CONCURRENCY_ARG" --timeout "$TIMEOUT_ARG"
set -- check --concurrency "$CONCURRENCY_ARG" --timeout "$TIMEOUT_ARG"
if [ "${FAIL_ON_FAILURES_ARG}" = "true" ]; then
set -- "$@" --fail-on-failures=true
else
@@ -22,12 +22,9 @@ else
fi
if [ -n "${PATTERNS_ARG}" ]; then
NORM_PATTERNS=$(printf "%s" "${PATTERNS_ARG}" | sed 's/,[[:space:]]*/,/g')
IFS=','
for pat in $NORM_PATTERNS; do
set -- "$@" --patterns "$pat"
done
unset IFS
set -- "$@" "$PATTERNS_ARG"
else
set -- "$@" "**/*"
fi
if [ -n "${JSON_OUT_ARG}" ]; then
@@ -60,8 +57,9 @@ fi
# Emit consolidated config at start (visible with ACTIONS_STEP_DEBUG=true)
EFFECTIVE_REPO_BLOB_BASE="${SLINKY_REPO_BLOB_BASE_URL:-$REPO_BLOB_BASE_ARG}"
printf "::debug:: Config: path=%s patterns=%s concurrency=%s timeout=%s respect_gitignore=%s json_out=%s md_out=%s fail_on_failures=%s comment_pr=%s step_summary=%s repo_blob_base_url=%s\n" \
"$PATH_ARG" "$PATTERNS_ARG" "$CONCURRENCY_ARG" "$TIMEOUT_ARG" "$RESPECT_GITIGNORE_ARG" "$JSON_OUT_ARG" "$MD_OUT_ARG" \
TARGETS_DEBUG="${PATTERNS_ARG:-**/*}"
printf "::debug:: Config: targets=%s concurrency=%s timeout=%s respect_gitignore=%s json_out=%s md_out=%s fail_on_failures=%s comment_pr=%s step_summary=%s repo_blob_base_url=%s\n" \
"$TARGETS_DEBUG" "$CONCURRENCY_ARG" "$TIMEOUT_ARG" "$RESPECT_GITIGNORE_ARG" "$JSON_OUT_ARG" "$MD_OUT_ARG" \
"$FAIL_ON_FAILURES_ARG" "$COMMENT_PR_ARG" "$STEP_SUMMARY_ARG" "$EFFECTIVE_REPO_BLOB_BASE"
printf "::debug:: CLI Args: slinky %s\n" "$*"