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,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: homebrew-tap-go-binary
|
|
3
|
+
description: Creates and maintains Homebrew taps for Go CLI tools that build from source.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# homebrew-tap-go-binary
|
|
7
|
+
|
|
8
|
+
Agent for working with Go Binary tap templates.
|
|
9
|
+
|
|
10
|
+
## Description
|
|
11
|
+
Helps create and maintain Homebrew taps for Go CLI tools that build from source.
|
|
12
|
+
|
|
13
|
+
## Stacks
|
|
14
|
+
- homebrew
|
|
15
|
+
- go
|
|
16
|
+
- ruby
|
|
17
|
+
|
|
18
|
+
## Requires
|
|
19
|
+
- basic-homebrew-knowledge
|
|
20
|
+
|
|
21
|
+
## Key Actions
|
|
22
|
+
|
|
23
|
+
### Go Build Pattern
|
|
24
|
+
```ruby
|
|
25
|
+
depends_on "go" => :build
|
|
26
|
+
|
|
27
|
+
def install
|
|
28
|
+
system "go", "build", *std_go_args(ldflags: "-s -w -X main.version=#{version}")
|
|
29
|
+
end
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### With Module Path
|
|
33
|
+
```ruby
|
|
34
|
+
def install
|
|
35
|
+
ENV["GOPATH"] = buildpath
|
|
36
|
+
path = buildpath/"src/github.com/<user>/<repo>"
|
|
37
|
+
path.install Dir["*"]
|
|
38
|
+
cd path do
|
|
39
|
+
system "go", "build", *std_go_args(ldflags: "-s -w -X main.version=#{version}")
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Common ldflags
|
|
45
|
+
- `-s -w` — strip debug information (smaller binary)
|
|
46
|
+
- `-X main.version=#{version}` — inject version at build time
|
|
47
|
+
- `-X main.commit=#{Utils.git_head}` — inject commit hash
|
|
48
|
+
|
|
49
|
+
### Permissions
|
|
50
|
+
- Read/write to `Formula/` and `scripts/`
|
|
51
|
+
- Network access for Go module downloads
|
|
52
|
+
|
|
53
|
+
### Common Pitfalls
|
|
54
|
+
|
|
55
|
+
- `depends_on "go" => :build` is required — without `=> :build`, Go becomes a runtime dependency and users must install it to use your binary.
|
|
56
|
+
- `ldflags` must be a single string: `"-s -w -X main.version=#{version}"`. Multiple arguments will cause a `go build` error.
|
|
57
|
+
- Module-aware projects (Go 1.11+) should NOT use the GOPATH layout. Uncomment the GOPATH `def install` only for legacy projects.
|
|
58
|
+
|
|
59
|
+
### LSP Validation
|
|
60
|
+
|
|
61
|
+
Before finishing any formula, validate with the Ruby LSP:
|
|
62
|
+
|
|
63
|
+
1. Ensure LSP is available:
|
|
64
|
+
```sh
|
|
65
|
+
ruby-lsp --version # Verify LSP is installed
|
|
66
|
+
```
|
|
67
|
+
2. Run syntax check:
|
|
68
|
+
```sh
|
|
69
|
+
ruby -c Formula/<formula>.rb
|
|
70
|
+
```
|
|
71
|
+
3. Run Homebrew linting:
|
|
72
|
+
```sh
|
|
73
|
+
brew style --tap <user>/<tapname> Formula/<formula>.rb
|
|
74
|
+
```
|
|
75
|
+
4. Run formula audit:
|
|
76
|
+
```sh
|
|
77
|
+
brew audit --new-formula Formula/<formula>.rb
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Do NOT mark work as complete until all diagnostics pass.
|
|
81
|
+
|
|
82
|
+
### Verification
|
|
83
|
+
```sh
|
|
84
|
+
ruby -c Formula/<formula>.rb
|
|
85
|
+
brew style --tap <user>/<tapname> Formula/<formula>.rb
|
|
86
|
+
```
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
|
|
2
|
+
# Go CLI tool formula — builds from source.
|
|
3
|
+
# Replace <formula>, <description>, <url>, <sha256> with real values.
|
|
4
|
+
# IMPORTANT: Rename class <Formula> to CamelCase matching the filename (e.g., MyTool for my-tool.rb).
|
|
5
|
+
|
|
6
|
+
class <Formula> < Formula
|
|
7
|
+
desc "<one-line description>"
|
|
8
|
+
homepage "https://github.com/<user>/<repo>"
|
|
9
|
+
url "https://github.com/<user>/<repo>/archive/refs/tags/v<version>.tar.gz"
|
|
10
|
+
sha256 "<sha256>"
|
|
11
|
+
version "<version>"
|
|
12
|
+
license "MIT"
|
|
13
|
+
|
|
14
|
+
depends_on "go" => :build
|
|
15
|
+
|
|
16
|
+
livecheck do
|
|
17
|
+
url :stable
|
|
18
|
+
regex(/^v?(\d+(?:\.\d+)+)$/i)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def install
|
|
22
|
+
ldflags = "-s -w -X main.version=#{version}"
|
|
23
|
+
system "go", "build", *std_go_args(ldflags: ldflags)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Alternative: traditional GOPATH layout
|
|
27
|
+
# def install
|
|
28
|
+
# ENV["GOPATH"] = buildpath
|
|
29
|
+
# path = buildpath/"src/github.com/<user>/<repo>"
|
|
30
|
+
# path.install Dir["*"]
|
|
31
|
+
# cd path do
|
|
32
|
+
# ldflags = "-s -w -X main.version=#{version}"
|
|
33
|
+
# system "go", "build", *std_go_args(ldflags: ldflags)
|
|
34
|
+
# end
|
|
35
|
+
# end
|
|
36
|
+
|
|
37
|
+
test do
|
|
38
|
+
assert_match version.to_s, shell_output("#{bin}/<cli> --version")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# <Tap Name>
|
|
2
|
+
|
|
3
|
+
Homebrew tap for <formula>.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
brew tap <user>/<tapname>
|
|
9
|
+
brew install <formula>
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Upgrade
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
brew upgrade <formula>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Uninstall
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
brew uninstall <formula>
|
|
22
|
+
brew untap <user>/<tapname>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## License
|
|
26
|
+
|
|
27
|
+
MIT
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Version bump script for Go binary formula.
|
|
5
|
+
# Usage: ruby scripts/update-formula.rb <version>
|
|
6
|
+
|
|
7
|
+
require "open-uri"
|
|
8
|
+
require "digest"
|
|
9
|
+
|
|
10
|
+
TAP_DIR = File.dirname(__dir__)
|
|
11
|
+
FORMULA_FILE = File.join(TAP_DIR, "Formula", "_formula.rb") # rename to your formula name
|
|
12
|
+
|
|
13
|
+
def usage
|
|
14
|
+
puts "Usage: ruby scripts/update-formula.rb <version>"
|
|
15
|
+
puts " e.g., ruby scripts/update-formula.rb 1.2.3"
|
|
16
|
+
exit 1
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def fetch_sha256(url)
|
|
20
|
+
uri = URI.parse(url)
|
|
21
|
+
puts " Downloading #{url}..."
|
|
22
|
+
content = URI.open(uri).read
|
|
23
|
+
Digest::SHA256.hexdigest(content)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
version = ARGV.first || usage()
|
|
27
|
+
tarball_url = "https://github.com/<user>/<repo>/archive/refs/tags/v#{version}.tar.gz"
|
|
28
|
+
|
|
29
|
+
puts "Updating Formula/<formula>.rb to version #{version}..."
|
|
30
|
+
sha = fetch_sha256(tarball_url)
|
|
31
|
+
|
|
32
|
+
formula_content = File.read(FORMULA_FILE)
|
|
33
|
+
formula_content.sub!(/version "\d+\.\d+\.\d+"/, "version \"#{version}\"")
|
|
34
|
+
formula_content.sub!(/sha256 "[0-9a-f]{64}"/, "sha256 \"#{sha}\"")
|
|
35
|
+
|
|
36
|
+
File.write(FORMULA_FILE, formula_content)
|
|
37
|
+
puts "Done. Formula updated to v#{version}."
|
|
38
|
+
puts "Review: git diff Formula/<formula>.rb"
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Template 08: Go Binary
|
|
2
|
+
|
|
3
|
+
## Pattern: CLI tools written in Go
|
|
4
|
+
|
|
5
|
+
For Go CLI tools that build from source using `go build`. Common for
|
|
6
|
+
modern cloud/CLI tools (e.g., `gh`, `docker`, `kubectl` clone). Uses
|
|
7
|
+
`depends_on "go" => :build` and compiles during `brew install`.
|
|
8
|
+
|
|
9
|
+
### Structure
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
homebrew-<tap>/
|
|
13
|
+
├── Formula/
|
|
14
|
+
│ └── <formula>.rb # Formula with Go build
|
|
15
|
+
├── scripts/
|
|
16
|
+
│ └── release.rb # (optional) Version bump script
|
|
17
|
+
├── .gitignore
|
|
18
|
+
├── LICENSE
|
|
19
|
+
└── README.md
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Key Formula Pattern
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
depends_on "go" => :build
|
|
26
|
+
|
|
27
|
+
def install
|
|
28
|
+
system "go", "build", *std_go_args(ldflags: "-s -w -X main.version=#{version}")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Alternative: traditional GOPATH layout (uncomment if needed)
|
|
32
|
+
# def install
|
|
33
|
+
# ENV["GOPATH"] = buildpath
|
|
34
|
+
# path = buildpath/"src/github.com/<user>/<repo>"
|
|
35
|
+
# path.install Dir["*"]
|
|
36
|
+
# cd path do
|
|
37
|
+
# system "go", "build", *std_go_args(ldflags: "-s -w -X main.version=#{version}")
|
|
38
|
+
# end
|
|
39
|
+
# end
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### When to Use
|
|
43
|
+
|
|
44
|
+
- Your CLI tool is written in Go
|
|
45
|
+
- The source is available on GitHub
|
|
46
|
+
- You want to build from source (not download a pre-built binary)
|
|
47
|
+
- You want to set version via `ldflags` at build time
|
|
48
|
+
|
|
49
|
+
### When NOT to Use
|
|
50
|
+
|
|
51
|
+
- The Go tool is distributed as a pre-built binary (use template 03 instead)
|
|
52
|
+
- The tool requires CGo with external C libraries (add `depends_on` as needed)
|
|
53
|
+
|
|
54
|
+
### Reference
|
|
55
|
+
|
|
56
|
+
- `std_go_args` is a Homebrew helper that sets `-o`, `-trimpath`, etc.
|
|
57
|
+
- Common `ldflags`: `-s -w` (strip debug), `-X main.version=#{version}` (inject version)
|
|
58
|
+
- For module-based projects, `GOPROXY` may need setting via `ENV`
|
|
59
|
+
|
|
60
|
+
- Start with `brew tap-new <user>/homebrew-<tapname>` to generate the CI skeleton (optional — manual structure works too)
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# workflows/analyze-source.sh
|
|
3
|
+
# Rich wrapper around brew-analyze.sh with human-readable output.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
8
|
+
ANALYZE="${PROJECT_ROOT}/scripts/brew-analyze.sh"
|
|
9
|
+
|
|
10
|
+
# --- Usage ---
|
|
11
|
+
usage() {
|
|
12
|
+
cat << 'EOF'
|
|
13
|
+
Usage: analyze-source.sh [OPTIONS]
|
|
14
|
+
|
|
15
|
+
Analyze a source directory or GitHub URL and recommend a Homebrew template.
|
|
16
|
+
|
|
17
|
+
Options:
|
|
18
|
+
--dir <path> Analyze local directory
|
|
19
|
+
--url <github-url> Analyze GitHub URL
|
|
20
|
+
--json Output raw JSON
|
|
21
|
+
--human Output human-readable report (default)
|
|
22
|
+
--help Show this help message
|
|
23
|
+
EOF
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# --- Parse args ---
|
|
27
|
+
DIR=""
|
|
28
|
+
URL=""
|
|
29
|
+
JSON_MODE=false
|
|
30
|
+
HUMAN_MODE=false
|
|
31
|
+
|
|
32
|
+
while [[ $# -gt 0 ]]; do
|
|
33
|
+
case "$1" in
|
|
34
|
+
--dir) DIR="$2"; shift 2 ;;
|
|
35
|
+
--url) URL="$2"; shift 2 ;;
|
|
36
|
+
--json) JSON_MODE=true; shift ;;
|
|
37
|
+
--human) HUMAN_MODE=true; shift ;;
|
|
38
|
+
--help) usage; exit 0 ;;
|
|
39
|
+
*) echo "error: unknown option: $1" >&2; usage >&2; exit 1 ;;
|
|
40
|
+
esac
|
|
41
|
+
done
|
|
42
|
+
|
|
43
|
+
# --- Determine mode ---
|
|
44
|
+
if $JSON_MODE; then
|
|
45
|
+
if [[ -n "$DIR" ]]; then
|
|
46
|
+
"$ANALYZE" --dir "$DIR" --json
|
|
47
|
+
elif [[ -n "$URL" ]]; then
|
|
48
|
+
"$ANALYZE" --url "$URL" --json
|
|
49
|
+
else
|
|
50
|
+
echo "error: --dir or --url required" >&2
|
|
51
|
+
usage >&2
|
|
52
|
+
exit 1
|
|
53
|
+
fi
|
|
54
|
+
exit 0
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# --- Run analysis ---
|
|
58
|
+
if [[ -n "$DIR" ]]; then
|
|
59
|
+
ANALYSIS=$("$ANALYZE" --dir "$DIR" --json 2>/dev/null)
|
|
60
|
+
SOURCE="$DIR"
|
|
61
|
+
elif [[ -n "$URL" ]]; then
|
|
62
|
+
ANALYSIS=$("$ANALYZE" --url "$URL" --json 2>/dev/null)
|
|
63
|
+
SOURCE="$URL"
|
|
64
|
+
else
|
|
65
|
+
echo "error: --dir or --url required" >&2
|
|
66
|
+
usage >&2
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
# --- Parse JSON ---
|
|
71
|
+
TYPE=$(echo "$ANALYSIS" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("type","unknown"))')
|
|
72
|
+
TEMPLATE=$(echo "$ANALYSIS" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("template","02"))')
|
|
73
|
+
TEMPLATE_NAME=$(echo "$ANALYSIS" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("template_name","root-level"))')
|
|
74
|
+
CONFIDENCE=$(echo "$ANALYSIS" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("confidence","low"))')
|
|
75
|
+
MARKERS=$(echo "$ANALYSIS" | python3 -c 'import sys,json; print(" ".join(json.load(sys.stdin).get("markers",[])))')
|
|
76
|
+
|
|
77
|
+
# --- Complexity estimation ---
|
|
78
|
+
COMPLEXITY="simple"
|
|
79
|
+
case "$TYPE" in
|
|
80
|
+
simple-script|root-level) COMPLEXITY="simple" ;;
|
|
81
|
+
node-npm|python-venv|go-binary) COMPLEXITY="medium" ;;
|
|
82
|
+
full-ci|cask|keg-only) COMPLEXITY="complex" ;;
|
|
83
|
+
*) COMPLEXITY="unknown" ;;
|
|
84
|
+
esac
|
|
85
|
+
|
|
86
|
+
# --- Print report ---
|
|
87
|
+
cat << EOF
|
|
88
|
+
|
|
89
|
+
Source Analysis Report
|
|
90
|
+
======================
|
|
91
|
+
Source: $SOURCE
|
|
92
|
+
|
|
93
|
+
Detected Type
|
|
94
|
+
-------------
|
|
95
|
+
Project type: $TYPE
|
|
96
|
+
Template: $TEMPLATE ($TEMPLATE_NAME)
|
|
97
|
+
Confidence: $CONFIDENCE
|
|
98
|
+
Markers: $MARKERS
|
|
99
|
+
|
|
100
|
+
Complexity Estimate
|
|
101
|
+
-------------------
|
|
102
|
+
$COMPLEXITY
|
|
103
|
+
|
|
104
|
+
Next Steps
|
|
105
|
+
----------
|
|
106
|
+
To scaffold this as a Homebrew tap, run:
|
|
107
|
+
|
|
108
|
+
bash workflows/create-tap.sh --source "$SOURCE" --name <FormulaName> --tap <user>/homebrew-<tap>
|
|
109
|
+
|
|
110
|
+
Or with all options:
|
|
111
|
+
|
|
112
|
+
bash workflows/create-tap.sh \
|
|
113
|
+
--source "$SOURCE" \
|
|
114
|
+
--name MyCli \
|
|
115
|
+
--tap myuser/homebrew-mycli \
|
|
116
|
+
--output ./mytap \
|
|
117
|
+
--non-interactive
|
|
118
|
+
|
|
119
|
+
EOF
|
|
120
|
+
|
|
121
|
+
if [[ "$TYPE" == "node-npm" ]]; then
|
|
122
|
+
echo "Note: This project uses npm. You can convert to bun with --convert-to-bun"
|
|
123
|
+
echo ""
|
|
124
|
+
fi
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# workflows/convert-npm-to-bun.sh
|
|
3
|
+
# Convert an npm-based Homebrew formula to use bun.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
# --- Usage ---
|
|
7
|
+
usage() {
|
|
8
|
+
cat << 'EOF'
|
|
9
|
+
Usage: convert-npm-to-bun.sh [OPTIONS]
|
|
10
|
+
|
|
11
|
+
Convert an npm-based Homebrew formula to use bun.
|
|
12
|
+
|
|
13
|
+
Options:
|
|
14
|
+
--formula <path> Path to the formula .rb file
|
|
15
|
+
--check Only check if conversion is possible
|
|
16
|
+
--dry-run Show what would change without modifying
|
|
17
|
+
--help Show this help message
|
|
18
|
+
|
|
19
|
+
Examples:
|
|
20
|
+
convert-npm-to-bun.sh --formula Formula/my-cli.rb --dry-run
|
|
21
|
+
convert-npm-to-bun.sh --formula Formula/my-cli.rb
|
|
22
|
+
EOF
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# --- Parse args ---
|
|
26
|
+
FORMULA=""
|
|
27
|
+
CHECK=false
|
|
28
|
+
DRY_RUN=false
|
|
29
|
+
|
|
30
|
+
while [[ $# -gt 0 ]]; do
|
|
31
|
+
case "$1" in
|
|
32
|
+
--formula) FORMULA="$2"; shift 2 ;;
|
|
33
|
+
--check) CHECK=true; shift ;;
|
|
34
|
+
--dry-run) DRY_RUN=true; shift ;;
|
|
35
|
+
--help) usage; exit 0 ;;
|
|
36
|
+
*) echo "error: unknown option: $1" >&2; usage >&2; exit 1 ;;
|
|
37
|
+
esac
|
|
38
|
+
done
|
|
39
|
+
|
|
40
|
+
# --- Validate ---
|
|
41
|
+
if [[ -z "$FORMULA" ]]; then
|
|
42
|
+
echo "error: --formula is required" >&2
|
|
43
|
+
usage >&2
|
|
44
|
+
exit 1
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
if [[ ! -f "$FORMULA" ]]; then
|
|
48
|
+
echo "error: file not found: $FORMULA" >&2
|
|
49
|
+
exit 1
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# --- Check for bun ---
|
|
53
|
+
if ! command -v bun >/dev/null 2>&1; then
|
|
54
|
+
echo "warning: bun is not installed. Install with: brew install oven-sh/bun/bun" >&2
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# --- Analyze formula ---
|
|
58
|
+
if grep -q 'depends_on.*"bun"' "$FORMULA"; then
|
|
59
|
+
echo "info: formula already uses bun — nothing to convert" >&2
|
|
60
|
+
exit 0
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
if ! grep -q 'depends_on.*"node"' "$FORMULA"; then
|
|
64
|
+
echo "error: no npm/node dependency found in formula" >&2
|
|
65
|
+
exit 1
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
if $CHECK; then
|
|
69
|
+
echo "info: conversion is possible (npm formula detected)" >&2
|
|
70
|
+
exit 0
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
# --- Compute changes ---
|
|
74
|
+
TMP_FORMULA=$(mktemp -p /tmp)
|
|
75
|
+
trap 'rm -f "$TMP_FORMULA"' EXIT
|
|
76
|
+
|
|
77
|
+
cp "$FORMULA" "$TMP_FORMULA"
|
|
78
|
+
|
|
79
|
+
# Replace node dependency with bun
|
|
80
|
+
sed -i.bak 's/depends_on "node"/depends_on "bun"/' "$TMP_FORMULA"
|
|
81
|
+
|
|
82
|
+
# Replace npm install with bun install
|
|
83
|
+
sed -i.bak 's/system "npm", "install", \*std_npm_args/system "bun", "install"/' "$TMP_FORMULA"
|
|
84
|
+
sed -i.bak 's/system "npm", "install"/system "bun", "install"/' "$TMP_FORMULA"
|
|
85
|
+
|
|
86
|
+
# Add bun tap note comment after the bun dependency line
|
|
87
|
+
awk '/depends_on "bun"/ { print; print " # NOTE: bun is provided by oven-sh/bun tap: brew tap oven-sh/bun"; next } 1' "$TMP_FORMULA" > "${TMP_FORMULA}.tmp"
|
|
88
|
+
mv "${TMP_FORMULA}.tmp" "$TMP_FORMULA"
|
|
89
|
+
|
|
90
|
+
rm -f "$TMP_FORMULA.bak"
|
|
91
|
+
|
|
92
|
+
# --- Show diff ---
|
|
93
|
+
if $DRY_RUN; then
|
|
94
|
+
echo "=== Proposed changes ===" >&2
|
|
95
|
+
diff -u "$FORMULA" "$TMP_FORMULA" || true
|
|
96
|
+
echo "" >&2
|
|
97
|
+
echo "Run without --dry-run to apply changes" >&2
|
|
98
|
+
exit 0
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# --- Apply changes ---
|
|
102
|
+
cp "$TMP_FORMULA" "$FORMULA"
|
|
103
|
+
|
|
104
|
+
# --- Validate ---
|
|
105
|
+
echo "=== Validating converted formula ===" >&2
|
|
106
|
+
if ruby -c "$FORMULA" >&2; then
|
|
107
|
+
echo "Formula syntax: OK" >&2
|
|
108
|
+
echo "Conversion complete: $FORMULA" >&2
|
|
109
|
+
else
|
|
110
|
+
echo "error: converted formula has syntax errors" >&2
|
|
111
|
+
exit 1
|
|
112
|
+
fi
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# workflows/create-tap.sh
|
|
3
|
+
# End-to-end workflow: analyze source, recommend template, scaffold tap.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
8
|
+
ANALYZE="${PROJECT_ROOT}/scripts/brew-analyze.sh"
|
|
9
|
+
TEMPLATE="${PROJECT_ROOT}/scripts/brew-template.sh"
|
|
10
|
+
|
|
11
|
+
# --- Defaults ---
|
|
12
|
+
SOURCE=""
|
|
13
|
+
NAME=""
|
|
14
|
+
TAP=""
|
|
15
|
+
OUTPUT=""
|
|
16
|
+
NON_INTERACTIVE=false
|
|
17
|
+
CONVERT_TO_BUN=false
|
|
18
|
+
|
|
19
|
+
# --- Usage ---
|
|
20
|
+
usage() {
|
|
21
|
+
cat << 'EOF'
|
|
22
|
+
Usage: create-tap.sh [OPTIONS]
|
|
23
|
+
|
|
24
|
+
Create a Homebrew tap from a source directory or GitHub URL.
|
|
25
|
+
|
|
26
|
+
Options:
|
|
27
|
+
--source <url|path> Source to analyze (GitHub URL or local directory)
|
|
28
|
+
--name <formula> Formula name (CamelCase or kebab-case)
|
|
29
|
+
--tap <user/tap> Tap identifier (e.g., user/homebrew-mytap)
|
|
30
|
+
--output <path> Output directory for the tap
|
|
31
|
+
--non-interactive Skip all prompts
|
|
32
|
+
--convert-to-bun Prefer bun over npm for node projects
|
|
33
|
+
--help Show this help message
|
|
34
|
+
|
|
35
|
+
Examples:
|
|
36
|
+
create-tap.sh --source https://github.com/user/cli-tool --name mycli --tap myuser/homebrew-mycli --output ./mytap --non-interactive
|
|
37
|
+
create-tap.sh --source /path/to/project --non-interactive
|
|
38
|
+
EOF
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# --- Parse args ---
|
|
42
|
+
while [[ $# -gt 0 ]]; do
|
|
43
|
+
case "$1" in
|
|
44
|
+
--source) SOURCE="$2"; shift 2 ;;
|
|
45
|
+
--name) NAME="$2"; shift 2 ;;
|
|
46
|
+
--tap) TAP="$2"; shift 2 ;;
|
|
47
|
+
--output) OUTPUT="$2"; shift 2 ;;
|
|
48
|
+
--non-interactive) NON_INTERACTIVE=true; shift ;;
|
|
49
|
+
--convert-to-bun) CONVERT_TO_BUN=true; shift ;;
|
|
50
|
+
--help) usage; exit 0 ;;
|
|
51
|
+
*) echo "error: unknown option: $1" >&2; usage >&2; exit 1 ;;
|
|
52
|
+
esac
|
|
53
|
+
done
|
|
54
|
+
|
|
55
|
+
# --- Validate ---
|
|
56
|
+
if [[ -z "$SOURCE" ]]; then
|
|
57
|
+
echo "error: --source is required" >&2
|
|
58
|
+
usage >&2
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# --- Interactive prompts ---
|
|
63
|
+
prompt() {
|
|
64
|
+
local var_name="$1"
|
|
65
|
+
local question="$2"
|
|
66
|
+
local default="${3:-}"
|
|
67
|
+
if $NON_INTERACTIVE; then
|
|
68
|
+
echo "error: $var_name is required in non-interactive mode" >&2
|
|
69
|
+
exit 1
|
|
70
|
+
fi
|
|
71
|
+
if [[ -n "$default" ]]; then
|
|
72
|
+
read -rp "$question [$default]: " val
|
|
73
|
+
val="${val:-$default}"
|
|
74
|
+
else
|
|
75
|
+
read -rp "$question: " val
|
|
76
|
+
fi
|
|
77
|
+
if [[ -z "$val" ]]; then
|
|
78
|
+
echo "error: $var_name cannot be empty" >&2
|
|
79
|
+
exit 1
|
|
80
|
+
fi
|
|
81
|
+
printf -v "$var_name" '%s' "$val"
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# --- Analyze source ---
|
|
85
|
+
echo "=== Analyzing source: $SOURCE ===" >&2
|
|
86
|
+
|
|
87
|
+
if [[ -d "$SOURCE" ]]; then
|
|
88
|
+
ANALYSIS=$("$ANALYZE" --dir "$SOURCE" --json 2>/dev/null)
|
|
89
|
+
else
|
|
90
|
+
ANALYSIS=$("$ANALYZE" --url "$SOURCE" --json 2>/dev/null)
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
TYPE=$(echo "$ANALYSIS" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("type","unknown"))')
|
|
94
|
+
TEMPLATE_NUM=$(echo "$ANALYSIS" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("template","02"))')
|
|
95
|
+
CONFIDENCE=$(echo "$ANALYSIS" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("confidence","low"))')
|
|
96
|
+
|
|
97
|
+
echo "Detected type: $TYPE (template $TEMPLATE_NUM, confidence: $CONFIDENCE)" >&2
|
|
98
|
+
|
|
99
|
+
# --- Gather metadata ---
|
|
100
|
+
if [[ -z "$NAME" ]]; then
|
|
101
|
+
prompt NAME "Formula name (e.g., MyCli)"
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
if [[ -z "$TAP" ]]; then
|
|
105
|
+
if [[ "$SOURCE" =~ github\.com/([^/]+)/([^/]+) ]]; then
|
|
106
|
+
DEFAULT_TAP="${BASH_REMATCH[1]}/homebrew-${BASH_REMATCH[2]}"
|
|
107
|
+
else
|
|
108
|
+
DEFAULT_TAP=""
|
|
109
|
+
fi
|
|
110
|
+
prompt TAP "Tap identifier (e.g., user/homebrew-mytap)" "$DEFAULT_TAP"
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
# --- Auto-generate output directory ---
|
|
114
|
+
# Smart fallback:
|
|
115
|
+
# GitHub URL: github.com/owner/repo-name → ./repo-name
|
|
116
|
+
# Local path: /path/to/my-project → ./my-project
|
|
117
|
+
# Prepend "homebrew-" if not present
|
|
118
|
+
if [[ -z "$OUTPUT" ]]; then
|
|
119
|
+
if [[ "$SOURCE" =~ github\.com/([^/]+)/([^/]+)(\.git)?$ ]]; then
|
|
120
|
+
# GitHub URL: use repo name
|
|
121
|
+
repo_name="${BASH_REMATCH[2]}"
|
|
122
|
+
# Strip .git suffix if present
|
|
123
|
+
repo_name="${repo_name%.git}"
|
|
124
|
+
raw_name="$repo_name"
|
|
125
|
+
else
|
|
126
|
+
# Local path: use basename
|
|
127
|
+
raw_name=$(basename "$SOURCE")
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
# Sanitize: lowercase, replace spaces with hyphens
|
|
131
|
+
raw_name=$(echo "$raw_name" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
|
|
132
|
+
|
|
133
|
+
# Prepend "homebrew-" if not already present
|
|
134
|
+
if [[ "$raw_name" == homebrew-* ]]; then
|
|
135
|
+
DEFAULT_OUTPUT="./${raw_name}"
|
|
136
|
+
else
|
|
137
|
+
DEFAULT_OUTPUT="./homebrew-${raw_name}"
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
if $NON_INTERACTIVE; then
|
|
141
|
+
OUTPUT="$DEFAULT_OUTPUT"
|
|
142
|
+
echo "Auto-generated output directory: $OUTPUT" >&2
|
|
143
|
+
else
|
|
144
|
+
prompt OUTPUT "Output directory" "$DEFAULT_OUTPUT"
|
|
145
|
+
fi
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
# --- Apply template ---
|
|
149
|
+
echo "=== Generating tap structure ===" >&2
|
|
150
|
+
|
|
151
|
+
TEMPLATE_ARGS=(
|
|
152
|
+
"$TYPE"
|
|
153
|
+
--name "$NAME"
|
|
154
|
+
--output "$OUTPUT"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
if $CONVERT_TO_BUN; then
|
|
158
|
+
TEMPLATE_ARGS+=(--convert-to-bun)
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
"$TEMPLATE" "${TEMPLATE_ARGS[@]}"
|
|
162
|
+
|
|
163
|
+
# --- Validate ---
|
|
164
|
+
FORMULA_FILE=""
|
|
165
|
+
for f in "$OUTPUT"/Formula/*.rb; do
|
|
166
|
+
if [[ -f "$f" ]]; then
|
|
167
|
+
FORMULA_FILE="$f"
|
|
168
|
+
break
|
|
169
|
+
fi
|
|
170
|
+
done
|
|
171
|
+
|
|
172
|
+
if [[ -n "$FORMULA_FILE" ]]; then
|
|
173
|
+
echo "=== Validating formula ===" >&2
|
|
174
|
+
if ruby -c "$FORMULA_FILE" >&2; then
|
|
175
|
+
echo "Formula syntax: OK" >&2
|
|
176
|
+
else
|
|
177
|
+
echo "Warning: formula has syntax errors" >&2
|
|
178
|
+
fi
|
|
179
|
+
fi
|
|
180
|
+
|
|
181
|
+
# --- Summary ---
|
|
182
|
+
echo "" >&2
|
|
183
|
+
echo "=== Tap Created ===" >&2
|
|
184
|
+
echo "Location: $(cd "$OUTPUT" && pwd)" >&2
|
|
185
|
+
echo "Tap: $TAP" >&2
|
|
186
|
+
echo "Formula: $NAME" >&2
|
|
187
|
+
echo "Template: $TEMPLATE_NUM ($TYPE)" >&2
|
|
188
|
+
echo "" >&2
|
|
189
|
+
echo "Next steps:" >&2
|
|
190
|
+
echo " 1. cd $(cd "$OUTPUT" && pwd)" >&2
|
|
191
|
+
echo " 2. Edit the formula in Formula/" >&2
|
|
192
|
+
echo " 3. Run: brew style --tap $TAP Formula/$NAME.rb" >&2
|
|
193
|
+
echo " 4. Run: brew audit --new-formula Formula/$NAME.rb" >&2
|
|
194
|
+
echo " 5. git init && git add . && git commit -m 'Initial commit'" >&2
|
|
195
|
+
echo " 6. git remote add origin https://github.com/$TAP.git" >&2
|
|
196
|
+
echo " 7. git push -u origin main" >&2
|