diff --git a/.woodpecker/.build.yml b/.woodpecker/.build.yml index c5a4748..b0fd267 100644 --- a/.woodpecker/.build.yml +++ b/.woodpecker/.build.yml @@ -6,20 +6,9 @@ matrix: platform: ${PLATFORM} pipeline: - install-modules: - image: 'chewingbever/vlang:latest' - pull: true - environment: - - VMODULES=.vmodules - commands: - - 'cd src && v install' - when: - event: [pull_request] - debug: image: 'chewingbever/vlang:latest' - environment: - - VMODULES=.vmodules + pull: true commands: - make when: @@ -29,9 +18,9 @@ pipeline: prod: image: 'chewingbever/vlang:latest' + pull: true environment: - LDFLAGS=-lz -lbz2 -llzma -lexpat -lzstd -llz4 -lsqlite3 -static - - VMODULES=.vmodules commands: # Apparently this -D is *very* important - CFLAGS='-DGC_THREADS=1' make prod diff --git a/PKGBUILD b/PKGBUILD index 9afddc3..639ce95 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -14,15 +14,7 @@ license=('AGPL3') source=("$pkgname::git+https://git.rustybever.be/vieter/vieter#tag=${pkgver//_/-}") md5sums=('SKIP') -prepare() { - export VMODULES=.vmodules - - cd "$pkgname/src" && v install -} - build() { - export VMODULES=.vmodules - cd "$pkgname" make prod diff --git a/PKGBUILD.dev b/PKGBUILD.dev index 34e19a7..d25abb3 100644 --- a/PKGBUILD.dev +++ b/PKGBUILD.dev @@ -22,15 +22,7 @@ pkgver() { git describe --long --tags | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g' } -prepare() { - export VMODULES=.vmodules - - cd "$pkgname/src" && v install -} - build() { - export VMODULES=.vmodules - cd "$pkgname" make prod diff --git a/README.md b/README.md index 31f262e..892396a 100644 --- a/README.md +++ b/README.md @@ -41,10 +41,6 @@ Besides a V installer, Vieter also requires the following libraries to work: * openssl * sqlite3 -Vieter also depends on some external V modules which you can install using `cd -src && v install`. Make sure to keep these dependencies up to date using `v -update`. - ### Compiler Vieter compiles with the standard Vlang compiler. However, I do maintain a diff --git a/src/console/git/git.v b/src/console/git/git.v index 303909e..e27c1c4 100644 --- a/src/console/git/git.v +++ b/src/console/git/git.v @@ -1,7 +1,7 @@ module git import cli -import vieter.vconf +import env import cron.expression { parse_expression } import client import console @@ -41,7 +41,7 @@ pub fn cmd() cli.Command { ] execute: fn (cmd cli.Command) ? { config_file := cmd.flags.get_string('config-file')? - conf := vconf.load(default_path: config_file)? + conf := env.load(config_file)? mut filter := GitRepoFilter{} @@ -70,7 +70,7 @@ pub fn cmd() cli.Command { description: 'Add a new repository.' execute: fn (cmd cli.Command) ? { config_file := cmd.flags.get_string('config-file')? - conf := vconf.load(default_path: config_file)? + conf := env.load(config_file)? add(conf, cmd.args[0], cmd.args[1], cmd.args[2])? } @@ -82,7 +82,7 @@ pub fn cmd() cli.Command { description: 'Remove a repository that matches the given ID prefix.' execute: fn (cmd cli.Command) ? { config_file := cmd.flags.get_string('config-file')? - conf := vconf.load(default_path: config_file)? + conf := env.load(config_file)? remove(conf, cmd.args[0])? } @@ -94,7 +94,7 @@ pub fn cmd() cli.Command { description: 'Show detailed information for the repo matching the ID prefix.' execute: fn (cmd cli.Command) ? { config_file := cmd.flags.get_string('config-file')? - conf := vconf.load(default_path: config_file)? + conf := env.load(config_file)? info(conf, cmd.args[0])? } @@ -133,7 +133,7 @@ pub fn cmd() cli.Command { ] execute: fn (cmd cli.Command) ? { config_file := cmd.flags.get_string('config-file')? - conf := vconf.load(default_path: config_file)? + conf := env.load(config_file)? found := cmd.flags.get_all_found() @@ -155,7 +155,7 @@ pub fn cmd() cli.Command { description: 'Build the repo with the given id & publish it.' execute: fn (cmd cli.Command) ? { config_file := cmd.flags.get_string('config-file')? - conf := vconf.load(default_path: config_file)? + conf := env.load(config_file)? build(conf, cmd.args[0].int())? } diff --git a/src/console/logs/logs.v b/src/console/logs/logs.v index d63e284..cb6997f 100644 --- a/src/console/logs/logs.v +++ b/src/console/logs/logs.v @@ -1,7 +1,7 @@ module logs import cli -import vieter.vconf +import env import client import console import time @@ -65,7 +65,7 @@ pub fn cmd() cli.Command { ] execute: fn (cmd cli.Command) ? { config_file := cmd.flags.get_string('config-file')? - conf := vconf.load(default_path: config_file)? + conf := env.load(config_file)? mut filter := BuildLogFilter{} @@ -143,7 +143,7 @@ pub fn cmd() cli.Command { description: 'Show all info for a specific build log.' execute: fn (cmd cli.Command) ? { config_file := cmd.flags.get_string('config-file')? - conf := vconf.load(default_path: config_file)? + conf := env.load(config_file)? id := cmd.args[0].int() info(conf, id)? @@ -156,7 +156,7 @@ pub fn cmd() cli.Command { description: 'Output the content of a build log to stdout.' execute: fn (cmd cli.Command) ? { config_file := cmd.flags.get_string('config-file')? - conf := vconf.load(default_path: config_file)? + conf := env.load(config_file)? id := cmd.args[0].int() content(conf, id)? diff --git a/src/cron/cli.v b/src/cron/cli.v index 10a4bdd..9703c66 100644 --- a/src/cron/cli.v +++ b/src/cron/cli.v @@ -1,7 +1,7 @@ module cron import cli -import vieter.vconf +import env struct Config { pub: @@ -24,7 +24,7 @@ pub fn cmd() cli.Command { description: 'Start the cron service that periodically runs builds.' execute: fn (cmd cli.Command) ? { config_file := cmd.flags.get_string('config-file')? - conf := vconf.load(default_path: config_file)? + conf := env.load(config_file)? cron(conf)? } diff --git a/src/env/README.md b/src/env/README.md new file mode 100644 index 0000000..135e8fa --- /dev/null +++ b/src/env/README.md @@ -0,0 +1,7 @@ +This module provides a framework for parsing a configuration, defined as a +struct, from both a TOML configuration file & environment variables. Some +notable features are: + +* Overwrite values in config file using environment variables +* Allow default values in config struct +* Read environment variable value from file diff --git a/src/env/env.v b/src/env/env.v new file mode 100644 index 0000000..5ed1955 --- /dev/null +++ b/src/env/env.v @@ -0,0 +1,102 @@ +module env + +import os +import toml + +const ( + // The prefix that every environment variable should have + prefix = 'VIETER_' + // The suffix an environment variable in order for it to be loaded from a file + // instead + file_suffix = '_FILE' +) + +// get_env_var tries to read the contents of the given environment variable. It +// looks for either `${env.prefix}${field_name.to_upper()}` or +// `${env.prefix}${field_name.to_upper()}${env.file_suffix}`, returning the +// contents of the file instead if the latter. If both or neither exist, the +// function returns an error. +fn get_env_var(field_name string) ?string { + env_var_name := '$env.prefix$field_name.to_upper()' + env_file_name := '$env.prefix$field_name.to_upper()$env.file_suffix' + env_var := os.getenv(env_var_name) + env_file := os.getenv(env_file_name) + + // If both are missing, we return an empty string + if env_var == '' && env_file == '' { + return '' + } + + // If they're both set, we report a conflict + if env_var != '' && env_file != '' { + return error('Only one of $env_var_name or $env_file_name can be defined.') + } + + // If it's the env var itself, we return it. + // I'm pretty sure this also prevents variable ending in _FILE (e.g. + // VIETER_LOG_FILE) from being mistakingely read as an _FILE suffixed env + // var. + if env_var != '' { + return env_var + } + + // Otherwise, we process the file + return os.read_file(env_file) or { + error('Failed to read file defined in $env_file_name: ${err.msg()}.') + } +} + +// load attempts to create an object of type T from the given path to a toml +// file & environment variables. For each field, it will select either a value +// given from an environment variable, a value defined in the config file or a +// configured default if present, in that order. +pub fn load(path string) ?T { + mut res := T{} + + if os.exists(path) { + // We don't use reflect here because reflect also sets any fields not + // in the toml back to their zero value, which we don't want + doc := toml.parse_file(path)? + + $for field in T.fields { + s := doc.value(field.name) + + if s !is toml.Null { + $if field.typ is string { + res.$(field.name) = s.string() + } $else $if field.typ is int { + res.$(field.name) = s.int() + } + } + } + } + + $for field in T.fields { + env_value := get_env_var(field.name)? + + // The value of an env var will always take precedence over the toml + // file. + if env_value != '' { + $if field.typ is string { + res.$(field.name) = env_value + } $else $if field.typ is int { + res.$(field.name) = env_value.int() + } + } + + // Now, we check whether a value is present. If there isn't, that means + // it isn't in the config file, nor is there a default or an env var. + mut has_value := false + + $if field.typ is string { + has_value = res.$(field.name) != '' + } $else $if field.typ is int { + has_value = res.$(field.name) != 0 + } + + if !has_value { + return error("Missing config variable '$field.name' with no provided default. Either add it to the config file or provide it using an environment variable.") + } + } + return res +} diff --git a/src/server/cli.v b/src/server/cli.v index cf0306c..556efcf 100644 --- a/src/server/cli.v +++ b/src/server/cli.v @@ -1,7 +1,7 @@ module server import cli -import vieter.vconf +import env struct Config { pub: @@ -19,7 +19,7 @@ pub fn cmd() cli.Command { description: 'Start the Vieter server.' execute: fn (cmd cli.Command) ? { config_file := cmd.flags.get_string('config-file')? - conf := vconf.load(default_path: config_file)? + conf := env.load(config_file)? server(conf)? } diff --git a/src/v.mod b/src/v.mod index 7f45917..e69de29 100644 --- a/src/v.mod +++ b/src/v.mod @@ -1,5 +0,0 @@ -Module { - dependencies: [ - 'https://git.rustybever.be/vieter/vconf' - ] -}