tangled
alpha
login
or
join now
desertthunder.dev
/
twisted
17
fork
atom
a love letter to tangled (android, iOS, and a search API)
17
fork
atom
overview
issues
pulls
pipelines
feat: local dev flag
desertthunder.dev
4 days ago
ed75fb55
9162307a
+113
-22
4 changed files
expand all
collapse all
unified
split
.gitignore
packages
api
internal
config
config.go
config_test.go
main.go
+2
.gitignore
···
31
31
platforms
32
32
plugins
33
33
www
34
34
+
35
35
+
*.db
+27
-1
packages/api/internal/config/config.go
···
35
35
AdminAuthToken string
36
36
}
37
37
38
38
-
func Load() (*Config, error) {
38
38
+
type LoadOptions struct {
39
39
+
Local bool
40
40
+
WorkDir string
41
41
+
}
42
42
+
43
43
+
func Load(opts LoadOptions) (*Config, error) {
39
44
loadDotEnv()
40
45
41
46
cfg := &Config{
···
63
68
EnableAdminEndpoints: envBool("ENABLE_ADMIN_ENDPOINTS", false),
64
69
}
65
70
71
71
+
if opts.Local {
72
72
+
dbURL, err := localDatabaseURL(opts.WorkDir)
73
73
+
if err != nil {
74
74
+
return nil, err
75
75
+
}
76
76
+
cfg.TursoURL = dbURL
77
77
+
cfg.TursoToken = ""
78
78
+
cfg.LogFormat = "text"
79
79
+
}
80
80
+
66
81
var errs []error
67
82
if cfg.TursoURL == "" {
68
83
errs = append(errs, errors.New("TURSO_DATABASE_URL is required"))
···
74
89
return nil, errors.Join(errs...)
75
90
}
76
91
return cfg, nil
92
92
+
}
93
93
+
94
94
+
func localDatabaseURL(workDir string) (string, error) {
95
95
+
if strings.TrimSpace(workDir) == "" {
96
96
+
var err error
97
97
+
workDir, err = os.Getwd()
98
98
+
if err != nil {
99
99
+
return "", err
100
100
+
}
101
101
+
}
102
102
+
return "file:" + filepath.Join(workDir, "twister-dev.db"), nil
77
103
}
78
104
79
105
func loadDotEnv() {
+59
packages/api/internal/config/config_test.go
···
1
1
+
package config
2
2
+
3
3
+
import (
4
4
+
"os"
5
5
+
"path/filepath"
6
6
+
"testing"
7
7
+
)
8
8
+
9
9
+
func TestLoadRequiresRemoteTursoConfigurationByDefault(t *testing.T) {
10
10
+
t.Setenv("TURSO_DATABASE_URL", "")
11
11
+
t.Setenv("TURSO_AUTH_TOKEN", "")
12
12
+
13
13
+
_, err := Load(LoadOptions{})
14
14
+
if err == nil {
15
15
+
t.Fatal("expected missing Turso config to fail")
16
16
+
}
17
17
+
}
18
18
+
19
19
+
func TestLoadLocalOverridesRemoteDatabaseAndLogging(t *testing.T) {
20
20
+
workDir := t.TempDir()
21
21
+
t.Setenv("TURSO_DATABASE_URL", "libsql://example.turso.io")
22
22
+
t.Setenv("TURSO_AUTH_TOKEN", "secret")
23
23
+
t.Setenv("LOG_FORMAT", "json")
24
24
+
25
25
+
cfg, err := Load(LoadOptions{Local: true, WorkDir: workDir})
26
26
+
if err != nil {
27
27
+
t.Fatalf("load config: %v", err)
28
28
+
}
29
29
+
30
30
+
wantURL := "file:" + filepath.Join(workDir, "twister-dev.db")
31
31
+
if cfg.TursoURL != wantURL {
32
32
+
t.Fatalf("TursoURL: got %q, want %q", cfg.TursoURL, wantURL)
33
33
+
}
34
34
+
if cfg.TursoToken != "" {
35
35
+
t.Fatalf("TursoToken: got %q, want empty", cfg.TursoToken)
36
36
+
}
37
37
+
if cfg.LogFormat != "text" {
38
38
+
t.Fatalf("LogFormat: got %q, want %q", cfg.LogFormat, "text")
39
39
+
}
40
40
+
}
41
41
+
42
42
+
func TestLoadLocalUsesCurrentWorkingDirectoryWhenUnset(t *testing.T) {
43
43
+
wd, err := os.Getwd()
44
44
+
if err != nil {
45
45
+
t.Fatalf("getwd: %v", err)
46
46
+
}
47
47
+
t.Setenv("TURSO_DATABASE_URL", "")
48
48
+
t.Setenv("TURSO_AUTH_TOKEN", "")
49
49
+
50
50
+
cfg, err := Load(LoadOptions{Local: true})
51
51
+
if err != nil {
52
52
+
t.Fatalf("load config: %v", err)
53
53
+
}
54
54
+
55
55
+
wantURL := "file:" + filepath.Join(wd, "twister-dev.db")
56
56
+
if cfg.TursoURL != wantURL {
57
57
+
t.Fatalf("TursoURL: got %q, want %q", cfg.TursoURL, wantURL)
58
58
+
}
59
59
+
}
+25
-21
packages/api/main.go
···
28
28
)
29
29
30
30
func main() {
31
31
+
var local bool
32
32
+
31
33
root := &cobra.Command{
32
34
Use: "twister",
33
35
Short: "Tangled search service",
···
36
38
SilenceErrors: true,
37
39
}
38
40
41
41
+
root.PersistentFlags().BoolVar(&local, "local", false, "Use a local twister-dev.db database and text logs for development")
42
42
+
39
43
root.AddCommand(
40
40
-
newAPICmd(),
41
41
-
newIndexerCmd(),
42
42
-
newBackfillCmd(),
43
43
-
newEmbedWorkerCmd(),
44
44
-
newReindexCmd(),
45
45
-
newReembedCmd(),
46
46
-
newHealthcheckCmd(),
44
44
+
newAPICmd(&local),
45
45
+
newIndexerCmd(&local),
46
46
+
newBackfillCmd(&local),
47
47
+
newEmbedWorkerCmd(&local),
48
48
+
newReindexCmd(&local),
49
49
+
newReembedCmd(&local),
50
50
+
newHealthcheckCmd(&local),
47
51
)
48
52
49
53
if err := root.Execute(); err != nil {
···
63
67
return ctx, cancel
64
68
}
65
69
66
66
-
func newAPICmd() *cobra.Command {
70
70
+
func newAPICmd(local *bool) *cobra.Command {
67
71
return &cobra.Command{
68
72
Use: "api",
69
73
Aliases: []string{"serve"},
70
74
Short: "Start the HTTP search API",
71
75
RunE: func(cmd *cobra.Command, args []string) error {
72
72
-
cfg, err := config.Load()
76
76
+
cfg, err := config.Load(config.LoadOptions{Local: *local})
73
77
if err != nil {
74
78
return fmt.Errorf("config: %w", err)
75
79
}
···
103
107
}
104
108
}
105
109
106
106
-
func newIndexerCmd() *cobra.Command {
110
110
+
func newIndexerCmd(local *bool) *cobra.Command {
107
111
return &cobra.Command{
108
112
Use: "indexer",
109
113
Short: "Start the Tap consumer and indexer",
110
114
RunE: func(cmd *cobra.Command, args []string) error {
111
111
-
cfg, err := config.Load()
115
115
+
cfg, err := config.Load(config.LoadOptions{Local: *local})
112
116
if err != nil {
113
117
return fmt.Errorf("config: %w", err)
114
118
}
···
176
180
}
177
181
}
178
182
179
179
-
func newEmbedWorkerCmd() *cobra.Command {
183
183
+
func newEmbedWorkerCmd(local *bool) *cobra.Command {
180
184
return &cobra.Command{
181
185
Use: "embed-worker",
182
186
Short: "Start the async embedding worker",
183
187
RunE: func(cmd *cobra.Command, args []string) error {
184
184
-
cfg, err := config.Load()
188
188
+
cfg, err := config.Load(config.LoadOptions{Local: *local})
185
189
if err != nil {
186
190
return fmt.Errorf("config: %w", err)
187
191
}
···
196
200
}
197
201
}
198
202
199
199
-
func newBackfillCmd() *cobra.Command {
203
203
+
func newBackfillCmd(local *bool) *cobra.Command {
200
204
var opts backfill.Options
201
205
202
206
cmd := &cobra.Command{
203
207
Use: "backfill",
204
208
Short: "Discover users from seeds and register repos for Tap backfill",
205
209
RunE: func(cmd *cobra.Command, args []string) error {
206
206
-
cfg, err := config.Load()
210
210
+
cfg, err := config.Load(config.LoadOptions{Local: *local})
207
211
if err != nil {
208
212
return fmt.Errorf("config: %w", err)
209
213
}
···
259
263
return cmd
260
264
}
261
265
262
262
-
func newReindexCmd() *cobra.Command {
266
266
+
func newReindexCmd(local *bool) *cobra.Command {
263
267
return &cobra.Command{
264
268
Use: "reindex",
265
269
Short: "Re-normalize and upsert all documents",
266
270
RunE: func(cmd *cobra.Command, args []string) error {
267
267
-
cfg, err := config.Load()
271
271
+
cfg, err := config.Load(config.LoadOptions{Local: *local})
268
272
if err != nil {
269
273
return fmt.Errorf("config: %w", err)
270
274
}
···
275
279
}
276
280
}
277
281
278
278
-
func newReembedCmd() *cobra.Command {
282
282
+
func newReembedCmd(local *bool) *cobra.Command {
279
283
return &cobra.Command{
280
284
Use: "reembed",
281
285
Short: "Re-generate all embeddings",
282
286
RunE: func(cmd *cobra.Command, args []string) error {
283
283
-
cfg, err := config.Load()
287
287
+
cfg, err := config.Load(config.LoadOptions{Local: *local})
284
288
if err != nil {
285
289
return fmt.Errorf("config: %w", err)
286
290
}
···
291
295
}
292
296
}
293
297
294
294
-
func newHealthcheckCmd() *cobra.Command {
298
298
+
func newHealthcheckCmd(local *bool) *cobra.Command {
295
299
return &cobra.Command{
296
300
Use: "healthcheck",
297
301
Short: "One-shot health probe",
298
302
RunE: func(cmd *cobra.Command, args []string) error {
299
299
-
cfg, err := config.Load()
303
303
+
cfg, err := config.Load(config.LoadOptions{Local: *local})
300
304
if err != nil {
301
305
return fmt.Errorf("config: %w", err)
302
306
}