@@ -440,35 +440,19 @@ func (c *config) Eject(w io.Writer) error {
440
440
// Loads custom config file to struct fields tagged with toml.
441
441
func (c * config ) loadFromFile (filename string , fsys fs.FS ) error {
442
442
v := viper .New ()
443
- v .SetConfigType ("toml" )
444
- // Load default values
445
- var buf bytes.Buffer
446
- if err := c .Eject (& buf ); err != nil {
443
+ if err := c .mergeDefaultValues (v ); err != nil {
447
444
return err
448
- } else if err := c . loadFromReader (v , & buf ); err != nil {
445
+ } else if err := mergeFileConfig (v , filename , fsys ); err != nil {
449
446
return err
450
447
}
451
- // Load custom config
452
- if ext := filepath .Ext (filename ); len (ext ) > 0 {
453
- v .SetConfigType (ext [1 :])
454
- }
455
- f , err := fsys .Open (filename )
456
- if errors .Is (err , os .ErrNotExist ) {
457
- return nil
458
- } else if err != nil {
459
- return errors .Errorf ("failed to read file config: %w" , err )
460
- }
461
- defer f .Close ()
462
- return c .loadFromReader (v , f )
463
- }
464
-
465
- func (c * config ) loadFromReader (v * viper.Viper , r io.Reader ) error {
466
- if err := v .MergeConfig (r ); err != nil {
467
- return errors .Errorf ("failed to merge config: %w" , err )
448
+ // Load base config and mapstructure overrides
449
+ if err := c .load (v ); err != nil {
450
+ return err
451
+ } else if err := c .loadFromEnv (); err != nil {
452
+ return err
468
453
}
469
454
// Find [remotes.*] block to override base config
470
- baseId := v .GetString ("project_id" )
471
- idToName := map [string ]string {baseId : "base" }
455
+ idToName := map [string ]string {}
472
456
for name , remote := range v .GetStringMap ("remotes" ) {
473
457
projectId := v .GetString (fmt .Sprintf ("remotes.%s.project_id" , name ))
474
458
// Track remote project_id to check for duplication
@@ -481,9 +465,43 @@ func (c *config) loadFromReader(v *viper.Viper, r io.Reader) error {
481
465
if err := v .MergeConfigMap (remote .(map [string ]any )); err != nil {
482
466
return err
483
467
}
484
- v .Set ("project_id" , baseId )
485
468
}
486
469
}
470
+ if _ , exists := idToName [c .ProjectId ]; exists {
471
+ return c .load (v )
472
+ }
473
+ return nil
474
+ }
475
+
476
+ func (c * config ) mergeDefaultValues (v * viper.Viper ) error {
477
+ v .SetConfigType ("toml" )
478
+ var buf bytes.Buffer
479
+ if err := c .Eject (& buf ); err != nil {
480
+ return err
481
+ } else if err := v .MergeConfig (& buf ); err != nil {
482
+ return errors .Errorf ("failed to merge default values: %w" , err )
483
+ }
484
+ return nil
485
+ }
486
+
487
+ func mergeFileConfig (v * viper.Viper , filename string , fsys fs.FS ) error {
488
+ if ext := filepath .Ext (filename ); len (ext ) > 0 {
489
+ v .SetConfigType (ext [1 :])
490
+ }
491
+ f , err := fsys .Open (filename )
492
+ if errors .Is (err , os .ErrNotExist ) {
493
+ return nil
494
+ } else if err != nil {
495
+ return errors .Errorf ("failed to read file config: %w" , err )
496
+ }
497
+ defer f .Close ()
498
+ if err := v .MergeConfig (f ); err != nil {
499
+ return errors .Errorf ("failed to merge file config: %w" , err )
500
+ }
501
+ return nil
502
+ }
503
+
504
+ func (c * config ) load (v * viper.Viper ) error {
487
505
// Set default values for [functions.*] when config struct is empty
488
506
for key , value := range v .GetStringMap ("functions" ) {
489
507
if _ , ok := value .(map [string ]any ); ! ok {
@@ -520,7 +538,7 @@ func (c *config) loadFromReader(v *viper.Viper, r io.Reader) error {
520
538
return nil
521
539
}
522
540
523
- func (c * config ) newDecodeHook (fs ... mapstructure.DecodeHookFunc ) mapstructure.DecodeHookFunc {
541
+ func (c * baseConfig ) newDecodeHook (fs ... mapstructure.DecodeHookFunc ) mapstructure.DecodeHookFunc {
524
542
fs = append (fs ,
525
543
mapstructure .StringToTimeDurationHookFunc (),
526
544
mapstructure .StringToIPHookFunc (),
@@ -532,26 +550,11 @@ func (c *config) newDecodeHook(fs ...mapstructure.DecodeHookFunc) mapstructure.D
532
550
}
533
551
534
552
// Loads envs prefixed with supabase_ to struct fields tagged with mapstructure.
535
- func (c * config ) loadFromEnv () error {
536
- v := viper .New ( )
553
+ func (c * baseConfig ) loadFromEnv () error {
554
+ v := viper .NewWithOptions ( viper . ExperimentalBindStruct () )
537
555
v .SetEnvPrefix ("SUPABASE" )
538
556
v .SetEnvKeyReplacer (strings .NewReplacer ("." , "_" ))
539
557
v .AutomaticEnv ()
540
- // Viper does not parse env vars automatically. Instead of calling viper.BindEnv
541
- // per key, we decode all keys from an existing struct, and merge them to viper.
542
- // Ref: https://github.com/spf13/viper/issues/761#issuecomment-859306364
543
- envKeysMap := map [string ]interface {}{}
544
- if dec , err := mapstructure .NewDecoder (& mapstructure.DecoderConfig {
545
- Result : & envKeysMap ,
546
- IgnoreUntaggedFields : true ,
547
- }); err != nil {
548
- return errors .Errorf ("failed to create decoder: %w" , err )
549
- } else if err := dec .Decode (c .baseConfig ); err != nil {
550
- return errors .Errorf ("failed to decode env: %w" , err )
551
- } else if err := v .MergeConfigMap (envKeysMap ); err != nil {
552
- return errors .Errorf ("failed to merge env config: %w" , err )
553
- }
554
- // Writes viper state back to config struct, with automatic env substitution
555
558
if err := v .UnmarshalExact (c , viper .DecodeHook (c .newDecodeHook ())); err != nil {
556
559
return errors .Errorf ("failed to parse env override: %w" , err )
557
560
}
@@ -567,9 +570,6 @@ func (c *config) Load(path string, fsys fs.FS) error {
567
570
if err := c .loadFromFile (builder .ConfigPath , fsys ); err != nil {
568
571
return err
569
572
}
570
- if err := c .loadFromEnv (); err != nil {
571
- return err
572
- }
573
573
// Generate JWT tokens
574
574
if len (c .Auth .AnonKey ) == 0 {
575
575
anonToken := CustomClaims {Role : "anon" }.NewToken ()
0 commit comments