Skip to content

Commit 87340c0

Browse files
authored
Merge pull request #459 from Unpackerr/dn2_conf_builder
Add config file definitions
2 parents c6a48ea + 31a776b commit 87340c0

8 files changed

Lines changed: 1334 additions & 6 deletions

File tree

.golangci.yml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,20 @@ linters:
1212
- tagliatelle
1313
- cyclop
1414
- testpackage
15-
run:
16-
timeout: 3m
1715

1816
issues:
1917
max-issues-per-linter: 0
2018
max-same-issues: 0
19+
exclude-rules:
20+
# Exclude some linters from testing files.
21+
- linters:
22+
- forbidigo
23+
- lll
24+
path: 'init/config/.*.\.go'
2125
output:
2226
sort-results: true
27+
run:
28+
timeout: 3m
2329

2430
linters-settings:
2531
ireturn:
@@ -41,4 +47,5 @@ linters-settings:
4147
- github.com/radovskyb/watcher
4248
- github.com/prometheus/client_golang/
4349
- github.com/spf13/pflag
44-
- github.com/julienschmidt/httprouter
50+
- github.com/julienschmidt/httprouter
51+
- github.com/BurntSushi/toml

go.mod

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/Unpackerr/unpackerr
33
go 1.22
44

55
require (
6+
github.com/BurntSushi/toml v1.4.0
67
github.com/fsnotify/fsnotify v1.7.0
78
github.com/gen2brain/dlgs v0.0.0-20220603100644-40c77870fa8d
89
github.com/getlantern/systray v1.2.2
@@ -20,11 +21,10 @@ require (
2021
golift.io/starr v1.0.0
2122
golift.io/version v0.0.2
2223
golift.io/xtractr v0.2.3-0.20240710043203-2d7c8a38d931
23-
24+
gopkg.in/yaml.v3 v3.0.1
2425
)
2526

2627
require (
27-
github.com/BurntSushi/toml v1.4.0 // indirect
2828
github.com/andybalholm/brotli v1.1.0 // indirect
2929
github.com/beorn7/perks v1.0.1 // indirect
3030
github.com/bodgit/plumbing v1.3.0 // indirect
@@ -68,5 +68,4 @@ require (
6868
golang.org/x/sys v0.21.0 // indirect
6969
golang.org/x/text v0.16.0 // indirect
7070
google.golang.org/protobuf v1.34.2 // indirect
71-
gopkg.in/yaml.v3 v3.0.1 // indirect
7271
)

init/config/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
- All params must have a default, even if it's `[]` or `''`.
2+
- Examples override params, but get commented out.
3+
- All list params are commented.

init/config/compose-builder.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"strings"
7+
)
8+
9+
const (
10+
space = " "
11+
composeHeader = `### Unpackerr docker-compose.yml Example
12+
### Please read this page for help using this example:
13+
### https://unpackerr.zip/docs/install/compose
14+
### Generator: https://notifiarr.com/unpackerr
15+
##################################################################
16+
services:
17+
18+
unpackerr:
19+
image: golift/unpackerr
20+
container_name: unpackerr
21+
volumes:
22+
# You need at least this one volume mapped so Unpackerr can find your files to extract.
23+
# Make sure this matches your Starr apps; the folder mount (/downloads or /data) should be identical.
24+
- /mnt/HostDownloads:/downloads
25+
restart: always
26+
# Get the user:group correct so unpackerr can read and write to your files.
27+
user: ${PUID}:${PGID}
28+
#user: 1000:100
29+
# What you see below are defaults for this compose. You only need to modify things specific to your environment.
30+
# Remove apps and feature configs you do not use or need.
31+
# ie. Remove all lines that begin with UN_CMDHOOK, UN_WEBHOOK, UN_FOLDER, UN_WEBSERVER, and other apps you do not use.
32+
environment:
33+
- TZ=${TZ}`
34+
)
35+
36+
func printCompose(config *Config) {
37+
fmt.Println(composeHeader)
38+
39+
// Loop the 'Order' list.
40+
for _, section := range config.Order {
41+
// If Order contains a missing section, panic.
42+
if config.Sections[section] == nil {
43+
panic(section + ": in order, but missing from sections. This is a bug in conf-builder.yml.")
44+
}
45+
46+
if config.Defs[section] == nil {
47+
fmt.Print(config.Sections[section].makeCompose(config.Sections[section].Title, config.Prefix, false))
48+
} else {
49+
fmt.Print(config.Sections[section].makeComposeDefined(config.Prefix, config.Defs[section], config.DefOrder[section], false))
50+
}
51+
}
52+
}
53+
54+
func (h *Header) makeCompose(title, prefix string, bare bool) string {
55+
var buf bytes.Buffer
56+
57+
if len(h.Params) > 0 && bare {
58+
buf.WriteString("## " + title + "\n")
59+
} else if len(h.Params) > 0 {
60+
buf.WriteString(space + " ## " + title + "\n")
61+
}
62+
63+
pfx := space + " - "
64+
if bare {
65+
pfx = ""
66+
}
67+
68+
for _, param := range h.Params {
69+
if h.Kind == list {
70+
buf.WriteString(param.Compose(pfx + prefix + h.Prefix + "0_"))
71+
} else {
72+
buf.WriteString(param.Compose(pfx + prefix + h.Prefix))
73+
}
74+
}
75+
76+
return buf.String()
77+
}
78+
79+
func (h *Header) makeComposeDefined(prefix string, defs Defs, order []section, bare bool) string {
80+
var buf bytes.Buffer
81+
82+
for _, section := range order {
83+
newHeader := createDefinedSection(defs[section], h)
84+
// Make a brand new section and print it.
85+
buf.WriteString(newHeader.makeCompose(h.Title, prefix, bare))
86+
}
87+
88+
return buf.String()
89+
}
90+
91+
func (p *Param) Compose(prefix string) string {
92+
val := p.Default
93+
if p.Example != nil {
94+
val = p.Example
95+
}
96+
97+
switch p.Kind {
98+
default:
99+
return fmt.Sprint(prefix, p.EnvVar, "=", val, "\n")
100+
case list:
101+
var out string
102+
103+
for idx, sv := range val.([]any) { //nolint:forcetypeassert
104+
out += fmt.Sprint(prefix, p.EnvVar, idx, "=", sv, "\n")
105+
}
106+
107+
return out
108+
case "conlist":
109+
out := []string{}
110+
111+
for _, sv := range val.([]any) { //nolint:forcetypeassert
112+
out = append(out, fmt.Sprint(sv))
113+
}
114+
115+
return fmt.Sprint(prefix, p.EnvVar, "=", strings.Join(out, ","), "\n")
116+
}
117+
}

init/config/conf-builder.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/BurntSushi/toml"
9+
)
10+
11+
func printConfFile(config *Config) {
12+
// Loop the 'Order' list.
13+
for _, section := range config.Order {
14+
// If Order contains a missing section, panic.
15+
if config.Sections[section] == nil {
16+
panic(section + ": in order, but missing from sections. This is a bug in conf-builder.yml.")
17+
}
18+
19+
if config.Defs[section] != nil {
20+
fmt.Print(config.Sections[section].makeDefinedSection(config.Defs[section], config.DefOrder[section], false))
21+
} else {
22+
fmt.Print(config.Sections[section].makeSection(section, false, false))
23+
}
24+
}
25+
}
26+
27+
// Not all sections have defs, and it may be nil. Defs only work on 'list' sections.
28+
func (h *Header) makeSection(name section, showHeader, showValue bool) string {
29+
var buf bytes.Buffer
30+
31+
// Print section header text.
32+
if h.Text != "" {
33+
buf.WriteString(h.Text)
34+
}
35+
36+
comment := "#"
37+
if showHeader {
38+
// this only happens when a defined section has a comment override on the repeating headers.
39+
comment = ""
40+
}
41+
42+
if !h.NoHeader { // Print the [section] or [[section]] header.
43+
if h.Kind == list { // list sections are commented by default.
44+
buf.WriteString(comment + "[[" + string(name) + "]]" + "\n") // list sections use double-brackets.
45+
} else {
46+
buf.WriteString("[" + string(name) + "]" + "\n") // non-list sections use single brackets.
47+
}
48+
}
49+
50+
for _, param := range h.Params {
51+
// Print an empty newline for each param if the section has no header and the param has a description.
52+
if h.NoHeader && param.Desc != "" {
53+
buf.WriteString("\n")
54+
}
55+
56+
// Add ## to the beginning of each line in the description.
57+
// Uses the newline \n character to figure out where each line begins.
58+
if param.Desc != "" {
59+
buf.WriteString("## " + strings.ReplaceAll(strings.TrimSpace(param.Desc), "\n", "\n## ") + "\n")
60+
}
61+
62+
switch {
63+
default:
64+
fallthrough
65+
case showValue:
66+
buf.WriteString(fmt.Sprintf("%s = %s\n", param.Name, param.Value()))
67+
case param.Example != nil:
68+
// If example is not empty, use that commented out, otherwise use the default.
69+
fallthrough
70+
case h.Kind == list:
71+
// If the 'kind' is a 'list', we comment all the parameters.
72+
buf.WriteString(fmt.Sprintf("#%s = %s\n", param.Name, param.Value()))
73+
}
74+
}
75+
76+
// Each section needs a newline at the end.
77+
buf.WriteString("\n")
78+
79+
return buf.String()
80+
}
81+
82+
func (p *Param) Value() string {
83+
// If example is not empty, use that commented out, otherwise use the default.
84+
out, _ := toml.Marshal(p.Default)
85+
if p.Example != nil {
86+
out, _ = toml.Marshal(p.Example)
87+
}
88+
89+
// The toml marshaller uses only regular quotes " which kinda suck, so replace them with single quotes ' on file paths.
90+
if strings.Contains(p.Name, "path") || strings.HasSuffix(p.Name, "file") || p.Name == "command" {
91+
return string(bytes.ReplaceAll(out, []byte{'"'}, []byte("'")))
92+
}
93+
94+
return string(out)
95+
}
96+
97+
// makeDefinedSection duplicates sections from overrides, and prints it once for each override.
98+
func (h *Header) makeDefinedSection(defs Defs, order []section, showValue bool) string {
99+
var buf bytes.Buffer
100+
101+
for _, section := range order {
102+
newHeader := createDefinedSection(defs[section], h)
103+
// Make a brand new section and pass it back in.
104+
// Only defined sections can comment the header.
105+
buf.WriteString(newHeader.makeSection(section, !defs[section].Comment, showValue))
106+
}
107+
108+
return buf.String()
109+
}

0 commit comments

Comments
 (0)