forked from vieter-v/vieter
				
			refactor: migrated env code to own external module
							parent
							
								
									4866cfa635
								
							
						
					
					
						commit
						44696fc11b
					
				| 
						 | 
				
			
			@ -41,6 +41,10 @@ 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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
module git
 | 
			
		||||
 | 
			
		||||
import cli
 | 
			
		||||
import env
 | 
			
		||||
import vieter.vconf
 | 
			
		||||
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 := env.load<Config>(config_file)?
 | 
			
		||||
					conf := vconf.load<Config>(default_path: 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 := env.load<Config>(config_file)?
 | 
			
		||||
					conf := vconf.load<Config>(default_path: 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 := env.load<Config>(config_file)?
 | 
			
		||||
					conf := vconf.load<Config>(default_path: 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 := env.load<Config>(config_file)?
 | 
			
		||||
					conf := vconf.load<Config>(default_path: 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 := env.load<Config>(config_file)?
 | 
			
		||||
					conf := vconf.load<Config>(default_path: 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 := env.load<Config>(config_file)?
 | 
			
		||||
					conf := vconf.load<Config>(default_path: config_file)?
 | 
			
		||||
 | 
			
		||||
					build(conf, cmd.args[0].int())?
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
module logs
 | 
			
		||||
 | 
			
		||||
import cli
 | 
			
		||||
import env
 | 
			
		||||
import vieter.vconf
 | 
			
		||||
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 := env.load<Config>(config_file)?
 | 
			
		||||
					conf := vconf.load<Config>(default_path: 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 := env.load<Config>(config_file)?
 | 
			
		||||
					conf := vconf.load<Config>(default_path: 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 := env.load<Config>(config_file)?
 | 
			
		||||
					conf := vconf.load<Config>(default_path: config_file)?
 | 
			
		||||
 | 
			
		||||
					id := cmd.args[0].int()
 | 
			
		||||
					content(conf, id)?
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
module cron
 | 
			
		||||
 | 
			
		||||
import cli
 | 
			
		||||
import env
 | 
			
		||||
import vieter.vconf
 | 
			
		||||
 | 
			
		||||
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 := env.load<Config>(config_file)?
 | 
			
		||||
			conf := vconf.load<Config>(default_path: config_file)?
 | 
			
		||||
 | 
			
		||||
			cron(conf)?
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
module server
 | 
			
		||||
 | 
			
		||||
import cli
 | 
			
		||||
import env
 | 
			
		||||
import vieter.vconf
 | 
			
		||||
 | 
			
		||||
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 := env.load<Config>(config_file)?
 | 
			
		||||
			conf := vconf.load<Config>(default_path: config_file)?
 | 
			
		||||
 | 
			
		||||
			server(conf)?
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue