opencode-homebrew-agent 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +175 -0
- package/agents/brew.md +146 -0
- package/package.json +22 -0
- package/src/AGENTS.md +172 -0
- package/src/scripts/brew-analyze.sh +373 -0
- package/src/scripts/brew-deps.sh +140 -0
- package/src/scripts/brew-env.sh +288 -0
- package/src/scripts/brew-search.sh +150 -0
- package/src/scripts/brew-template.sh +263 -0
- package/src/scripts/package.json +21 -0
- package/src/skills/homebrew-agent/SKILL.md +203 -0
- package/src/skills/homebrew-tap/SKILL.md +97 -0
- package/src/templates/taps/01-full-ci/AGENTS.md +71 -0
- package/src/templates/taps/01-full-ci/tap-structure/.github/workflows/autobump.yml +24 -0
- package/src/templates/taps/01-full-ci/tap-structure/.github/workflows/publish.yml +33 -0
- package/src/templates/taps/01-full-ci/tap-structure/.github/workflows/tests.yml +33 -0
- package/src/templates/taps/01-full-ci/tap-structure/Formula/_formula.rb +31 -0
- package/src/templates/taps/01-full-ci/tap-structure/README.md +27 -0
- package/src/templates/taps/01-full-ci/tap-structure/cmd/.gitkeep +0 -0
- package/src/templates/taps/01-full-ci/tap-structure/formula_renames.json +1 -0
- package/src/templates/taps/01-full-ci/tap-structure/lib/.gitkeep +0 -0
- package/src/templates/taps/01-full-ci/tap-structure/require/.gitkeep +0 -0
- package/src/templates/taps/01-full-ci/tap-structure/tap_migrations.json +1 -0
- package/src/templates/taps/01-full-ci/template.md +45 -0
- package/src/templates/taps/02-root-level/AGENTS.md +72 -0
- package/src/templates/taps/02-root-level/tap-structure/README.md +27 -0
- package/src/templates/taps/02-root-level/tap-structure/_formula.rb +29 -0
- package/src/templates/taps/02-root-level/template.md +35 -0
- package/src/templates/taps/03-simple-script/AGENTS.md +71 -0
- package/src/templates/taps/03-simple-script/tap-structure/Formula/_formula.rb +27 -0
- package/src/templates/taps/03-simple-script/tap-structure/README.md +27 -0
- package/src/templates/taps/03-simple-script/tap-structure/scripts/release.rb +46 -0
- package/src/templates/taps/03-simple-script/template.md +41 -0
- package/src/templates/taps/04-python-venv/AGENTS.md +83 -0
- package/src/templates/taps/04-python-venv/tap-structure/Formula/_formula.rb +57 -0
- package/src/templates/taps/04-python-venv/tap-structure/README.md +27 -0
- package/src/templates/taps/04-python-venv/tap-structure/scripts/release.rb +44 -0
- package/src/templates/taps/04-python-venv/template.md +58 -0
- package/src/templates/taps/05-node-npm/AGENTS.md +90 -0
- package/src/templates/taps/05-node-npm/tap-structure/Formula/_formula.rb +46 -0
- package/src/templates/taps/05-node-npm/tap-structure/README.md +27 -0
- package/src/templates/taps/05-node-npm/tap-structure/scripts/release.rb +40 -0
- package/src/templates/taps/05-node-npm/template.md +74 -0
- package/src/templates/taps/06-keg-only/AGENTS.md +82 -0
- package/src/templates/taps/06-keg-only/tap-structure/Formula/_formula.rb +45 -0
- package/src/templates/taps/06-keg-only/tap-structure/README.md +27 -0
- package/src/templates/taps/06-keg-only/template.md +60 -0
- package/src/templates/taps/07-cask-only/AGENTS.md +97 -0
- package/src/templates/taps/07-cask-only/tap-structure/Casks/_app.rb +26 -0
- package/src/templates/taps/07-cask-only/tap-structure/README.md +27 -0
- package/src/templates/taps/07-cask-only/template.md +58 -0
- package/src/templates/taps/08-go-binary/AGENTS.md +86 -0
- package/src/templates/taps/08-go-binary/tap-structure/Formula/_formula.rb +40 -0
- package/src/templates/taps/08-go-binary/tap-structure/README.md +27 -0
- package/src/templates/taps/08-go-binary/tap-structure/scripts/release.rb +38 -0
- package/src/templates/taps/08-go-binary/template.md +60 -0
- package/src/workflows/analyze-source.sh +124 -0
- package/src/workflows/convert-npm-to-bun.sh +112 -0
- package/src/workflows/create-tap.sh +196 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# brew-analyze.sh — Analyze a source directory or GitHub URL to determine package type
|
|
5
|
+
# and recommend a Homebrew tap template.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
usage() {
|
|
9
|
+
cat <<EOF
|
|
10
|
+
Usage: $(basename "$0") [OPTIONS]
|
|
11
|
+
|
|
12
|
+
Analyze a source directory or GitHub URL to determine the project type and
|
|
13
|
+
recommend a Homebrew tap template.
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
--dir <path> Analyze a local directory
|
|
17
|
+
--url <github-url> Analyze a GitHub repository (shallow clone)
|
|
18
|
+
--json Output as JSON (default for non-TTY)
|
|
19
|
+
--human Output as human-readable report (default for TTY)
|
|
20
|
+
--help Show this help message
|
|
21
|
+
|
|
22
|
+
Examples:
|
|
23
|
+
$(basename "$0") --dir ./my-project --json
|
|
24
|
+
$(basename "$0") --url https://github.com/user/repo
|
|
25
|
+
EOF
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# --- JSON helpers ---
|
|
29
|
+
|
|
30
|
+
json_escape() {
|
|
31
|
+
local str="$1"
|
|
32
|
+
str="${str//\\/\\\\}"
|
|
33
|
+
str="${str//\"/\\\"}"
|
|
34
|
+
str="${str//$'\n'/\\n}"
|
|
35
|
+
str="${str//$'\r'/\\r}"
|
|
36
|
+
str="${str//$'\t'/\\t}"
|
|
37
|
+
printf '%s' "$str"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
json_array() {
|
|
41
|
+
local items=("$@")
|
|
42
|
+
local first=true
|
|
43
|
+
printf '['
|
|
44
|
+
for item in "${items[@]}"; do
|
|
45
|
+
if [ "$first" = true ]; then
|
|
46
|
+
first=false
|
|
47
|
+
else
|
|
48
|
+
printf ','
|
|
49
|
+
fi
|
|
50
|
+
printf '"%s"' "$(json_escape "$item")"
|
|
51
|
+
done
|
|
52
|
+
printf ']'
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# --- Analysis functions ---
|
|
56
|
+
|
|
57
|
+
analyze_dir() {
|
|
58
|
+
local dir="$1"
|
|
59
|
+
local type="unknown"
|
|
60
|
+
local template="02"
|
|
61
|
+
local template_name="root-level"
|
|
62
|
+
local confidence="low"
|
|
63
|
+
local markers=()
|
|
64
|
+
local details=()
|
|
65
|
+
|
|
66
|
+
# 1. Cask: *.app bundle or Info.plist
|
|
67
|
+
if find "$dir" -maxdepth 2 -name "*.app" -type d 2>/dev/null | grep -q .; then
|
|
68
|
+
type="cask"
|
|
69
|
+
template="07"
|
|
70
|
+
template_name="cask-only"
|
|
71
|
+
confidence="high"
|
|
72
|
+
markers+=("*.app bundle")
|
|
73
|
+
details+=("has_app_bundle" "true")
|
|
74
|
+
elif [ -f "$dir/Info.plist" ]; then
|
|
75
|
+
type="cask"
|
|
76
|
+
template="07"
|
|
77
|
+
template_name="cask-only"
|
|
78
|
+
confidence="high"
|
|
79
|
+
markers+=("Info.plist")
|
|
80
|
+
details+=("has_info_plist" "true")
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
# 2. Rust: Cargo.toml
|
|
84
|
+
if [ "$type" = "unknown" ] && [ -f "$dir/Cargo.toml" ]; then
|
|
85
|
+
type="rust"
|
|
86
|
+
template="01"
|
|
87
|
+
template_name="full-ci"
|
|
88
|
+
confidence="high"
|
|
89
|
+
markers+=("Cargo.toml")
|
|
90
|
+
local cargo_ver=""
|
|
91
|
+
cargo_ver=$(grep -m1 '^version' "$dir/Cargo.toml" 2>/dev/null | sed 's/.*= *"\([^"]*\)".*/\1/' || true)
|
|
92
|
+
[ -n "$cargo_ver" ] && details+=("cargo_version" "$cargo_ver")
|
|
93
|
+
details+=("has_tests" "$([ -d "$dir/src" ] && echo "true" || echo "false")")
|
|
94
|
+
details+=("has_ci" "$([ -d "$dir/.github" ] && echo "true" || echo "false")")
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
# 3. Go binary: go.mod + (main.go or cmd/)
|
|
98
|
+
if [ "$type" = "unknown" ] && [ -f "$dir/go.mod" ]; then
|
|
99
|
+
local has_main=false
|
|
100
|
+
local has_cmd=false
|
|
101
|
+
[ -f "$dir/main.go" ] && has_main=true
|
|
102
|
+
[ -d "$dir/cmd" ] && has_cmd=true
|
|
103
|
+
|
|
104
|
+
if [ "$has_main" = true ] || [ "$has_cmd" = true ]; then
|
|
105
|
+
type="go-binary"
|
|
106
|
+
template="08"
|
|
107
|
+
template_name="go-binary"
|
|
108
|
+
confidence="high"
|
|
109
|
+
markers+=("go.mod")
|
|
110
|
+
[ "$has_main" = true ] && markers+=("main.go")
|
|
111
|
+
[ "$has_cmd" = true ] && markers+=("cmd/")
|
|
112
|
+
|
|
113
|
+
local go_ver=""
|
|
114
|
+
go_ver=$(grep -m1 '^go ' "$dir/go.mod" 2>/dev/null | awk '{print $2}' || true)
|
|
115
|
+
[ -n "$go_ver" ] && details+=("go_version" "$go_ver")
|
|
116
|
+
|
|
117
|
+
local entry_points=()
|
|
118
|
+
[ "$has_main" = true ] && entry_points+=("main.go")
|
|
119
|
+
[ "$has_cmd" = true ] && entry_points+=("cmd/")
|
|
120
|
+
|
|
121
|
+
details+=("has_tests" "$(find "$dir" -name '*_test.go' -print -quit 2>/dev/null | grep -q . && echo "true" || echo "false")")
|
|
122
|
+
details+=("has_ci" "$([ -d "$dir/.github" ] && echo "true" || echo "false")")
|
|
123
|
+
details+=("entry_points" "$(printf '%s' "${entry_points[*]}")")
|
|
124
|
+
fi
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
# 4. Python venv: pyproject.toml or setup.py or requirements.txt
|
|
128
|
+
if [ "$type" = "unknown" ]; then
|
|
129
|
+
local has_pyproject=false
|
|
130
|
+
local has_setup=false
|
|
131
|
+
local has_reqs=false
|
|
132
|
+
[ -f "$dir/pyproject.toml" ] && has_pyproject=true
|
|
133
|
+
[ -f "$dir/setup.py" ] && has_setup=true
|
|
134
|
+
[ -f "$dir/requirements.txt" ] && has_reqs=true
|
|
135
|
+
|
|
136
|
+
if [ "$has_pyproject" = true ] || [ "$has_setup" = true ] || [ "$has_reqs" = true ]; then
|
|
137
|
+
type="python-venv"
|
|
138
|
+
template="04"
|
|
139
|
+
template_name="python-venv"
|
|
140
|
+
confidence="high"
|
|
141
|
+
[ "$has_pyproject" = true ] && markers+=("pyproject.toml")
|
|
142
|
+
[ "$has_setup" = true ] && markers+=("setup.py")
|
|
143
|
+
[ "$has_reqs" = true ] && markers+=("requirements.txt")
|
|
144
|
+
|
|
145
|
+
local py_ver=""
|
|
146
|
+
if [ "$has_pyproject" = true ]; then
|
|
147
|
+
py_ver=$(grep -m1 '^requires-python' "$dir/pyproject.toml" 2>/dev/null | sed 's/.*= *"\([^"]*\)".*/\1/' || true)
|
|
148
|
+
fi
|
|
149
|
+
[ -n "$py_ver" ] && details+=("python_version" "$py_ver")
|
|
150
|
+
details+=("has_tests" "$([ -d "$dir/tests" ] || [ -d "$dir/test" ] && echo "true" || echo "false")")
|
|
151
|
+
details+=("has_ci" "$([ -d "$dir/.github" ] && echo "true" || echo "false")")
|
|
152
|
+
fi
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
# 5. Node npm: package.json with bin field
|
|
156
|
+
if [ "$type" = "unknown" ] && [ -f "$dir/package.json" ]; then
|
|
157
|
+
local has_bin=false
|
|
158
|
+
if grep -q '"bin"' "$dir/package.json" 2>/dev/null; then
|
|
159
|
+
has_bin=true
|
|
160
|
+
fi
|
|
161
|
+
|
|
162
|
+
if [ "$has_bin" = true ]; then
|
|
163
|
+
type="node-npm"
|
|
164
|
+
template="05"
|
|
165
|
+
template_name="node-npm"
|
|
166
|
+
confidence="high"
|
|
167
|
+
markers+=("package.json")
|
|
168
|
+
markers+=("bin field")
|
|
169
|
+
|
|
170
|
+
local node_ver=""
|
|
171
|
+
node_ver=$(grep -m1 '"node"' "$dir/package.json" 2>/dev/null | sed 's/.*: *"\([^"]*\)".*/\1/' || true)
|
|
172
|
+
[ -n "$node_ver" ] && details+=("node_version" "$node_ver")
|
|
173
|
+
details+=("has_tests" "$([ -d "$dir/test" ] || [ -d "$dir/tests" ] || [ -d "$dir/__tests__" ] && echo "true" || echo "false")")
|
|
174
|
+
details+=("has_ci" "$([ -d "$dir/.github" ] && echo "true" || echo "false")")
|
|
175
|
+
fi
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
# 6. Full CI: Makefile or configure.ac or CMakeLists.txt
|
|
179
|
+
if [ "$type" = "unknown" ]; then
|
|
180
|
+
local has_makefile=false
|
|
181
|
+
local has_configure=false
|
|
182
|
+
local has_cmake=false
|
|
183
|
+
[ -f "$dir/Makefile" ] || [ -f "$dir/makefile" ] || [ -f "$dir/GNUmakefile" ] && has_makefile=true
|
|
184
|
+
[ -f "$dir/configure.ac" ] || [ -f "$dir/configure.in" ] && has_configure=true
|
|
185
|
+
[ -f "$dir/CMakeLists.txt" ] && has_cmake=true
|
|
186
|
+
|
|
187
|
+
if [ "$has_makefile" = true ] || [ "$has_configure" = true ] || [ "$has_cmake" = true ]; then
|
|
188
|
+
type="full-ci"
|
|
189
|
+
template="01"
|
|
190
|
+
template_name="full-ci"
|
|
191
|
+
confidence="high"
|
|
192
|
+
[ "$has_makefile" = true ] && markers+=("Makefile")
|
|
193
|
+
[ "$has_configure" = true ] && markers+=("configure.ac")
|
|
194
|
+
[ "$has_cmake" = true ] && markers+=("CMakeLists.txt")
|
|
195
|
+
details+=("has_tests" "$([ -d "$dir/tests" ] || [ -d "$dir/test" ] && echo "true" || echo "false")")
|
|
196
|
+
details+=("has_ci" "$([ -d "$dir/.github" ] && echo "true" || echo "false")")
|
|
197
|
+
fi
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
# 7. Simple script: single .sh or .py file at root
|
|
201
|
+
if [ "$type" = "unknown" ]; then
|
|
202
|
+
local sh_count
|
|
203
|
+
local py_count
|
|
204
|
+
sh_count=$(find "$dir" -maxdepth 1 -name "*.sh" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
205
|
+
py_count=$(find "$dir" -maxdepth 1 -name "*.py" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
206
|
+
|
|
207
|
+
if [ "$sh_count" -eq 1 ] && [ "$py_count" -eq 0 ]; then
|
|
208
|
+
type="simple-script"
|
|
209
|
+
template="03"
|
|
210
|
+
template_name="simple-script"
|
|
211
|
+
confidence="medium"
|
|
212
|
+
markers+=("single .sh file")
|
|
213
|
+
local script_file
|
|
214
|
+
script_file=$(find "$dir" -maxdepth 1 -name "*.sh" -type f | head -1)
|
|
215
|
+
details+=("script_file" "$(basename "$script_file")")
|
|
216
|
+
elif [ "$py_count" -eq 1 ] && [ "$sh_count" -eq 0 ]; then
|
|
217
|
+
type="simple-script"
|
|
218
|
+
template="03"
|
|
219
|
+
template_name="simple-script"
|
|
220
|
+
confidence="medium"
|
|
221
|
+
markers+=("single .py file")
|
|
222
|
+
local script_file
|
|
223
|
+
script_file=$(find "$dir" -maxdepth 1 -name "*.py" -type f | head -1)
|
|
224
|
+
details+=("script_file" "$(basename "$script_file")")
|
|
225
|
+
fi
|
|
226
|
+
fi
|
|
227
|
+
|
|
228
|
+
# 8. Unknown fallback
|
|
229
|
+
if [ "$type" = "unknown" ]; then
|
|
230
|
+
confidence="low"
|
|
231
|
+
markers=()
|
|
232
|
+
fi
|
|
233
|
+
|
|
234
|
+
# Output results
|
|
235
|
+
local output_mode="json"
|
|
236
|
+
if [ -t 1 ] && [ "${HUMAN_FLAG:-}" != "true" ] && [ "${JSON_FLAG:-}" != "true" ]; then
|
|
237
|
+
output_mode="human"
|
|
238
|
+
elif [ "${HUMAN_FLAG:-}" = "true" ]; then
|
|
239
|
+
output_mode="human"
|
|
240
|
+
elif [ "${JSON_FLAG:-}" = "true" ]; then
|
|
241
|
+
output_mode="json"
|
|
242
|
+
fi
|
|
243
|
+
|
|
244
|
+
if [ "$output_mode" = "json" ]; then
|
|
245
|
+
printf '{'
|
|
246
|
+
printf '"type":"%s",' "$(json_escape "$type")"
|
|
247
|
+
printf '"template":"%s",' "$(json_escape "$template")"
|
|
248
|
+
printf '"template_name":"%s",' "$(json_escape "$template_name")"
|
|
249
|
+
printf '"confidence":"%s",' "$(json_escape "$confidence")"
|
|
250
|
+
printf '"markers":'
|
|
251
|
+
json_array "${markers[@]}"
|
|
252
|
+
printf ','
|
|
253
|
+
printf '"details":{'
|
|
254
|
+
local first_detail=true
|
|
255
|
+
local i=0
|
|
256
|
+
while [ $i -lt ${#details[@]} ]; do
|
|
257
|
+
local key="${details[$i]}"
|
|
258
|
+
local val="${details[$((i+1))]}"
|
|
259
|
+
if [ "$first_detail" = true ]; then
|
|
260
|
+
first_detail=false
|
|
261
|
+
else
|
|
262
|
+
printf ','
|
|
263
|
+
fi
|
|
264
|
+
printf '"%s":"%s"' "$(json_escape "$key")" "$(json_escape "$val")"
|
|
265
|
+
i=$((i+2))
|
|
266
|
+
done
|
|
267
|
+
printf '}'
|
|
268
|
+
printf '}\n'
|
|
269
|
+
else
|
|
270
|
+
printf 'Analysis Result\n'
|
|
271
|
+
printf '===============\n'
|
|
272
|
+
printf 'Type: %s\n' "$type"
|
|
273
|
+
printf 'Template: %s (%s)\n' "$template" "$template_name"
|
|
274
|
+
printf 'Confidence: %s\n' "$confidence"
|
|
275
|
+
printf 'Markers: %s\n' "${markers[*]:-none}"
|
|
276
|
+
printf 'Details:\n'
|
|
277
|
+
local i=0
|
|
278
|
+
while [ $i -lt ${#details[@]} ]; do
|
|
279
|
+
local key="${details[$i]}"
|
|
280
|
+
local val="${details[$((i+1))]}"
|
|
281
|
+
printf ' %s: %s\n' "$key" "$val"
|
|
282
|
+
i=$((i+2))
|
|
283
|
+
done
|
|
284
|
+
fi
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
# --- Main ---
|
|
288
|
+
|
|
289
|
+
DIR=""
|
|
290
|
+
URL=""
|
|
291
|
+
JSON_FLAG="false"
|
|
292
|
+
HUMAN_FLAG="false"
|
|
293
|
+
|
|
294
|
+
while [ $# -gt 0 ]; do
|
|
295
|
+
case "$1" in
|
|
296
|
+
--dir)
|
|
297
|
+
shift
|
|
298
|
+
if [ $# -eq 0 ]; then
|
|
299
|
+
echo "Error: --dir requires an argument" >&2
|
|
300
|
+
usage >&2
|
|
301
|
+
exit 1
|
|
302
|
+
fi
|
|
303
|
+
DIR="$1"
|
|
304
|
+
shift
|
|
305
|
+
;;
|
|
306
|
+
--url)
|
|
307
|
+
shift
|
|
308
|
+
if [ $# -eq 0 ]; then
|
|
309
|
+
echo "Error: --url requires an argument" >&2
|
|
310
|
+
usage >&2
|
|
311
|
+
exit 1
|
|
312
|
+
fi
|
|
313
|
+
URL="$1"
|
|
314
|
+
shift
|
|
315
|
+
;;
|
|
316
|
+
--json)
|
|
317
|
+
JSON_FLAG="true"
|
|
318
|
+
shift
|
|
319
|
+
;;
|
|
320
|
+
--human)
|
|
321
|
+
HUMAN_FLAG="true"
|
|
322
|
+
shift
|
|
323
|
+
;;
|
|
324
|
+
--help|-h)
|
|
325
|
+
usage
|
|
326
|
+
exit 0
|
|
327
|
+
;;
|
|
328
|
+
*)
|
|
329
|
+
echo "Error: Unknown option: $1" >&2
|
|
330
|
+
usage >&2
|
|
331
|
+
exit 1
|
|
332
|
+
;;
|
|
333
|
+
esac
|
|
334
|
+
done
|
|
335
|
+
|
|
336
|
+
if [ -n "$DIR" ] && [ -n "$URL" ]; then
|
|
337
|
+
echo "Error: Cannot specify both --dir and --url" >&2
|
|
338
|
+
exit 1
|
|
339
|
+
fi
|
|
340
|
+
|
|
341
|
+
if [ -z "$DIR" ] && [ -z "$URL" ]; then
|
|
342
|
+
echo "Error: Must specify either --dir or --url" >&2
|
|
343
|
+
usage >&2
|
|
344
|
+
exit 1
|
|
345
|
+
fi
|
|
346
|
+
|
|
347
|
+
if [ -n "$DIR" ]; then
|
|
348
|
+
if [ ! -d "$DIR" ]; then
|
|
349
|
+
echo "Error: Directory not found: $DIR" >&2
|
|
350
|
+
exit 1
|
|
351
|
+
fi
|
|
352
|
+
analyze_dir "$DIR"
|
|
353
|
+
fi
|
|
354
|
+
|
|
355
|
+
if [ -n "$URL" ]; then
|
|
356
|
+
# Validate GitHub URL
|
|
357
|
+
if ! [[ "$URL" =~ ^https://github\.com/ ]]; then
|
|
358
|
+
echo "Error: URL must be a GitHub repository URL" >&2
|
|
359
|
+
exit 1
|
|
360
|
+
fi
|
|
361
|
+
|
|
362
|
+
tmpdir=$(mktemp -d -p /tmp)
|
|
363
|
+
# shellcheck disable=SC2064
|
|
364
|
+
trap "rm -rf '$tmpdir'" EXIT
|
|
365
|
+
|
|
366
|
+
echo "Cloning $URL ..." >&2
|
|
367
|
+
if ! git clone --depth 1 "$URL" "$tmpdir" 2>/dev/null; then
|
|
368
|
+
echo "Error: Failed to clone repository" >&2
|
|
369
|
+
exit 1
|
|
370
|
+
fi
|
|
371
|
+
|
|
372
|
+
analyze_dir "$tmpdir"
|
|
373
|
+
fi
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT_NAME="$(basename "$0")"
|
|
5
|
+
|
|
6
|
+
usage() {
|
|
7
|
+
cat <<EOF
|
|
8
|
+
Usage: ${SCRIPT_NAME} [OPTIONS] <formula>
|
|
9
|
+
|
|
10
|
+
Show dependencies and reverse dependencies for a Homebrew formula.
|
|
11
|
+
|
|
12
|
+
Options:
|
|
13
|
+
--tree Show dependency tree (default)
|
|
14
|
+
--flat Show flat dependency list
|
|
15
|
+
--reverse, -r Show only reverse dependencies
|
|
16
|
+
--json Output as JSON
|
|
17
|
+
--help Show this help message
|
|
18
|
+
|
|
19
|
+
Examples:
|
|
20
|
+
${SCRIPT_NAME} git
|
|
21
|
+
${SCRIPT_NAME} --reverse git
|
|
22
|
+
${SCRIPT_NAME} --json git
|
|
23
|
+
${SCRIPT_NAME} --flat git
|
|
24
|
+
EOF
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
main() {
|
|
28
|
+
local show_tree=true
|
|
29
|
+
local show_reverse=false
|
|
30
|
+
local show_flat=false
|
|
31
|
+
local json_output=false
|
|
32
|
+
local formula=""
|
|
33
|
+
|
|
34
|
+
# Parse flags
|
|
35
|
+
while [[ $# -gt 0 ]]; do
|
|
36
|
+
case "$1" in
|
|
37
|
+
--help)
|
|
38
|
+
usage
|
|
39
|
+
exit 0
|
|
40
|
+
;;
|
|
41
|
+
--tree)
|
|
42
|
+
show_tree=true
|
|
43
|
+
show_flat=false
|
|
44
|
+
shift
|
|
45
|
+
;;
|
|
46
|
+
--flat)
|
|
47
|
+
show_flat=true
|
|
48
|
+
show_tree=false
|
|
49
|
+
shift
|
|
50
|
+
;;
|
|
51
|
+
--reverse | --rev)
|
|
52
|
+
show_reverse=true
|
|
53
|
+
shift
|
|
54
|
+
;;
|
|
55
|
+
--json)
|
|
56
|
+
json_output=true
|
|
57
|
+
shift
|
|
58
|
+
;;
|
|
59
|
+
-*)
|
|
60
|
+
echo "Error: Unknown option $1" >&2
|
|
61
|
+
usage >&2
|
|
62
|
+
exit 1
|
|
63
|
+
;;
|
|
64
|
+
*)
|
|
65
|
+
if [[ -z "$formula" ]]; then
|
|
66
|
+
formula="$1"
|
|
67
|
+
else
|
|
68
|
+
echo "Error: Only one formula argument allowed" >&2
|
|
69
|
+
usage >&2
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
shift
|
|
73
|
+
;;
|
|
74
|
+
esac
|
|
75
|
+
done
|
|
76
|
+
|
|
77
|
+
if [[ -z "$formula" ]]; then
|
|
78
|
+
echo "Error: No formula specified" >&2
|
|
79
|
+
usage >&2
|
|
80
|
+
exit 1
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
# Validate formula exists
|
|
84
|
+
if ! brew info "$formula" >/dev/null 2>&1; then
|
|
85
|
+
echo "Error: Formula '${formula}' not found" >&2
|
|
86
|
+
exit 1
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
if [[ "$json_output" == true ]]; then
|
|
90
|
+
# JSON output: capture deps and reverse deps as arrays
|
|
91
|
+
local deps_json revdeps_json
|
|
92
|
+
|
|
93
|
+
deps_json=$(brew deps --include-build "$formula" 2>/dev/null | jq -R . | jq -s . || echo "[]")
|
|
94
|
+
revdeps_json=$(brew uses --recursive --installed "$formula" 2>/dev/null | jq -R . | jq -s . || echo "[]")
|
|
95
|
+
|
|
96
|
+
cat <<EOF
|
|
97
|
+
{
|
|
98
|
+
"formula": "${formula}",
|
|
99
|
+
"dependencies": ${deps_json},
|
|
100
|
+
"reverse_dependencies": ${revdeps_json}
|
|
101
|
+
}
|
|
102
|
+
EOF
|
|
103
|
+
exit 0
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
if [[ "$show_reverse" == true ]]; then
|
|
107
|
+
echo "=== Reverse Dependencies ==="
|
|
108
|
+
echo ""
|
|
109
|
+
if ! brew uses --recursive --installed "$formula" 2>/dev/null; then
|
|
110
|
+
echo " (none)"
|
|
111
|
+
fi
|
|
112
|
+
exit 0
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
if [[ "$show_flat" == true ]]; then
|
|
116
|
+
echo "=== Dependencies ==="
|
|
117
|
+
echo ""
|
|
118
|
+
brew deps --include-build "$formula" 2>/dev/null || true
|
|
119
|
+
echo ""
|
|
120
|
+
echo "=== Reverse Dependencies ==="
|
|
121
|
+
echo ""
|
|
122
|
+
if ! brew uses --recursive --installed "$formula" 2>/dev/null; then
|
|
123
|
+
echo " (none)"
|
|
124
|
+
fi
|
|
125
|
+
exit 0
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
# Default: tree view
|
|
129
|
+
echo "=== Dependencies ==="
|
|
130
|
+
echo ""
|
|
131
|
+
brew deps --tree --include-build "$formula" 2>/dev/null || true
|
|
132
|
+
echo ""
|
|
133
|
+
echo "=== Reverse Dependencies ==="
|
|
134
|
+
echo ""
|
|
135
|
+
if ! brew uses --recursive --installed "$formula" 2>/dev/null; then
|
|
136
|
+
echo " (none)"
|
|
137
|
+
fi
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
main "$@"
|