Compare commits

...

2 Commits

7 changed files with 138 additions and 24 deletions

View File

@ -10,6 +10,9 @@ pub:
api_key string api_key string
address string address string
base_image string = 'archlinux:base-devel' base_image string = 'archlinux:base-devel'
max_concurrent_builds int = 1
api_update_frequency int = 60
global_schedule string
} }
// cmd returns the cli module that handles the cron daemon. // cmd returns the cli module that handles the cron daemon.

View File

@ -2,17 +2,23 @@ module cron
import git import git
import time import time
import log
struct ScheduledBuild { import util
repo git.GitRepo import cron.daemon
timestamp time.Time
}
fn (r1 ScheduledBuild) < (r2 ScheduledBuild) bool {
return r1.timestamp < r2.timestamp
}
// cron starts a cron daemon & starts periodically scheduling builds. // cron starts a cron daemon & starts periodically scheduling builds.
pub fn cron(conf Config) ? { pub fn cron(conf Config) ? {
println('WIP') // Configure logger
log_level := log.level_from_tag(conf.log_level) or {
util.exit_with_message(1, 'Invalid log level. The allowed values are FATAL, ERROR, WARN, INFO & DEBUG.')
}
mut logger := log.Log{
level: log_level
}
logger.set_full_logpath(conf.log_file)
logger.log_to_console_too()
d := daemon.init(conf)
} }

View File

@ -0,0 +1,54 @@
module daemon
import git
import time
import log
import datatypes
struct ScheduledBuild {
repo git.GitRepo
timestamp time.Time
}
fn (r1 ScheduledBuild) < (r2 ScheduledBuild) bool {
return r1.timestamp < r2.timestamp
}
pub struct Daemon {
mut:
conf Config
// Repos currently loaded from API.
repos_map map[string]git.GitRepo
// At what point to update the list of repositories.
api_update_timestamp time.Time
queue datatypes.MinHeap<ScheduledBuild>
// Which builds are currently running
builds []git.GitRepo
// Atomic variables used to detect when a build has finished; length is the
// same as builds
atomics []u64
logger shared log.Log
}
// init
pub fn init(conf Config) Daemon {
return Daemon{
conf: conf
atomics: [conf.max_concurrent_builds]u64{}
}
}
fn (mut d Daemon) run() ? {
d.renew_repos() ?
d.renew_queue() ?
}
fn (mut d Daemon) renew_repos() ? {
mut new_repos := git.get_repos(d.conf.address, d.conf.api_key) ?
d.repos_map = new_repos.move()
}
fn (mut d Daemon) renew_queue() ? {
}

View File

@ -0,0 +1,35 @@
module daemon
import log
// log reate a log message with the given level
pub fn (mut d Daemon) log(msg &string, level log.Level) {
lock d.logger {
d.logger.send_output(msg, level)
}
}
// lfatal create a log message with the fatal level
pub fn (mut d Daemon) lfatal(msg &string) {
d.log(msg, log.Level.fatal)
}
// lerror create a log message with the error level
pub fn (mut d Daemon) lerror(msg &string) {
d.log(msg, log.Level.error)
}
// lwarn create a log message with the warn level
pub fn (mut d Daemon) lwarn(msg &string) {
d.log(msg, log.Level.warn)
}
// linfo create a log message with the info level
pub fn (mut d Daemon) linfo(msg &string) {
d.log(msg, log.Level.info)
}
// ldebug create a log message with the debug level
pub fn (mut d Daemon) ldebug(msg &string) {
d.log(msg, log.Level.debug)
}

View File

@ -241,7 +241,7 @@ fn parse_expression(exp string) ?CronExpression {
// This for loop allows us to more clearly propagate the error to the user. // This for loop allows us to more clearly propagate the error to the user.
for i, min in mins { for i, min in mins {
part_results << parse_part(parts[i], min, maxs[i]) or { part_results << parse_part(parts[i], min, maxs[i]) or {
return error('An error occurred with part $i: $err.msg') return error('An error occurred with part $i: $err.msg()')
} }
} }

32
src/env/env.v vendored
View File

@ -55,28 +55,42 @@ pub fn load<T>(path string) ?T {
$for field in T.fields { $for field in T.fields {
s := doc.value(field.name) s := doc.value(field.name)
// We currently only support strings if s !is toml.Null {
if s.type_name() == 'string' { $if field.typ is string {
res.$(field.name) = s.string() res.$(field.name) = s.string()
}$else $if field.typ is int {
res.$(field.name) = s.int()
}
} }
} }
} }
$for field in T.fields { $for field in T.fields {
$if field.typ is string {
env_value := get_env_var(field.name) ? env_value := get_env_var(field.name) ?
// The value of the env var will always be chosen over the config // The value of an env var will always take precedence over the toml
// file // file.
if env_value != '' { if env_value != '' {
$if field.typ is string {
res.$(field.name) = env_value res.$(field.name) = env_value
} $else $if field.typ is int {
res.$(field.name) = env_value.int()
} }
// If there's no value from the toml file either, we try to find a }
// default value
else if res.$(field.name) == '' { // 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 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 return res
} }

View File

@ -14,6 +14,8 @@ pub mut:
arch []string arch []string
// Which repo the builder should publish packages to // Which repo the builder should publish packages to
repo string repo string
// Cron schedule describing how frequently to build the repo.
schedule string
} }
// patch_from_params patches a GitRepo from a map[string]string, usually // patch_from_params patches a GitRepo from a map[string]string, usually