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
				
			
		
	
				
					
				
			
				
	
				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#244pull/248/head
						commit
						ae04fe63a7
					
				|  | @ -6,10 +6,19 @@ matrix: | |||
| platform: ${PLATFORM} | ||||
| 
 | ||||
| pipeline: | ||||
|   debug: | ||||
|   install-modules: | ||||
|     image: 'chewingbever/vlang:latest' | ||||
|     pull: true | ||||
|     commands: | ||||
|       - export VMODULES=$PWD/.vmodules | ||||
|       - 'cd src && v install' | ||||
|     when: | ||||
|       event: [pull_request] | ||||
| 
 | ||||
|   debug: | ||||
|     image: 'chewingbever/vlang:latest' | ||||
|     commands: | ||||
|       - export VMODULES=$PWD/.vmodules | ||||
|       - make | ||||
|     when: | ||||
|       event: [pull_request] | ||||
|  | @ -18,10 +27,10 @@ pipeline: | |||
| 
 | ||||
|   prod: | ||||
|     image: 'chewingbever/vlang:latest' | ||||
|     pull: true | ||||
|     environment: | ||||
|       - LDFLAGS=-lz -lbz2 -llzma -lexpat -lzstd -llz4 -lsqlite3 -static | ||||
|     commands: | ||||
|       - export VMODULES=$PWD/.vmodules | ||||
|       # Apparently this -D is *very* important | ||||
|       - CFLAGS='-DGC_THREADS=1' make prod | ||||
|       # Make sure the binary is actually statically built | ||||
|  |  | |||
							
								
								
									
										8
									
								
								PKGBUILD
								
								
								
								
							
							
						
						
									
										8
									
								
								PKGBUILD
								
								
								
								
							|  | @ -14,7 +14,15 @@ license=('AGPL3') | |||
| source=("$pkgname::git+https://git.rustybever.be/vieter/vieter#tag=${pkgver//_/-}") | ||||
| md5sums=('SKIP') | ||||
| 
 | ||||
| prepare() { | ||||
|     export VMODULES="$srcdir/.vmodules" | ||||
| 
 | ||||
|     cd "$pkgname/src" && v install | ||||
| } | ||||
| 
 | ||||
| build() { | ||||
|     export VMODULES="$srcdir/.vmodules" | ||||
| 
 | ||||
|     cd "$pkgname" | ||||
| 
 | ||||
|     make prod | ||||
|  |  | |||
|  | @ -22,7 +22,15 @@ pkgver() { | |||
|     git describe --long --tags | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g' | ||||
| } | ||||
| 
 | ||||
| prepare() { | ||||
|     export VMODULES="$srcdir/.vmodules" | ||||
| 
 | ||||
|     cd "$pkgname/src" && v install | ||||
| } | ||||
| 
 | ||||
| build() { | ||||
|     export VMODULES="$srcdir/.vmodules" | ||||
| 
 | ||||
|     cd "$pkgname" | ||||
| 
 | ||||
|     make prod | ||||
|  |  | |||
|  | @ -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