2021-04-10 17:47:18 +02:00
|
|
|
//nolint:exhaustivestruct,gochecknoglobals,gochecknoinits
|
2021-03-30 19:59:26 +02:00
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
|
2021-04-02 08:59:21 +02:00
|
|
|
"github.com/fsnotify/fsnotify"
|
|
|
|
"github.com/rs/zerolog"
|
2021-03-30 19:59:26 +02:00
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/spf13/viper"
|
|
|
|
"go.xsfx.dev/schnutibox/internal/config"
|
2021-04-10 17:47:18 +02:00
|
|
|
"go.xsfx.dev/schnutibox/pkg/prepare"
|
2021-03-30 19:59:26 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
var cfgFile string
|
|
|
|
|
|
|
|
var rootCmd = &cobra.Command{
|
|
|
|
Use: "schnutibox",
|
|
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
|
|
if err := cmd.Usage(); err != nil {
|
|
|
|
log.Error().Msg(err.Error())
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// init initializes the command line interface.
|
|
|
|
func init() {
|
2021-04-02 08:59:21 +02:00
|
|
|
// Run.
|
|
|
|
rootCmd.AddCommand(runCmd)
|
2021-04-02 10:48:30 +02:00
|
|
|
runCmd.Flags().StringVarP(&cfgFile, "config", "c", "", "config file")
|
2021-04-10 17:47:18 +02:00
|
|
|
|
2021-04-02 10:48:30 +02:00
|
|
|
if err := runCmd.MarkFlagRequired("config"); err != nil {
|
|
|
|
log.Fatal().Err(err).Msg("missing flag")
|
|
|
|
}
|
2021-04-02 08:59:21 +02:00
|
|
|
|
|
|
|
// Prepare.
|
|
|
|
rootCmd.AddCommand(prepareCmd)
|
2021-04-10 17:47:18 +02:00
|
|
|
prepareCmd.Flags().BoolVar(&prepare.Cfg.ReadOnly, "read-only", false, "Setup read-only system")
|
|
|
|
prepareCmd.Flags().StringVarP(&prepare.Cfg.System, "system", "s", "raspbian", "Which kind of system to prepare")
|
|
|
|
prepareCmd.Flags().StringVar(&prepare.Cfg.SpotifyUsername, "spotify-username", "", "Spotify username")
|
|
|
|
prepareCmd.Flags().StringVar(&prepare.Cfg.SpotifyPassword, "spotify-password", "", "Spotify password")
|
|
|
|
prepareCmd.Flags().StringVar(&prepare.Cfg.SpotifyClientID, "spotify-client-id", "", "Spotify client ID")
|
|
|
|
prepareCmd.Flags().StringVar(&prepare.Cfg.SpotifyClientSecret, "spotify-client-secret", "", "Spotify client secret")
|
2021-04-14 10:40:57 +02:00
|
|
|
prepareCmd.Flags().StringVar(&prepare.Cfg.RFIDReader, "rfid-reader", "/dev/hidraw0", "dev path of rfid reader")
|
|
|
|
prepareCmd.Flags().StringVar(&prepare.Cfg.StopID, "stop-id", "", "ID of stop tag")
|
2021-04-10 17:47:18 +02:00
|
|
|
|
|
|
|
// Version.
|
|
|
|
rootCmd.AddCommand(versionCmd)
|
2021-05-03 14:51:53 +02:00
|
|
|
|
|
|
|
// Web.
|
|
|
|
rootCmd.AddCommand(webCmd)
|
|
|
|
webCmd.Flags().StringVarP(&cfgFile, "config", "c", "", "config file")
|
|
|
|
|
|
|
|
if err := webCmd.MarkFlagRequired("config"); err != nil {
|
|
|
|
log.Fatal().Err(err).Msg("missing flag")
|
|
|
|
}
|
2021-03-30 19:59:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// initConfig loads the config file.
|
2021-05-03 14:51:53 +02:00
|
|
|
// fatal defines if config parsing should end in a fatal error or not.
|
|
|
|
func initConfig(fatal bool) {
|
2021-03-30 19:59:26 +02:00
|
|
|
logger := log.With().Str("config", cfgFile).Logger()
|
2021-04-02 08:59:21 +02:00
|
|
|
|
|
|
|
// Defaults.
|
|
|
|
viper.SetDefault("box.hostname", "localhost")
|
|
|
|
viper.SetDefault("box.port", 9999)
|
|
|
|
viper.SetDefault("mpd.hostname", "localhost")
|
|
|
|
viper.SetDefault("mpd.port", 6600)
|
|
|
|
viper.SetDefault("reader.dev", "/dev/hidraw0")
|
|
|
|
|
|
|
|
// Environment handling.
|
2021-03-30 19:59:26 +02:00
|
|
|
viper.SetEnvPrefix("SCHNUTIBOX")
|
|
|
|
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
|
|
|
viper.AutomaticEnv()
|
|
|
|
|
2021-04-14 10:40:57 +02:00
|
|
|
// Flags.
|
2021-04-16 09:23:11 +02:00
|
|
|
if err := viper.BindPFlag("reader.dev", prepareCmd.Flags().Lookup("rfid-reader")); err != nil {
|
|
|
|
logger.Fatal().Err(err).Msg("could not bind flag")
|
|
|
|
}
|
2021-04-14 10:40:57 +02:00
|
|
|
|
2021-04-02 08:59:21 +02:00
|
|
|
// Parse config file.
|
2021-03-30 19:59:26 +02:00
|
|
|
if cfgFile != "" {
|
|
|
|
viper.SetConfigFile(cfgFile)
|
2021-05-03 14:51:53 +02:00
|
|
|
parseConfig(logger, fatal)
|
2021-03-30 19:59:26 +02:00
|
|
|
} else {
|
|
|
|
logger.Fatal().Msg("missing config file")
|
|
|
|
}
|
2021-04-02 08:59:21 +02:00
|
|
|
|
|
|
|
viper.WatchConfig()
|
|
|
|
viper.OnConfigChange(func(e fsnotify.Event) {
|
|
|
|
logger.Info().Msg("config file changed")
|
2021-04-18 11:33:15 +02:00
|
|
|
parseConfig(logger, false)
|
2021-04-02 08:59:21 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-04-18 11:33:15 +02:00
|
|
|
// parseConfig parses the config and does some tests if required fields are there.
|
|
|
|
// Its also possible to decide if parsing should end up in a fatal or just an error.
|
|
|
|
func parseConfig(logger zerolog.Logger, fatal bool) {
|
2021-04-02 08:59:21 +02:00
|
|
|
if err := viper.ReadInConfig(); err != nil {
|
2021-04-18 11:33:15 +02:00
|
|
|
if fatal {
|
|
|
|
logger.Fatal().Err(err).Msg("error loading config file")
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.Error().Err(err).Msg("error loading config file")
|
|
|
|
|
|
|
|
return
|
2021-04-02 08:59:21 +02:00
|
|
|
}
|
2021-04-10 17:47:18 +02:00
|
|
|
|
2021-04-02 08:59:21 +02:00
|
|
|
if err := viper.Unmarshal(&config.Cfg); err != nil {
|
2021-04-18 11:33:15 +02:00
|
|
|
if fatal {
|
|
|
|
logger.Fatal().Err(err).Msg("could not unmarshal config")
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.Error().Err(err).Msg("could not unmarshal config")
|
|
|
|
|
|
|
|
return
|
2021-04-02 08:59:21 +02:00
|
|
|
}
|
2021-04-10 17:47:18 +02:00
|
|
|
|
2021-04-02 08:59:21 +02:00
|
|
|
if err := config.Cfg.Require(); err != nil {
|
2021-04-18 11:33:15 +02:00
|
|
|
if fatal {
|
|
|
|
logger.Fatal().Err(err).Msg("missing config parts")
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.Error().Err(err).Msg("missing config parts")
|
|
|
|
|
|
|
|
return
|
2021-04-02 08:59:21 +02:00
|
|
|
}
|
2021-03-30 19:59:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Execute executes the commandline interface.
|
|
|
|
func Execute() {
|
|
|
|
if err := rootCmd.Execute(); err != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|