Make some options configurable

Amolith created

Implements: https://todo.sr.ht/~amolith/willow/4

Change summary

go.mod      |   4 +
go.sum      |   4 +
main.go     | 113 ++++++++++++++++++++++++++++++++++++++++++++++--------
releases.go |   2 
4 files changed, 104 insertions(+), 19 deletions(-)

Detailed changes

go.mod 🔗

@@ -7,8 +7,11 @@ module git.sr.ht/~amolith/willow
 go 1.20
 
 require (
+	github.com/BurntSushi/toml v1.3.2
 	github.com/go-git/go-git/v5 v5.8.0
+	github.com/microcosm-cc/bluemonday v1.0.25
 	github.com/mmcdole/gofeed v1.2.1
+	github.com/spf13/pflag v1.0.5
 )
 
 require (
@@ -28,7 +31,6 @@ require (
 	github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/kevinburke/ssh_config v1.2.0 // indirect
-	github.com/microcosm-cc/bluemonday v1.0.25 // indirect
 	github.com/mmcdole/goxpp v1.1.0 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect

go.sum 🔗

@@ -1,3 +1,5 @@
+github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
+github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
 github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
 github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
 github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
@@ -78,6 +80,8 @@ github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NF
 github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM=
 github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=

main.go 🔗

@@ -8,6 +8,7 @@ import (
 	"encoding/csv"
 	"errors"
 	"fmt"
+	"github.com/BurntSushi/toml"
 	"log"
 	"net/http"
 	"os"
@@ -16,6 +17,7 @@ import (
 	"time"
 
 	"github.com/microcosm-cc/bluemonday"
+	flag "github.com/spf13/pflag"
 )
 
 type (
@@ -37,9 +39,23 @@ type (
 		URL     string
 		Date    time.Time
 	}
+
+	Config struct {
+		Server      server
+		CSVLocation string
+		// TODO: Make cache location configurable
+		// CacheLocation string
+		FetchInterval int
+	}
+
+	server struct {
+		Listen string
+	}
 )
 
 var (
+	flagConfig    *string = flag.StringP("config", "c", "config.toml", "Path to config file")
+	config        Config
 	req           = make(chan struct{})
 	manualRefresh = make(chan struct{})
 	res           = make(chan []project)
@@ -51,26 +67,20 @@ var (
 )
 
 func main() {
-	file, err := os.Open("projects.csv")
+
+	flag.Parse()
+
+	err := checkConfig()
 	if err != nil {
-		if os.IsNotExist(err) {
-			file, err = os.Create("projects.csv")
-			if err != nil {
-				log.Fatalln(err)
-			}
-			defer file.Close()
+		log.Fatalln(err)
+	}
 
-			_, err = file.WriteString("url,name,forge,running\nhttps://git.sr.ht/~amolith/earl,earl,sourcehut,v0.0.1-rc0\n")
-			if err != nil {
-				log.Fatalln(err)
-			}
-		} else {
-			log.Fatalln(err)
-		}
+	err = checkCSV()
+	if err != nil {
+		log.Fatalln(err)
 	}
-	defer file.Close()
 
-	reader := csv.NewReader(file)
+	reader := csv.NewReader(strings.NewReader(config.CSVLocation))
 
 	records, err := reader.ReadAll()
 	if err != nil {
@@ -98,7 +108,7 @@ func main() {
 	mux := http.NewServeMux()
 
 	httpServer := &http.Server{
-		Addr:    "0.0.0.0:1337",
+		Addr:    config.Server.Listen,
 		Handler: mux,
 	}
 
@@ -147,3 +157,72 @@ func refreshLoop(manualRefresh, req chan struct{}, res chan []project) {
 		}
 	}
 }
+
+func checkConfig() error {
+	file, err := os.Open(*flagConfig)
+	if err != nil {
+		if os.IsNotExist(err) {
+			file, err = os.Create(*flagConfig)
+			if err != nil {
+				return err
+			}
+			defer file.Close()
+
+			_, err = file.WriteString("# Location of the CSV file containing the projects\nCSVLocation = \"projects.csv\"\n# How often to fetch new releases in seconds\nFetchInterval = 3600\n\n[Server]\n# Address to listen on\nListen = \"127.0.0.1:1313\"\n")
+			if err != nil {
+				return err
+			}
+
+			fmt.Println("Config file created at", *flagConfig)
+			fmt.Println("Please edit it and restart the server")
+			os.Exit(0)
+		} else {
+			return err
+		}
+	}
+	defer file.Close()
+
+	_, err = toml.DecodeFile(*flagConfig, &config)
+	if err != nil {
+		return err
+	}
+
+	if config.CSVLocation == "" {
+		fmt.Println("No CSV location specified, using projects.csv")
+		config.CSVLocation = "projects.csv"
+	}
+
+	if config.FetchInterval < 10 {
+		fmt.Println("Fetch interval is set to", config.FetchInterval, "seconds, but the minimum is 10, using 10")
+		config.FetchInterval = 10
+	}
+
+	if config.Server.Listen == "" {
+		fmt.Println("No listen address specified, using 127.0.0.1:1313")
+		config.Server.Listen = "127.0.0.1:1313"
+	}
+
+	return nil
+}
+
+func checkCSV() error {
+	file, err := os.Open(config.CSVLocation)
+	if err != nil {
+		if os.IsNotExist(err) {
+			file, err = os.Create(config.CSVLocation)
+			if err != nil {
+				return err
+			}
+			defer file.Close()
+
+			_, err = file.WriteString("url,name,forge,running\nhttps://git.sr.ht/~amolith/earl,earl,sourcehut,v0.0.1-rc0\n")
+			if err != nil {
+				return err
+			}
+		} else {
+			return err
+		}
+	}
+	defer file.Close()
+	return nil
+}

releases.go 🔗

@@ -64,7 +64,7 @@ func untrack(url string) {
 }
 
 func writeCSV() {
-	file, err := os.OpenFile("projects.csv", os.O_RDWR|os.O_CREATE, 0o600)
+	file, err := os.OpenFile(config.CSVLocation, os.O_RDWR|os.O_CREATE, 0o600)
 	if err != nil {
 		log.Fatalln(err)
 	}