Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ coingecko-cli/
- **Tagging**: always tag from `main` after pulling latest — `git tag vX.Y.Z && git push origin vX.Y.Z`
- **Install script**: `install.sh` downloads the latest release binary from GitHub Releases
- **Go install**: `go install github.com/coingecko/coingecko-cli@latest` — produces a binary named `coingecko-cli` (module-path basename). Users alias or symlink it to `cg`; the `cg update --method go` flow prints this heads-up before running install.
- **npm**: `npm install -g @coingecko/cg` — umbrella package in `npm/cg/` plus 6 platform sub-packages (`npm/cg-<os>-<arch>/`) published via OIDC Trusted Publishing from `release.yml`. Each package needs its trusted publisher configured on npmjs.com (Repository: `coingecko/coingecko-cli`, Workflow: `release.yml`) before its first publish. Publish flow lives in `scripts/npm-publish.sh`.
- **`cg update` install methods**: auto-detects `homebrew`, `npm`, `go`, or `script` from the executable path. npm detection (`node_modules/@coingecko/cg-*`) runs before homebrew because Homebrew-installed Node puts npm globals under `/opt/homebrew/lib/node_modules/`.

## Key Design Decisions

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -355,13 +355,13 @@ cg watch --ids bitcoin --dry-run # Show WebSocket request info

### `cg update` — Upgrade the CLI

Check for a new version and upgrade in one step. Auto-detects whether you installed via Homebrew, `go install`, or the install script, and hands off to the right tool.
Check for a new version and upgrade in one step. Auto-detects whether you installed via Homebrew, npm, `go install`, or the install script, and hands off to the right tool.

```sh
cg update

# Override install method if auto-detection gets it wrong
cg update --method homebrew # or: go, script
cg update --method homebrew # or: npm, go, script
```

The CLI also checks for updates on launch (cached 24h) and shows a reminder if you're behind. Set `CG_NO_UPDATE_CHECK=1` to disable this in CI.
Expand Down
18 changes: 14 additions & 4 deletions cmd/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ var updateCmd = &cobra.Command{
}

func init() {
updateCmd.Flags().String("method", "", "Install method override (homebrew, go, script)")
updateCmd.Flags().String("method", "", "Install method override (homebrew, npm, go, script)")
rootCmd.AddCommand(updateCmd)
}

Expand All @@ -36,9 +36,9 @@ func runUpdate(cmd *cobra.Command, args []string) error {
method = detectInstallMethod()
} else {
switch method {
case "homebrew", "go", "script":
case "homebrew", "npm", "go", "script":
default:
return fmt.Errorf("unknown install method %qmust be one of: homebrew, go, script", method)
return fmt.Errorf("unknown install method %q, must be one of: homebrew, npm, go, script", method)
}
}

Expand Down Expand Up @@ -92,9 +92,16 @@ func detectInstallMethod() string {
return classifyInstallPath(exe)
}

// classifyInstallPath returns the install method ("homebrew", "go", or "script")
// classifyInstallPath returns the install method ("homebrew", "npm", "go", or "script")
// for a resolved executable path.
func classifyInstallPath(exe string) string {
// npm check runs before homebrew because Homebrew-installed Node puts npm globals
// under /opt/homebrew/lib/node_modules/, which would otherwise match the homebrew rule.
// filepath.ToSlash is a no-op off Windows, so normalize backslashes explicitly.
if strings.Contains(strings.ReplaceAll(exe, `\`, "/"), "node_modules/@coingecko/cg-") {
return "npm"
}

if strings.Contains(exe, "/Cellar/") ||
strings.Contains(exe, "/homebrew/") ||
strings.Contains(exe, "/opt/homebrew/") {
Expand Down Expand Up @@ -124,6 +131,9 @@ func runInstallCommand(method string) error {
case "homebrew":
name = "brew"
args = []string{"upgrade", "coingecko/coingecko-cli/cg"}
case "npm":
name = "npm"
args = []string{"install", "-g", "@coingecko/cg@latest"}
case "go":
warnf("Note: 'go install' produces a binary named 'coingecko-cli'. If you invoke this CLI as 'cg', make sure you have an alias or symlink (e.g. alias cg=coingecko-cli).\n\n")
name = "go"
Expand Down
20 changes: 19 additions & 1 deletion cmd/update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,24 @@ func TestClassifyInstallPath_Go_ExplicitGOPATH(t *testing.T) {
assert.Equal(t, "go", classifyInstallPath(exe))
}

func TestClassifyInstallPath_Npm(t *testing.T) {
t.Setenv("GOBIN", "")
t.Setenv("GOPATH", "")

cases := []struct {
path string
desc string
}{
{"/usr/local/lib/node_modules/@coingecko/cg-darwin-arm64/cg", "macOS npm global"},
{"/home/user/.npm-global/lib/node_modules/@coingecko/cg-linux-x64/cg", "Linux user npm prefix"},
{"/opt/homebrew/lib/node_modules/@coingecko/cg-darwin-arm64/cg", "Homebrew-node npm prefix"},
{`C:\Users\runner\AppData\Roaming\npm\node_modules\@coingecko\cg-win32-x64\cg.exe`, "Windows npm global"},
}
for _, tc := range cases {
assert.Equal(t, "npm", classifyInstallPath(tc.path), tc.desc)
}
}

func TestClassifyInstallPath_Script(t *testing.T) {
t.Setenv("GOBIN", "")
t.Setenv("GOPATH", "")
Expand All @@ -156,7 +174,7 @@ func TestClassifyInstallPath_GoBinNotParentDir(t *testing.T) {

func TestDetectInstallMethod_ReturnsValidMethod(t *testing.T) {
method := detectInstallMethod()
assert.Contains(t, []string{"homebrew", "go", "script"}, method)
assert.Contains(t, []string{"homebrew", "npm", "go", "script"}, method)
}

func TestRunUpdate_FetchError(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion npm/cg-darwin-arm64/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "CoinGecko CLI - Real Time & Historical Crypto Data (macOS ARM64)",
"repository": {
"type": "git",
"url": "https://github.com/coingecko/coingecko-cli.git"
"url": "git+https://github.com/coingecko/coingecko-cli.git"
},
"homepage": "https://github.com/coingecko/coingecko-cli",
"bugs": {
Expand Down
2 changes: 1 addition & 1 deletion npm/cg-darwin-x64/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "CoinGecko CLI - Real Time & Historical Crypto Data (macOS x64)",
"repository": {
"type": "git",
"url": "https://github.com/coingecko/coingecko-cli.git"
"url": "git+https://github.com/coingecko/coingecko-cli.git"
},
"homepage": "https://github.com/coingecko/coingecko-cli",
"bugs": {
Expand Down
2 changes: 1 addition & 1 deletion npm/cg-linux-arm64/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "CoinGecko CLI - Real Time & Historical Crypto Data (Linux ARM64)",
"repository": {
"type": "git",
"url": "https://github.com/coingecko/coingecko-cli.git"
"url": "git+https://github.com/coingecko/coingecko-cli.git"
},
"homepage": "https://github.com/coingecko/coingecko-cli",
"bugs": {
Expand Down
2 changes: 1 addition & 1 deletion npm/cg-linux-x64/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "CoinGecko CLI - Real Time & Historical Crypto Data (Linux x64)",
"repository": {
"type": "git",
"url": "https://github.com/coingecko/coingecko-cli.git"
"url": "git+https://github.com/coingecko/coingecko-cli.git"
},
"homepage": "https://github.com/coingecko/coingecko-cli",
"bugs": {
Expand Down
2 changes: 1 addition & 1 deletion npm/cg-win32-arm64/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "CoinGecko CLI - Real Time & Historical Crypto Data (Windows ARM64)",
"repository": {
"type": "git",
"url": "https://github.com/coingecko/coingecko-cli.git"
"url": "git+https://github.com/coingecko/coingecko-cli.git"
},
"homepage": "https://github.com/coingecko/coingecko-cli",
"bugs": {
Expand Down
2 changes: 1 addition & 1 deletion npm/cg-win32-x64/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "CoinGecko CLI - Real Time & Historical Crypto Data (Windows x64)",
"repository": {
"type": "git",
"url": "https://github.com/coingecko/coingecko-cli.git"
"url": "git+https://github.com/coingecko/coingecko-cli.git"
},
"homepage": "https://github.com/coingecko/coingecko-cli",
"bugs": {
Expand Down
2 changes: 1 addition & 1 deletion npm/cg/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "CoinGecko CLI - Real Time & Historical Crypto Data",
"repository": {
"type": "git",
"url": "https://github.com/coingecko/coingecko-cli.git"
"url": "git+https://github.com/coingecko/coingecko-cli.git"
},
"homepage": "https://github.com/coingecko/coingecko-cli",
"bugs": {
Expand Down