diff --git a/.woodpecker/.test.yml b/.woodpecker/.test.yml deleted file mode 100644 index ec559c8..0000000 --- a/.woodpecker/.test.yml +++ /dev/null @@ -1,15 +0,0 @@ -matrix: - PLATFORM: - - linux/amd64 - - linux/arm64 - -platform: ${PLATFORM} - -pipeline: - test: - image: 'chewingbever/vlang:latest' - pull: true - commands: - - make test - when: - event: push diff --git a/Makefile b/Makefile index 9421fb6..76ab7b5 100644 --- a/Makefile +++ b/Makefile @@ -60,10 +60,6 @@ fmt: vet: $(V) vet -W $(SRC_DIR) -.PHONY: test -test: - $(V) test $(SRC_DIR) - # Build & patch the V compiler .PHONY: v v: v/v diff --git a/src/cron/cli.v b/src/cron/cli.v deleted file mode 100644 index 8e6b0f1..0000000 --- a/src/cron/cli.v +++ /dev/null @@ -1,27 +0,0 @@ -module cron - -import cli -import env - -struct Config { -pub: - log_level string = 'WARN' - log_file string = 'vieter.log' - api_key string - address string - base_image string = 'archlinux:base-devel' -} - -// cmd returns the cli module that handles the cron daemon. -pub fn cmd() cli.Command { - return cli.Command{ - name: 'cron' - 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_file) ? - - cron(conf) ? - } - } -} diff --git a/src/cron/cron.v b/src/cron/cron.v deleted file mode 100644 index 3ba9d0f..0000000 --- a/src/cron/cron.v +++ /dev/null @@ -1,18 +0,0 @@ -module cron - -import git -import time - -struct ScheduledBuild { - repo git.GitRepo - timestamp time.Time -} - -fn (r1 ScheduledBuild) < (r2 ScheduledBuild) bool { - return r1.timestamp < r2.timestamp -} - -// cron starts a cron daemon & starts periodically scheduling builds. -pub fn cron(conf Config) ? { - println('WIP') -} diff --git a/src/cron/expression.v b/src/cron/expression.v deleted file mode 100644 index 0a35541..0000000 --- a/src/cron/expression.v +++ /dev/null @@ -1,254 +0,0 @@ -module cron - -import time - -struct CronExpression { - minutes []int - hours []int - days []int - months []int -} - -// next calculates the earliest time this cron expression is valid. It will -// always pick a moment in the future, even if ref matches completely up to the -// minute. This function conciously does not take gap years into account. -pub fn (ce &CronExpression) next(ref time.Time) ?time.Time { - // If the given ref matches the next cron occurence up to the minute, it - // will return that value. Because we always want to return a value in the - // future, we artifically shift the ref 60 seconds to make sure we always - // match in the future. A shift of 60 seconds is enough because the cron - // expression does not allow for accuracy smaller than one minute. - sref := ref - - // For all of these values, the rule is the following: if their value is - // the length of their respective array in the CronExpression object, that - // means we've looped back around. This means that the "bigger" value has - // to be incremented by one. For example, if the minutes have looped - // around, that means that the hour has to be incremented as well. - mut minute_index := 0 - mut hour_index := 0 - mut day_index := 0 - mut month_index := 0 - - // This chain is the same logic multiple times, namely that if a "bigger" - // value loops around, then the smaller value will always reset as well. - // For example, if we're going to a new day, the hour & minute will always - // be their smallest value again. - for month_index < ce.months.len && sref.month > ce.months[month_index] { - month_index++ - } - - if month_index < ce.months.len && sref.month == ce.months[month_index] { - for day_index < ce.days.len && sref.day > ce.days[day_index] { - day_index++ - } - - if day_index < ce.days.len && ce.days[day_index] == sref.day { - for hour_index < ce.hours.len && sref.hour > ce.hours[hour_index] { - hour_index++ - } - - if hour_index < ce.hours.len && ce.hours[hour_index] == sref.hour { - // Minute is the only value where we explicitely make sure we - // can't match sref's value exactly. This is to ensure we only - // return values in the future. - for minute_index < ce.minutes.len && sref.minute >= ce.minutes[minute_index] { - minute_index++ - } - } - } - } - - // Here, we increment the "bigger" values by one if the smaller ones loop - // around. The order is important, as it allows a sort-of waterfall effect - // to occur which updates all values if required. - if minute_index == ce.minutes.len && hour_index < ce.hours.len { - hour_index += 1 - } - if hour_index == ce.hours.len && day_index < ce.days.len { - day_index += 1 - } - - if day_index == ce.days.len && month_index < ce.months.len { - month_index += 1 - } - - mut minute := ce.minutes[minute_index % ce.minutes.len] - mut hour := ce.hours[hour_index % ce.hours.len] - mut day := ce.days[day_index % ce.days.len] - - // Sometimes, we end up with a day that does not exist within the selected - // month, e.g. day 30 in February. When this occurs, we reset day back to - // the smallest value & loop over to the next month that does have this - // day. - if day > time.month_days[ce.months[month_index % ce.months.len] - 1] { - day = ce.days[0] - month_index += 1 - - for day > time.month_days[ce.months[month_index & ce.months.len] - 1] { - month_index += 1 - - // If for whatever reason the day value ends up being something - // that can't be scheduled in any month, we have to make sure we - // don't create an infinite loop. - if month_index == 2 * ce.months.len { - return error('No schedulable moment.') - } - } - } - - month := ce.months[month_index % ce.months.len] - mut year := sref.year - - // If the month loops over, we need to increment the year. - if month_index >= ce.months.len { - year++ - } - - return time.new_time(time.Time{ - year: year - month: month - day: day - minute: minute - hour: hour - }) -} - -fn (ce &CronExpression) next_from_now() ?time.Time { - return ce.next(time.now()) -} - -// parse_range parses a given string into a range of sorted integers, if -// possible. -fn parse_range(s string, min int, max int, mut bitv []bool) ? { - mut start := min - mut end := max - mut interval := 1 - - exps := s.split('/') - - if exps.len > 2 { - return error('Invalid expression.') - } - - if exps[0] != '*' { - dash_parts := exps[0].split('-') - - if dash_parts.len > 2 { - return error('Invalid expression.') - } - - start = dash_parts[0].int() - - // The builtin parsing functions return zero if the string can't be - // parsed into a number, so we have to explicitely check whether they - // actually entered zero or if it's an invalid number. - if start == 0 && dash_parts[0] != '0' { - return error('Invalid number.') - } - - // Check whether the start value is out of range - if start < min || start > max { - return error('Out of range.') - } - - if dash_parts.len == 2 { - end = dash_parts[1].int() - - if end == 0 && dash_parts[1] != '0' { - return error('Invalid number.') - } - - if end < start || end > max { - return error('Out of range.') - } - } - } - - if exps.len > 1 { - interval = exps[1].int() - - // interval being zero is always invalid, but we want to check why - // it's invalid for better error messages. - if interval == 0 { - if exps[1] != '0' { - return error('Invalid number.') - } else { - return error('Step size zero not allowed.') - } - } - - if interval > max - min { - return error('Step size too large.') - } - } - // Here, s solely consists of a number, so that's the only value we - // should return. - else if exps[0] != '*' && !exps[0].contains('-') { - bitv[start - min] = true - return - } - - for start <= end { - bitv[start - min] = true - start += interval - } -} - -fn bitv_to_ints(bitv []bool, min int) []int { - mut out := []int{} - - for i in 0 .. bitv.len { - if bitv[i] { - out << min + i - } - } - - return out -} - -fn parse_part(s string, min int, max int) ?[]int { - mut bitv := []bool{len: max - min + 1, init: false} - - for range in s.split(',') { - parse_range(range, min, max, mut bitv) ? - } - - return bitv_to_ints(bitv, min) -} - -// parse_expression parses an entire cron expression string into a -// CronExpression object, if possible. -fn parse_expression(exp string) ?CronExpression { - // The filter allows for multiple spaces between parts - mut parts := exp.split(' ').filter(it != '') - - if parts.len < 2 || parts.len > 4 { - return error('Expression must contain between 2 and 4 space-separated parts.') - } - - // For ease of use, we allow the user to only specify as many parts as they - // need. - for parts.len < 4 { - parts << '*' - } - - mut part_results := [][]int{} - - mins := [0, 0, 1, 1] - maxs := [59, 23, 31, 12] - - // This for loop allows us to more clearly propagate the error to the user. - for i, min in mins { - part_results << parse_part(parts[i], min, maxs[i]) or { - return error('An error occurred with part $i: $err.msg') - } - } - - return CronExpression{ - minutes: part_results[0] - hours: part_results[1] - days: part_results[2] - months: part_results[3] - } -} diff --git a/src/cron/expression_parse_test.v b/src/cron/expression_parse_test.v deleted file mode 100644 index 8f3ac38..0000000 --- a/src/cron/expression_parse_test.v +++ /dev/null @@ -1,98 +0,0 @@ -module cron - -// parse_range_error returns the returned error message. If the result is '', -// that means the function didn't error. -fn parse_range_error(s string, min int, max int) string { - mut bitv := []bool{len: max - min + 1, init: false} - - parse_range(s, min, max, mut bitv) or { return err.msg } - - return '' -} - -// =====parse_range===== -fn test_range_star_range() ? { - mut bitv := []bool{len: 6, init: false} - parse_range('*', 0, 5, mut bitv) ? - - assert bitv == [true, true, true, true, true, true] -} - -fn test_range_number() ? { - mut bitv := []bool{len: 6, init: false} - parse_range('4', 0, 5, mut bitv) ? - - assert bitv_to_ints(bitv, 0) == [4] -} - -fn test_range_number_too_large() ? { - assert parse_range_error('10', 0, 6) == 'Out of range.' -} - -fn test_range_number_too_small() ? { - assert parse_range_error('0', 2, 6) == 'Out of range.' -} - -fn test_range_number_invalid() ? { - assert parse_range_error('x', 0, 6) == 'Invalid number.' -} - -fn test_range_step_star_1() ? { - mut bitv := []bool{len: 21, init: false} - parse_range('*/4', 0, 20, mut bitv) ? - - assert bitv_to_ints(bitv, 0) == [0, 4, 8, 12, 16, 20] -} - -fn test_range_step_star_2() ? { - mut bitv := []bool{len: 8, init: false} - parse_range('*/3', 1, 8, mut bitv) ? - - assert bitv_to_ints(bitv, 1) == [1, 4, 7] -} - -fn test_range_step_star_too_large() ? { - assert parse_range_error('*/21', 0, 20) == 'Step size too large.' -} - -fn test_range_step_zero() ? { - assert parse_range_error('*/0', 0, 20) == 'Step size zero not allowed.' -} - -fn test_range_step_number() ? { - mut bitv := []bool{len: 21, init: false} - parse_range('5/4', 2, 22, mut bitv) ? - - assert bitv_to_ints(bitv, 2) == [5, 9, 13, 17, 21] -} - -fn test_range_step_number_too_large() ? { - assert parse_range_error('10/4', 0, 5) == 'Out of range.' -} - -fn test_range_step_number_too_small() ? { - assert parse_range_error('2/4', 5, 10) == 'Out of range.' -} - -fn test_range_dash() ? { - mut bitv := []bool{len: 10, init: false} - parse_range('4-8', 0, 9, mut bitv) ? - - assert bitv_to_ints(bitv, 0) == [4, 5, 6, 7, 8] -} - -fn test_range_dash_step() ? { - mut bitv := []bool{len: 10, init: false} - parse_range('4-8/2', 0, 9, mut bitv) ? - - assert bitv_to_ints(bitv, 0) == [4, 6, 8] -} - -// =====parse_part===== -fn test_part_single() ? { - assert parse_part('*', 0, 5) ? == [0, 1, 2, 3, 4, 5] -} - -fn test_part_multiple() ? { - assert parse_part('*/2,2/3', 1, 8) ? == [1, 2, 3, 5, 7, 8] -} diff --git a/src/cron/expression_test.v b/src/cron/expression_test.v deleted file mode 100644 index 0be9a64..0000000 --- a/src/cron/expression_test.v +++ /dev/null @@ -1,34 +0,0 @@ -module cron - -import time { parse } - -fn util_test_time(exp string, t1_str string, t2_str string) ? { - ce := parse_expression(exp) ? - t1 := parse(t1_str) ? - t2 := parse(t2_str) ? - - t3 := ce.next(t1) ? - - assert t2.year == t3.year - assert t2.month == t3.month - assert t2.day == t3.day - assert t2.hour == t3.hour - assert t2.minute == t3.minute -} - -fn test_next_simple() ? { - // Very simple - util_test_time('0 3', '2002-01-01 00:00:00', '2002-01-01 03:00:00') ? - - // Overlap to next day - util_test_time('0 3', '2002-01-01 03:00:00', '2002-01-02 03:00:00') ? - util_test_time('0 3', '2002-01-01 04:00:00', '2002-01-02 03:00:00') ? - - util_test_time('0 3/4', '2002-01-01 04:00:00', '2002-01-01 07:00:00') ? - - // Overlap to next month - util_test_time('0 3', '2002-11-31 04:00:00', '2002-12-01 03:00:00') ? - - // Overlap to next year - util_test_time('0 3', '2002-12-31 04:00:00', '2003-01-01 03:00:00') ? -} diff --git a/src/env/env.v b/src/env.v similarity index 100% rename from src/env/env.v rename to src/env.v diff --git a/src/main.v b/src/main.v index 37cabc3..3025389 100644 --- a/src/main.v +++ b/src/main.v @@ -5,7 +5,6 @@ import server import cli import build import git -import cron fn main() { mut app := cli.Command{ @@ -26,7 +25,6 @@ fn main() { server.cmd(), build.cmd(), git.cmd(), - cron.cmd(), ] } diff --git a/src/package/package.v b/src/package.v similarity index 100% rename from src/package/package.v rename to src/package.v diff --git a/src/response/response.v b/src/response.v similarity index 100% rename from src/response/response.v rename to src/response.v diff --git a/src/util/util.v b/src/util.v similarity index 99% rename from src/util/util.v rename to src/util.v index 228f584..49c9d22 100644 --- a/src/util/util.v +++ b/src/util.v @@ -44,7 +44,6 @@ pub fn reader_to_file(mut reader io.BufferedReader, length int, path string) ? { for to_write > 0 { // TODO don't just loop infinitely here bytes_written := file.write(buf[bytes_read - to_write..bytes_read]) or { continue } - // file.flush() to_write = to_write - bytes_written }