Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add config file definitions #459

Merged
merged 8 commits into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,20 @@ linters:
- tagliatelle
- cyclop
- testpackage
run:
timeout: 3m

issues:
max-issues-per-linter: 0
max-same-issues: 0
exclude-rules:
# Exclude some linters from testing files.
- linters:
- forbidigo
- lll
path: 'init/config/.*.\.go'
output:
sort-results: true
run:
timeout: 3m

linters-settings:
ireturn:
Expand All @@ -41,4 +47,5 @@ linters-settings:
- github.com/radovskyb/watcher
- github.com/prometheus/client_golang/
- github.com/spf13/pflag
- github.com/julienschmidt/httprouter
- github.com/julienschmidt/httprouter
- github.com/BurntSushi/toml
5 changes: 2 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/Unpackerr/unpackerr
go 1.22

require (
github.com/BurntSushi/toml v1.4.0
github.com/fsnotify/fsnotify v1.7.0
github.com/gen2brain/dlgs v0.0.0-20220603100644-40c77870fa8d
github.com/getlantern/systray v1.2.2
Expand All @@ -20,11 +21,10 @@ require (
golift.io/starr v1.0.0
golift.io/version v0.0.2
golift.io/xtractr v0.2.3-0.20240710043203-2d7c8a38d931

gopkg.in/yaml.v3 v3.0.1
)

require (
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bodgit/plumbing v1.3.0 // indirect
Expand Down Expand Up @@ -68,5 +68,4 @@ require (
golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
3 changes: 3 additions & 0 deletions init/config/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- All params must have a default, even if it's `[]` or `''`.
- Examples override params, but get commented out.
- All list params are commented.
117 changes: 117 additions & 0 deletions init/config/compose-builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package main

import (
"bytes"
"fmt"
"strings"
)

const (
space = " "
composeHeader = `### Unpackerr docker-compose.yml Example
### Please read this page for help using this example:
### https://unpackerr.zip/docs/install/compose
### Generator: https://notifiarr.com/unpackerr
##################################################################
services:

unpackerr:
image: golift/unpackerr
container_name: unpackerr
volumes:
# You need at least this one volume mapped so Unpackerr can find your files to extract.
# Make sure this matches your Starr apps; the folder mount (/downloads or /data) should be identical.
- /mnt/HostDownloads:/downloads
restart: always
# Get the user:group correct so unpackerr can read and write to your files.
user: ${PUID}:${PGID}
#user: 1000:100
# What you see below are defaults for this compose. You only need to modify things specific to your environment.
# Remove apps and feature configs you do not use or need.
# ie. Remove all lines that begin with UN_CMDHOOK, UN_WEBHOOK, UN_FOLDER, UN_WEBSERVER, and other apps you do not use.
environment:
- TZ=${TZ}`
)

func printCompose(config *Config) {
fmt.Println(composeHeader)

// Loop the 'Order' list.
for _, section := range config.Order {
// If Order contains a missing section, panic.
if config.Sections[section] == nil {
panic(section + ": in order, but missing from sections. This is a bug in conf-builder.yml.")
}

if config.Defs[section] == nil {
fmt.Print(config.Sections[section].makeCompose(config.Sections[section].Title, config.Prefix, false))
} else {
fmt.Print(config.Sections[section].makeComposeDefined(config.Prefix, config.Defs[section], config.DefOrder[section], false))
}
}
}

func (h *Header) makeCompose(title, prefix string, bare bool) string {
var buf bytes.Buffer

if len(h.Params) > 0 && bare {
buf.WriteString("## " + title + "\n")
} else if len(h.Params) > 0 {
buf.WriteString(space + " ## " + title + "\n")
}

pfx := space + " - "
if bare {
pfx = ""
}

for _, param := range h.Params {
if h.Kind == list {
buf.WriteString(param.Compose(pfx + prefix + h.Prefix + "0_"))
} else {
buf.WriteString(param.Compose(pfx + prefix + h.Prefix))
}
}

return buf.String()
}

func (h *Header) makeComposeDefined(prefix string, defs Defs, order []section, bare bool) string {
var buf bytes.Buffer

for _, section := range order {
newHeader := createDefinedSection(defs[section], h)
// Make a brand new section and print it.
buf.WriteString(newHeader.makeCompose(h.Title, prefix, bare))
}

return buf.String()
}

func (p *Param) Compose(prefix string) string {
val := p.Default
if p.Example != nil {
val = p.Example
}

switch p.Kind {
default:
return fmt.Sprint(prefix, p.EnvVar, "=", val, "\n")
case list:
var out string

for idx, sv := range val.([]any) { //nolint:forcetypeassert
out += fmt.Sprint(prefix, p.EnvVar, idx, "=", sv, "\n")
}

return out
case "conlist":
out := []string{}

for _, sv := range val.([]any) { //nolint:forcetypeassert
out = append(out, fmt.Sprint(sv))
}

return fmt.Sprint(prefix, p.EnvVar, "=", strings.Join(out, ","), "\n")
}
}
109 changes: 109 additions & 0 deletions init/config/conf-builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package main

import (
"bytes"
"fmt"
"strings"

"github.com/BurntSushi/toml"
)

func printConfFile(config *Config) {
// Loop the 'Order' list.
for _, section := range config.Order {
// If Order contains a missing section, panic.
if config.Sections[section] == nil {
panic(section + ": in order, but missing from sections. This is a bug in conf-builder.yml.")
}

if config.Defs[section] != nil {
fmt.Print(config.Sections[section].makeDefinedSection(config.Defs[section], config.DefOrder[section], false))
} else {
fmt.Print(config.Sections[section].makeSection(section, false, false))
}
}
}

// Not all sections have defs, and it may be nil. Defs only work on 'list' sections.
func (h *Header) makeSection(name section, showHeader, showValue bool) string {
var buf bytes.Buffer

// Print section header text.
if h.Text != "" {
buf.WriteString(h.Text)
}

comment := "#"
if showHeader {
// this only happens when a defined section has a comment override on the repeating headers.
comment = ""
}

if !h.NoHeader { // Print the [section] or [[section]] header.
if h.Kind == list { // list sections are commented by default.
buf.WriteString(comment + "[[" + string(name) + "]]" + "\n") // list sections use double-brackets.
} else {
buf.WriteString("[" + string(name) + "]" + "\n") // non-list sections use single brackets.
}
}

for _, param := range h.Params {
// Print an empty newline for each param if the section has no header and the param has a description.
if h.NoHeader && param.Desc != "" {
buf.WriteString("\n")
}

// Add ## to the beginning of each line in the description.
// Uses the newline \n character to figure out where each line begins.
if param.Desc != "" {
buf.WriteString("## " + strings.ReplaceAll(strings.TrimSpace(param.Desc), "\n", "\n## ") + "\n")
}

switch {
default:
fallthrough
case showValue:
buf.WriteString(fmt.Sprintf("%s = %s\n", param.Name, param.Value()))
case param.Example != nil:
// If example is not empty, use that commented out, otherwise use the default.
fallthrough
case h.Kind == list:
// If the 'kind' is a 'list', we comment all the parameters.
buf.WriteString(fmt.Sprintf("#%s = %s\n", param.Name, param.Value()))
}
}

// Each section needs a newline at the end.
buf.WriteString("\n")

return buf.String()
}

func (p *Param) Value() string {
// If example is not empty, use that commented out, otherwise use the default.
out, _ := toml.Marshal(p.Default)
if p.Example != nil {
out, _ = toml.Marshal(p.Example)
}

// The toml marshaller uses only regular quotes " which kinda suck, so replace them with single quotes ' on file paths.
if strings.Contains(p.Name, "path") || strings.HasSuffix(p.Name, "file") || p.Name == "command" {
return string(bytes.ReplaceAll(out, []byte{'"'}, []byte("'")))
}

return string(out)
}

// makeDefinedSection duplicates sections from overrides, and prints it once for each override.
func (h *Header) makeDefinedSection(defs Defs, order []section, showValue bool) string {
var buf bytes.Buffer

for _, section := range order {
newHeader := createDefinedSection(defs[section], h)
// Make a brand new section and pass it back in.
// Only defined sections can comment the header.
buf.WriteString(newHeader.makeSection(section, !defs[section].Comment, showValue))
}

return buf.String()
}
Loading