Merge pull request 'Migrated env module to own Git repository' (#244) from Chewing_Bever/vieter:split-env into dev
ci/woodpecker/push/test Pipeline is pending Details
ci/woodpecker/push/build Pipeline failed Details
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline failed Details
ci/woodpecker/push/docker unknown status Details
ci/woodpecker/push/man unknown status Details
ci/woodpecker/push/deploy unknown status Details
ci/woodpecker/push/arch Pipeline failed Details

Reviewed-on: vieter/vieter#244
pull/248/head
Jef Roosens 2022-06-15 19:57:43 +02:00
commit ae04fe63a7
11 changed files with 51 additions and 126 deletions

View File

@ -6,10 +6,19 @@ matrix:
platform: ${PLATFORM} platform: ${PLATFORM}
pipeline: pipeline:
debug: install-modules:
image: 'chewingbever/vlang:latest' image: 'chewingbever/vlang:latest'
pull: true pull: true
commands: commands:
- export VMODULES=$PWD/.vmodules
- 'cd src && v install'
when:
event: [pull_request]
debug:
image: 'chewingbever/vlang:latest'
commands:
- export VMODULES=$PWD/.vmodules
- make - make
when: when:
event: [pull_request] event: [pull_request]
@ -18,10 +27,10 @@ pipeline:
prod: prod:
image: 'chewingbever/vlang:latest' image: 'chewingbever/vlang:latest'
pull: true
environment: environment:
- LDFLAGS=-lz -lbz2 -llzma -lexpat -lzstd -llz4 -lsqlite3 -static - LDFLAGS=-lz -lbz2 -llzma -lexpat -lzstd -llz4 -lsqlite3 -static
commands: commands:
- export VMODULES=$PWD/.vmodules
# Apparently this -D is *very* important # Apparently this -D is *very* important
- CFLAGS='-DGC_THREADS=1' make prod - CFLAGS='-DGC_THREADS=1' make prod
# Make sure the binary is actually statically built # Make sure the binary is actually statically built

View File

@ -14,7 +14,15 @@ license=('AGPL3')
source=("$pkgname::git+https://git.rustybever.be/vieter/vieter#tag=${pkgver//_/-}") source=("$pkgname::git+https://git.rustybever.be/vieter/vieter#tag=${pkgver//_/-}")
md5sums=('SKIP') md5sums=('SKIP')
prepare() {
export VMODULES="$srcdir/.vmodules"
cd "$pkgname/src" && v install
}
build() { build() {
export VMODULES="$srcdir/.vmodules"
cd "$pkgname" cd "$pkgname"
make prod make prod

View File

@ -22,7 +22,15 @@ pkgver() {
git describe --long --tags | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g' git describe --long --tags | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g'
} }
prepare() {
export VMODULES="$srcdir/.vmodules"
cd "$pkgname/src" && v install
}
build() { build() {
export VMODULES="$srcdir/.vmodules"
cd "$pkgname" cd "$pkgname"
make prod make prod

View File

@ -41,6 +41,10 @@ Besides a V installer, Vieter also requires the following libraries to work:
* openssl * openssl
* sqlite3 * 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 ### Compiler
Vieter compiles with the standard Vlang compiler. However, I do maintain a Vieter compiles with the standard Vlang compiler. However, I do maintain a

View File

@ -1,7 +1,7 @@
module git module git
import cli import cli
import env import vieter.vconf
import cron.expression { parse_expression } import cron.expression { parse_expression }
import client import client
import console import console
@ -41,7 +41,7 @@ pub fn cmd() cli.Command {
] ]
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := env.load<Config>(config_file)? conf := vconf.load<Config>(default_path: config_file)?
mut filter := GitRepoFilter{} mut filter := GitRepoFilter{}
@ -70,7 +70,7 @@ pub fn cmd() cli.Command {
description: 'Add a new repository.' description: 'Add a new repository.'
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := env.load<Config>(config_file)? conf := vconf.load<Config>(default_path: config_file)?
add(conf, cmd.args[0], cmd.args[1], cmd.args[2])? 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.' description: 'Remove a repository that matches the given ID prefix.'
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := env.load<Config>(config_file)? conf := vconf.load<Config>(default_path: config_file)?
remove(conf, cmd.args[0])? 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.' description: 'Show detailed information for the repo matching the ID prefix.'
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := env.load<Config>(config_file)? conf := vconf.load<Config>(default_path: config_file)?
info(conf, cmd.args[0])? info(conf, cmd.args[0])?
} }
@ -133,7 +133,7 @@ pub fn cmd() cli.Command {
] ]
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := env.load<Config>(config_file)? conf := vconf.load<Config>(default_path: config_file)?
found := cmd.flags.get_all_found() 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.' description: 'Build the repo with the given id & publish it.'
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := env.load<Config>(config_file)? conf := vconf.load<Config>(default_path: config_file)?
build(conf, cmd.args[0].int())? build(conf, cmd.args[0].int())?
} }

View File

@ -1,7 +1,7 @@
module logs module logs
import cli import cli
import env import vieter.vconf
import client import client
import console import console
import time import time
@ -65,7 +65,7 @@ pub fn cmd() cli.Command {
] ]
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := env.load<Config>(config_file)? conf := vconf.load<Config>(default_path: config_file)?
mut filter := BuildLogFilter{} mut filter := BuildLogFilter{}
@ -143,7 +143,7 @@ pub fn cmd() cli.Command {
description: 'Show all info for a specific build log.' description: 'Show all info for a specific build log.'
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := env.load<Config>(config_file)? conf := vconf.load<Config>(default_path: config_file)?
id := cmd.args[0].int() id := cmd.args[0].int()
info(conf, id)? info(conf, id)?
@ -156,7 +156,7 @@ pub fn cmd() cli.Command {
description: 'Output the content of a build log to stdout.' description: 'Output the content of a build log to stdout.'
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := env.load<Config>(config_file)? conf := vconf.load<Config>(default_path: config_file)?
id := cmd.args[0].int() id := cmd.args[0].int()
content(conf, id)? content(conf, id)?

View File

@ -1,7 +1,7 @@
module cron module cron
import cli import cli
import env import vieter.vconf
struct Config { struct Config {
pub: pub:
@ -24,7 +24,7 @@ pub fn cmd() cli.Command {
description: 'Start the cron service that periodically runs builds.' description: 'Start the cron service that periodically runs builds.'
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := env.load<Config>(config_file)? conf := vconf.load<Config>(default_path: config_file)?
cron(conf)? cron(conf)?
} }

7
src/env/README.md vendored
View File

@ -1,7 +0,0 @@
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

102
src/env/env.v vendored
View File

@ -1,102 +0,0 @@
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<T> 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<T>(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
}

View File

@ -1,7 +1,7 @@
module server module server
import cli import cli
import env import vieter.vconf
struct Config { struct Config {
pub: pub:
@ -19,7 +19,7 @@ pub fn cmd() cli.Command {
description: 'Start the Vieter server.' description: 'Start the Vieter server.'
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := env.load<Config>(config_file)? conf := vconf.load<Config>(default_path: config_file)?
server(conf)? server(conf)?
} }

View File

@ -0,0 +1,5 @@
Module {
dependencies: [
'https://git.rustybever.be/vieter/vconf'
]
}