-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathmain.go
More file actions
169 lines (143 loc) · 4 KB
/
main.go
File metadata and controls
169 lines (143 loc) · 4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package main
import (
"fmt"
"os"
"path/filepath"
"github.com/joho/godotenv"
"github.com/zapstore/zapstore/cmd"
"github.com/zapstore/zapstore/nostr"
"github.com/zapstore/zapstore/store"
"github.com/zapstore/zapstore/ui"
)
// Set at build time: go build -ldflags "-X main.buildVersion=0.1.0"
var buildVersion = "dev"
const usage = `zapstore - Nostr-native package manager
Usage:
zapstore [flags] <command> [arguments]
Commands:
install <app-id> Install a package
remove <app-id> Remove an installed package
update [<app-id>] Update one or all installed packages
list List installed packages
search <query> Search for packages
cleanup Remove old versions and free disk space
Flags:
-q, --quiet Suppress status output (results and errors only)
-v, --verbose Show detailed output
-y, --yes Skip confirmation prompts
--json Output results as JSON (list, search)
--no-color Disable colored output
--refresh-profiles Force refresh of cached developer profiles
--version Print version and exit
-h, --help Show this help
Environment:
RELAY_URLS Comma-separated relay URLs (default: wss://relay.zapstore.dev)
NO_COLOR Disable colored output
`
func main() {
// Parse global flags, extract command and positional args.
command, args := parseArgs(os.Args[1:])
// Load .env files: .env.local > .env > ~/.config/zapstore/config.env
loadEnv()
if command == "" {
fmt.Fprint(os.Stderr, usage)
os.Exit(1)
}
// Migrate from legacy ~/.zapstore if needed
if err := store.MigrateIfNeeded(); err != nil {
ui.Warningf("migration: %v", err)
// Non-fatal: continue even if migration fails
}
var err error
switch command {
case "install":
if len(args) < 1 {
ui.Fatal("usage: zapstore install <app-id>")
os.Exit(2)
}
err = cmd.Install(args[0])
case "update":
appID := ""
if len(args) >= 1 {
appID = args[0]
}
err = cmd.Update(appID)
case "remove":
if len(args) < 1 {
ui.Fatal("usage: zapstore remove <app-id>")
os.Exit(2)
}
err = cmd.Remove(args[0])
case "list":
err = cmd.List()
case "search":
if len(args) < 1 {
ui.Fatal("usage: zapstore search <query>")
os.Exit(2)
}
err = cmd.Search(args[0])
case "cleanup":
err = cmd.Cleanup()
case "help", "--help", "-h":
fmt.Print(usage)
return
default:
ui.Fatal(fmt.Sprintf("unknown command: %s", command))
fmt.Fprint(os.Stderr, usage)
os.Exit(2)
}
if err != nil {
ui.Fatal(err.Error())
os.Exit(1)
}
}
// parseArgs scans raw arguments for global flags and returns the command
// name and remaining positional arguments.
func parseArgs(raw []string) (string, []string) {
var args []string
for _, arg := range raw {
switch arg {
case "-q", "--quiet":
ui.Level = ui.Quiet
case "-v", "--verbose":
ui.Level = ui.Verbose
case "-vv":
ui.Level = ui.Debug
case "-y", "--yes":
ui.YesFlag = true
case "--json":
ui.JSONMode = true
case "--no-color":
ui.SetNoColor(true)
case "--refresh-profiles":
nostr.RefreshProfiles = true
case "--version":
fmt.Printf("zapstore %s\n", buildVersion)
os.Exit(0)
default:
args = append(args, arg)
}
}
if len(args) == 0 {
return "", nil
}
return args[0], args[1:]
}
// loadEnv silently loads environment files in priority order.
// Later files only fill in values not already set.
//
// Each file is loaded independently so a missing file (e.g. .env.local)
// does not prevent subsequent files from being read.
func loadEnv() {
paths := []string{".env.local", ".env"}
// Append user-level config from XDG_CONFIG_HOME
if dir, err := store.ConfigDir(); err == nil {
paths = append(paths, filepath.Join(dir, "config.env"))
}
// godotenv.Load does not override existing env vars, so the first
// file to set a key wins. Load each independently — Load() with
// multiple filenames stops at the first missing file.
for _, p := range paths {
_ = godotenv.Load(p)
}
}