Commit Diff


commit - ca426ad963867dc9a8223817e4f70d5a96bb5d5a
commit + e80961fe8ae238bac3883d9cffc9fe1941ae7bb6
blob - 45c7a4005010362dbf4e6924b2eea2556a0839bc
blob + e501bac2d3569b5e694aec6c5745416d8e8ff284
--- cmd/jiraexport/jiraexport.go
+++ cmd/jiraexport/jiraexport.go
@@ -29,14 +29,12 @@
 package main
 
 import (
-	"bytes"
 	"flag"
 	"fmt"
 	"io"
 	"io/fs"
 	"log"
 	"net/mail"
-	"net/url"
 	"os"
 	"path"
 	"strings"
@@ -45,23 +43,6 @@ import (
 	"olowe.co/issues/jira"
 )
 
-func readJiraAuth() (user, pass string, err error) {
-	confDir, err := os.UserConfigDir()
-	if err != nil {
-		return "", "", err
-	}
-	b, err := os.ReadFile(path.Join(confDir, "atlassian/jira"))
-	if err != nil {
-		return "", "", err
-	}
-	b = bytes.TrimSpace(b)
-	u, p, ok := strings.Cut(string(b), ":")
-	if !ok {
-		return "", "", fmt.Errorf(`missing ":" between username and password`)
-	}
-	return u, p, nil
-}
-
 func copyMessage(w io.Writer, msg *mail.Message) (n int, err error) {
 	for k, v := range msg.Header {
 		for i := range v {
@@ -98,19 +79,19 @@ func main() {
 		log.Fatal(usage)
 	}
 
-	user, pass, err := readJiraAuth()
+	confDir, err := os.UserConfigDir()
 	if err != nil {
-		log.Fatalf("read jira auth credentials: %v", err)
+		log.Fatal(err)
 	}
-	u, err := url.Parse(*apiRoot)
+	confDir = path.Join(confDir, "atlassian/jira")
+	config, err := readConfig(confDir)
 	if err != nil {
-		log.Fatalln("parse api url:", err)
+		log.Fatalln("read config:", err)
 	}
-	u.Path = path.Join(u.Path, "rest/api/2")
 	jclient := &jira.Client{
-		APIRoot:  u,
-		Username: user,
-		Password: pass,
+		APIRoot:  config.BaseURL,
+		Username: config.Username,
+		Password: config.Password,
 		Debug:    false,
 	}
 	fsys := &jira.FS{Client: jclient}
blob - /dev/null
blob + 8446f85284e03a77313b0d7f5fde033016c1fbfb (mode 644)
--- /dev/null
+++ cmd/jiraexport/config.go
@@ -0,0 +1,54 @@
+package main
+
+import (
+	"bufio"
+	"fmt"
+	"net/url"
+	"os"
+	"strings"
+)
+
+type Config struct {
+	BaseURL  *url.URL
+	Username string
+	Password string
+}
+
+func readConfig(name string) (*Config, error) {
+	f, err := os.Open(name)
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	var conf Config
+	sc := bufio.NewScanner(f)
+	for sc.Scan() {
+		line := strings.TrimSpace(sc.Text())
+		if strings.HasPrefix(line, "#") {
+			continue
+		} else if line == "" {
+			continue
+		}
+		k, v, ok := strings.Cut(line, " ")
+		if !ok {
+			return nil, fmt.Errorf("key %s: expected whitespace after configuration key", k)
+		} else if v == "" {
+			return nil, fmt.Errorf("key %s: missing parameter", k)
+		}
+		switch k {
+		case "url":
+			u, err := url.Parse(v)
+			if err != nil {
+				return nil, fmt.Errorf("parse base url: %w", err)
+			}
+			conf.BaseURL = u
+		case "username":
+			conf.Username = v
+		case "password":
+			conf.Password = v
+		default:
+			return nil, fmt.Errorf("unknown configuration key %q", k)
+		}
+	}
+	return &conf, sc.Err()
+}
blob - c0cf2f7316e4f8104c39eaa0c3a0d25b1b134670
blob + e8fccfe56b4bd2765fc93bd3aa497e24dff8fbf6
--- cmd/jiraq/jiraq.go
+++ cmd/jiraq/jiraq.go
@@ -30,11 +30,9 @@
 package main
 
 import (
-	"bytes"
 	"flag"
 	"fmt"
 	"log"
-	"net/url"
 	"os"
 	"path"
 	"strings"
@@ -42,23 +40,6 @@ import (
 	"olowe.co/issues/jira"
 )
 
-func readJiraAuth() (user, pass string, err error) {
-	confDir, err := os.UserConfigDir()
-	if err != nil {
-		return "", "", err
-	}
-	b, err := os.ReadFile(path.Join(confDir, "atlassian/jira"))
-	if err != nil {
-		return "", "", err
-	}
-	b = bytes.TrimSpace(b)
-	u, p, ok := strings.Cut(string(b), ":")
-	if !ok {
-		return "", "", fmt.Errorf(`missing ":" between username and password`)
-	}
-	return u, p, nil
-}
-
 var apiRoot = flag.String("u", "http://[::1]:8080", "base URL for the JIRA API")
 
 const usage = "usage: jiraq [-u url] query"
@@ -74,19 +55,20 @@ func main() {
 		log.Fatal(usage)
 	}
 
-	user, pass, err := readJiraAuth()
+	confDir, err := os.UserConfigDir()
 	if err != nil {
-		log.Fatalf("read jira auth credentials: %v", err)
+		log.Fatal(err)
 	}
-	u, err := url.Parse(*apiRoot)
+	confDir = path.Join(confDir, "atlassian/jira")
+	config, err := readConfig(confDir)
 	if err != nil {
-		log.Fatalln("parse api url:", err)
+		log.Fatalln("read config:", err)
 	}
-	u.Path = path.Join(u.Path, "rest/api/2")
+
 	client := &jira.Client{
-		APIRoot:  u,
-		Username: user,
-		Password: pass,
+		APIRoot:  config.BaseURL,
+		Username: config.Username,
+		Password: config.Password,
 	}
 
 	issues, err := client.SearchIssues(strings.Join(flag.Args(), " "))
blob - /dev/null
blob + 8446f85284e03a77313b0d7f5fde033016c1fbfb (mode 644)
--- /dev/null
+++ cmd/jiraq/config.go
@@ -0,0 +1,54 @@
+package main
+
+import (
+	"bufio"
+	"fmt"
+	"net/url"
+	"os"
+	"strings"
+)
+
+type Config struct {
+	BaseURL  *url.URL
+	Username string
+	Password string
+}
+
+func readConfig(name string) (*Config, error) {
+	f, err := os.Open(name)
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	var conf Config
+	sc := bufio.NewScanner(f)
+	for sc.Scan() {
+		line := strings.TrimSpace(sc.Text())
+		if strings.HasPrefix(line, "#") {
+			continue
+		} else if line == "" {
+			continue
+		}
+		k, v, ok := strings.Cut(line, " ")
+		if !ok {
+			return nil, fmt.Errorf("key %s: expected whitespace after configuration key", k)
+		} else if v == "" {
+			return nil, fmt.Errorf("key %s: missing parameter", k)
+		}
+		switch k {
+		case "url":
+			u, err := url.Parse(v)
+			if err != nil {
+				return nil, fmt.Errorf("parse base url: %w", err)
+			}
+			conf.BaseURL = u
+		case "username":
+			conf.Username = v
+		case "password":
+			conf.Password = v
+		default:
+			return nil, fmt.Errorf("unknown configuration key %q", k)
+		}
+	}
+	return &conf, sc.Err()
+}