Skip to content

Commit 5803fe3

Browse files
committed
Dune CLI
0 parents  commit 5803fe3

51 files changed

Lines changed: 4519 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
jobs:
8+
test:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v4
12+
13+
- uses: actions/setup-go@v5
14+
with:
15+
go-version-file: go.mod
16+
17+
- run: go test ./...

.github/workflows/release.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
release:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Checkout
16+
uses: actions/checkout@v4
17+
with:
18+
fetch-depth: 0
19+
20+
- name: Set up Go
21+
uses: actions/setup-go@v5
22+
with:
23+
go-version-file: go.mod
24+
25+
- name: Run GoReleaser
26+
uses: goreleaser/goreleaser-action@v6
27+
with:
28+
distribution: goreleaser
29+
version: "~> v2"
30+
args: release --clean
31+
env:
32+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Binaries for programs and plugins
2+
*.exe
3+
*.exe~
4+
*.dll
5+
*.so
6+
*.dylib
7+
8+
# Test binary, built with `go test -c`
9+
*.test
10+
11+
# Output of the go coverage tool, specifically when used with LiteIDE
12+
*.out
13+
14+
# Local tools
15+
/bin/
16+
17+
# CLI binary
18+
dune-cli

.golangci.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
linters:
3+
enable:
4+
- goimports
5+
- stylecheck
6+
- lll
7+
disable:
8+
- errcheck
9+
10+
run:
11+
go: '1.25'
12+
13+
issues:
14+
exclude-rules:
15+
- linters:
16+
- lll
17+
source: "// nolint:lll"

.goreleaser.yml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
version: 2
2+
3+
project_name: dune-cli
4+
5+
before:
6+
hooks:
7+
- go mod tidy
8+
9+
builds:
10+
- main: ./cmd
11+
binary: dune
12+
env:
13+
- CGO_ENABLED=0
14+
ldflags:
15+
- -s -w
16+
- -X main.version={{.Version}}
17+
- -X main.commit={{.Commit}}
18+
- -X main.date={{.Date}}
19+
goos:
20+
- linux
21+
- darwin
22+
- windows
23+
goarch:
24+
- amd64
25+
- arm64
26+
27+
archives:
28+
- formats:
29+
- tar.gz
30+
name_template: >-
31+
{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}
32+
format_overrides:
33+
- goos: windows
34+
formats:
35+
- zip
36+
37+
checksum:
38+
name_template: "checksums.txt"
39+
40+
changelog:
41+
sort: asc
42+
filters:
43+
exclude:
44+
- "^docs:"
45+
- "^test:"
46+
- "^ci:"
47+
- "^chore:"
48+
49+
release:
50+
github:
51+
owner: duneanalytics
52+
name: cli
53+
draft: false
54+
prerelease: auto
55+
extra_files:
56+
- glob: install.sh

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Dune Analytics
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Dune CLI
2+
3+
A command-line interface for interacting with the Dune Analytics API.
4+
5+
## Authentication
6+
7+
```bash
8+
# Save your API key to ~/.config/dune/config.yaml
9+
dune auth --api-key <key>
10+
11+
# Or run interactively (prompts for key)
12+
dune auth
13+
14+
# Or set via environment variable
15+
export DUNE_API_KEY=<key>
16+
```
17+
18+
The `--api-key` flag is available on all commands to override the stored key.
19+
20+
## Commands
21+
22+
### `dune query`
23+
24+
Manage and execute Dune queries.
25+
26+
| Command | Description |
27+
|---------|-------------|
28+
| `query create --name <name> --sql <sql> [--description] [--private] [--temp]` | Create a new saved query |
29+
| `query get <query-id>` | Get a saved query's details and SQL |
30+
| `query update <query-id> [--name] [--sql] [--description] [--private] [--tags]` | Update an existing query |
31+
| `query archive <query-id>` | Archive a saved query |
32+
| `query run <query-id> [--param key=value] [--performance medium\|large] [--limit] [--timeout] [--no-wait]` | Execute a saved query and display results |
33+
| `query run-sql --sql <sql> [--param key=value] [--performance medium\|large] [--limit] [--timeout] [--no-wait]` | Execute raw SQL directly |
34+
35+
### `dune execution`
36+
37+
Manage query executions.
38+
39+
| Command | Description |
40+
|---------|-------------|
41+
| `execution results <execution-id> [--limit] [--offset] [--timeout] [--no-wait]` | Fetch results of a query execution |
42+
43+
### `dune dataset`
44+
45+
Search the Dune dataset catalog.
46+
47+
| Command | Description |
48+
|---------|-------------|
49+
| `dataset search [--query] [--categories] [--blockchains] [--schemas] [--dataset-types] [--owner-scope] [--include-private] [--include-schema] [--include-metadata] [--limit] [--offset]` | Search for datasets |
50+
| `dataset search-by-contract --contract-address <address> [--blockchains] [--include-schema] [--limit] [--offset]` | Search for decoded tables by contract address |
51+
52+
Categories: `canonical`, `decoded`, `spell`, `community`
53+
54+
### `dune docs`
55+
56+
Search and browse Dune documentation. No authentication required.
57+
58+
| Command | Description |
59+
|---------|-------------|
60+
| `docs search --query <text> [--api-reference-only] [--code-only]` | Search the Dune documentation |
61+
62+
### `dune usage`
63+
64+
Show credit and resource usage for your account.
65+
66+
```bash
67+
dune usage [--start-date YYYY-MM-DD] [--end-date YYYY-MM-DD]
68+
```
69+
70+
## Output Format
71+
72+
All commands (except `auth`) support `-o, --output <format>` with `text` (default) or `json`.

authconfig/authconfig.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package authconfig
2+
3+
import (
4+
"errors"
5+
"io/fs"
6+
"os"
7+
"path/filepath"
8+
"sync"
9+
10+
"gopkg.in/yaml.v3"
11+
)
12+
13+
// Config holds the persisted CLI configuration.
14+
type Config struct {
15+
APIKey string `yaml:"api_key"`
16+
}
17+
18+
// configDirFunc allows tests to override the config directory.
19+
var (
20+
configDirFunc = defaultDir
21+
configMu sync.RWMutex
22+
)
23+
24+
func defaultDir() (string, error) {
25+
home, err := os.UserHomeDir()
26+
if err != nil {
27+
return "", err
28+
}
29+
return filepath.Join(home, ".config", "dune"), nil
30+
}
31+
32+
// SetDirFunc overrides the config directory function (for testing).
33+
func SetDirFunc(fn func() (string, error)) {
34+
configMu.Lock()
35+
defer configMu.Unlock()
36+
configDirFunc = fn
37+
}
38+
39+
// ResetDirFunc restores the default config directory function.
40+
func ResetDirFunc() {
41+
configMu.Lock()
42+
defer configMu.Unlock()
43+
configDirFunc = defaultDir
44+
}
45+
46+
// Dir returns the config directory path ($HOME/.config/dune).
47+
func Dir() (string, error) {
48+
configMu.RLock()
49+
defer configMu.RUnlock()
50+
return configDirFunc()
51+
}
52+
53+
// Path returns the full path to the config file.
54+
func Path() (string, error) {
55+
dir, err := Dir()
56+
if err != nil {
57+
return "", err
58+
}
59+
return filepath.Join(dir, "config.yaml"), nil
60+
}
61+
62+
// Load reads and parses the config file. Returns nil, nil if the file does not exist.
63+
func Load() (*Config, error) {
64+
p, err := Path()
65+
if err != nil {
66+
return nil, err
67+
}
68+
69+
data, err := os.ReadFile(p)
70+
if err != nil {
71+
if errors.Is(err, fs.ErrNotExist) {
72+
return nil, nil
73+
}
74+
return nil, err
75+
}
76+
77+
var cfg Config
78+
if err := yaml.Unmarshal(data, &cfg); err != nil {
79+
return nil, err
80+
}
81+
return &cfg, nil
82+
}
83+
84+
// Save writes the config to disk, creating the directory (0700) and file (0600) as needed.
85+
func Save(cfg *Config) error {
86+
p, err := Path()
87+
if err != nil {
88+
return err
89+
}
90+
91+
if err := os.MkdirAll(filepath.Dir(p), 0o700); err != nil {
92+
return err
93+
}
94+
95+
data, err := yaml.Marshal(cfg)
96+
if err != nil {
97+
return err
98+
}
99+
100+
return os.WriteFile(p, data, 0o600)
101+
}

0 commit comments

Comments
 (0)