forked from vieter-v/vieter
Compare commits
11 Commits
febefe26d1
...
546d79ed2e
| Author | SHA1 | Date |
|---|---|---|
|
|
546d79ed2e | |
|
|
a374924ea1 | |
|
|
74baccb507 | |
|
|
596da100b6 | |
|
|
0d5704ba15 | |
|
|
cdb88e1620 | |
|
|
768da5b790 | |
|
|
bd4bb9a9fb | |
|
|
c0b739035b | |
|
|
7f6e9e636c | |
|
|
96d0c2f1eb |
10
CHANGELOG.md
10
CHANGELOG.md
|
|
@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased](https://git.rustybever.be/vieter/vieter/src/branch/dev)
|
||||
|
||||
### Added
|
||||
|
||||
* Database migrations
|
||||
* Query parameters for GitRepo API to filter responses
|
||||
* Respective CLI flags for new GitRepo API parameters
|
||||
|
||||
### Changed
|
||||
|
||||
* Refactor of main types into `models` module
|
||||
|
||||
## [0.3.0-alpha.2](https://git.rustybever.be/vieter/vieter/src/tag/0.3.0-alpha.2)
|
||||
|
||||
### Added
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import models { GitRepo, GitRepoFilter }
|
|||
import net.http { Method }
|
||||
import response { Response }
|
||||
|
||||
// get_git_repos returns the current list of repos.
|
||||
// get_git_repos returns a list of GitRepo's, given a filter object.
|
||||
pub fn (c &Client) get_git_repos(filter GitRepoFilter) ?[]GitRepo {
|
||||
params := models.params_from(filter)
|
||||
data := c.send_request<[]GitRepo>(Method.get, '/api/repos', params)?
|
||||
|
|
@ -12,6 +12,27 @@ pub fn (c &Client) get_git_repos(filter GitRepoFilter) ?[]GitRepo {
|
|||
return data.data
|
||||
}
|
||||
|
||||
// get_all_git_repos retrieves *all* GitRepo's from the API using the default
|
||||
// limit.
|
||||
pub fn (c &Client) get_all_git_repos() ?[]GitRepo {
|
||||
mut repos := []GitRepo{}
|
||||
mut offset := u64(0)
|
||||
|
||||
for {
|
||||
sub_repos := c.get_git_repos(offset: offset)?
|
||||
|
||||
if sub_repos.len == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
repos << sub_repos
|
||||
|
||||
offset += u64(sub_repos.len)
|
||||
}
|
||||
|
||||
return repos
|
||||
}
|
||||
|
||||
// get_git_repo returns the repo for a specific ID.
|
||||
pub fn (c &Client) get_git_repo(id int) ?GitRepo {
|
||||
data := c.send_request<GitRepo>(Method.get, '/api/repos/$id', {})?
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
module schedule
|
||||
|
||||
import cli
|
||||
import cron.expression { parse_expression }
|
||||
import time
|
||||
|
||||
// cmd returns the cli submodule for previewing a cron schedule.
|
||||
pub fn cmd() cli.Command {
|
||||
return cli.Command{
|
||||
name: 'schedule'
|
||||
usage: 'schedule'
|
||||
description: 'Preview the behavior of a cron schedule.'
|
||||
flags: [
|
||||
cli.Flag{
|
||||
name: 'count'
|
||||
description: 'How many scheduled times to show.'
|
||||
flag: cli.FlagType.int
|
||||
default_value: ['5']
|
||||
},
|
||||
]
|
||||
execute: fn (cmd cli.Command) ? {
|
||||
ce := parse_expression(cmd.args.join(' '))?
|
||||
count := cmd.flags.get_int('count')?
|
||||
|
||||
for t in ce.next_n(time.now(), count)? {
|
||||
println(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -178,7 +178,7 @@ fn (mut d Daemon) schedule_build(repo GitRepo) {
|
|||
fn (mut d Daemon) renew_repos() {
|
||||
d.linfo('Renewing repos...')
|
||||
|
||||
mut new_repos := d.client.get_git_repos() or {
|
||||
mut new_repos := d.client.get_all_git_repos() or {
|
||||
d.lerror('Failed to renew repos. Retrying in ${daemon.api_update_retry_timeout}s...')
|
||||
d.api_update_timestamp = time.now().add_seconds(daemon.api_update_retry_timeout)
|
||||
|
||||
|
|
|
|||
|
|
@ -121,6 +121,20 @@ pub fn (ce &CronExpression) next_from_now() ?time.Time {
|
|||
return ce.next(time.now())
|
||||
}
|
||||
|
||||
// next_n returns the n next occurences of the expression, given a starting
|
||||
// time.
|
||||
pub fn (ce &CronExpression) next_n(ref time.Time, n int) ?[]time.Time {
|
||||
mut times := []time.Time{cap: n}
|
||||
|
||||
times << ce.next(ref)?
|
||||
|
||||
for i in 1 .. n {
|
||||
times << ce.next(times[i - 1])?
|
||||
}
|
||||
|
||||
return times
|
||||
}
|
||||
|
||||
// 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) ? {
|
||||
|
|
|
|||
71
src/db/db.v
71
src/db/db.v
|
|
@ -1,22 +1,87 @@
|
|||
module db
|
||||
|
||||
import sqlite
|
||||
import models { BuildLog, GitRepo }
|
||||
import time
|
||||
|
||||
struct VieterDb {
|
||||
conn sqlite.DB
|
||||
}
|
||||
|
||||
struct MigrationVersion {
|
||||
id int [primary]
|
||||
version int
|
||||
}
|
||||
|
||||
const (
|
||||
migrations_up = [$embed_file('migrations/001-initial/up.sql')]
|
||||
migrations_down = [$embed_file('migrations/001-initial/down.sql')]
|
||||
)
|
||||
|
||||
// init initializes a database & adds the correct tables.
|
||||
pub fn init(db_path string) ?VieterDb {
|
||||
conn := sqlite.connect(db_path)?
|
||||
|
||||
sql conn {
|
||||
create table GitRepo
|
||||
create table BuildLog
|
||||
create table MigrationVersion
|
||||
}
|
||||
|
||||
cur_version := sql conn {
|
||||
select from MigrationVersion limit 1
|
||||
}
|
||||
|
||||
// If there's no row yet, we add it here
|
||||
if cur_version == MigrationVersion{} {
|
||||
sql conn {
|
||||
insert cur_version into MigrationVersion
|
||||
}
|
||||
}
|
||||
|
||||
// Apply each migration in order
|
||||
for i in cur_version.version .. db.migrations_up.len {
|
||||
migration := db.migrations_up[i].to_string()
|
||||
|
||||
version_num := i + 1
|
||||
|
||||
// vfmt does not like these dots
|
||||
println('Applying migration $version_num' + '...')
|
||||
|
||||
// The sqlite library seems to not like it when multiple statements are
|
||||
// passed in a single exec. Therefore, we split them & run them all
|
||||
// separately.
|
||||
for part in migration.split(';').map(it.trim_space()).filter(it != '') {
|
||||
res := conn.exec_none(part)
|
||||
|
||||
if res != sqlite.sqlite_done {
|
||||
return error('An error occurred while applying migration $version_num')
|
||||
}
|
||||
}
|
||||
|
||||
// The where clause doesn't really matter, as there will always only be
|
||||
// one entry anyways.
|
||||
sql conn {
|
||||
update MigrationVersion set version = version_num where id > 0
|
||||
}
|
||||
}
|
||||
|
||||
return VieterDb{
|
||||
conn: conn
|
||||
}
|
||||
}
|
||||
|
||||
pub fn row_into<T>(row sqlite.Row) T {
|
||||
mut i := 0
|
||||
mut out := T{}
|
||||
|
||||
$for field in T.fields {
|
||||
$if field.typ is string {
|
||||
out.$(field.name) = row.vals[i]
|
||||
} $else $if field.typ is int {
|
||||
out.$(field.name) = row.vals[i].int()
|
||||
} $else $if field.typ is time.Time {
|
||||
out.$(field.name) = time.unix(row.vals[i].int())
|
||||
}
|
||||
|
||||
i += 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,38 @@
|
|||
module db
|
||||
|
||||
import models { BuildLog }
|
||||
import models { BuildLog, BuildLogFilter }
|
||||
import time
|
||||
|
||||
// get_build_logs returns all BuildLog's in the database.
|
||||
pub fn (db &VieterDb) get_build_logs() []BuildLog {
|
||||
res := sql db.conn {
|
||||
select from BuildLog order by id
|
||||
pub fn (db &VieterDb) get_build_logs(filter BuildLogFilter) []BuildLog {
|
||||
mut where_parts := []string{}
|
||||
|
||||
if filter.repo != 0 {
|
||||
where_parts << 'repo_id == $filter.repo'
|
||||
}
|
||||
|
||||
if filter.before != time.Time{} {
|
||||
where_parts << 'start_time < $filter.before.unix_time()'
|
||||
}
|
||||
|
||||
if filter.after != time.Time{} {
|
||||
where_parts << 'start_time < $filter.after.unix_time()'
|
||||
}
|
||||
|
||||
mut where_str := ''
|
||||
|
||||
if where_parts.len > 0 {
|
||||
where_str = ' where ' + where_parts.map('($it)').join(' and ')
|
||||
}
|
||||
|
||||
query := 'select from BuildLog' + where_str
|
||||
rows, _ := db.conn.exec(query)
|
||||
res := rows.map(row_into<BuildLog>(it))
|
||||
|
||||
// res := sql db.conn {
|
||||
// select from BuildLog where filter.repo == 0 || repo_id == filter.repo order by id
|
||||
// }
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
DROP TABLE IF EXISTS BuildLog;
|
||||
DROP TABLE IF EXISTS GitRepoArch;
|
||||
DROP TABLE IF EXISTS GitRepo;
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
CREATE TABLE IF NOT EXISTS GitRepo (
|
||||
id INTEGER PRIMARY KEY,
|
||||
url TEXT NOT NULL,
|
||||
branch TEXT NOT NULL,
|
||||
repo TEXT NOT NULL,
|
||||
schedule TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS GitRepoArch (
|
||||
id INTEGER PRIMARY KEY,
|
||||
repo_id INTEGER NOT NULL,
|
||||
value TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS BuildLog (
|
||||
id INTEGER PRIMARY KEY,
|
||||
repo_id INTEGER NOT NULL,
|
||||
start_time INTEGER NOT NULL,
|
||||
end_time iNTEGER NOT NULL,
|
||||
arch TEXT NOT NULL,
|
||||
exit_code INTEGER NOT NULL
|
||||
);
|
||||
|
|
@ -5,6 +5,7 @@ import server
|
|||
import cli
|
||||
import console.git
|
||||
import console.logs
|
||||
import console.schedule
|
||||
import cron
|
||||
|
||||
fn main() {
|
||||
|
|
@ -27,6 +28,7 @@ fn main() {
|
|||
git.cmd(),
|
||||
cron.cmd(),
|
||||
logs.cmd(),
|
||||
schedule.cmd(),
|
||||
]
|
||||
}
|
||||
app.setup()
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ module models
|
|||
import time
|
||||
|
||||
pub struct BuildLog {
|
||||
pub:
|
||||
pub mut:
|
||||
id int [primary; sql: serial]
|
||||
repo_id int [nonull]
|
||||
start_time time.Time [nonull]
|
||||
|
|
@ -26,3 +26,16 @@ pub fn (bl &BuildLog) str() string {
|
|||
|
||||
return str
|
||||
}
|
||||
|
||||
[params]
|
||||
pub struct BuildLogFilter {
|
||||
pub mut:
|
||||
limit u64 = 25
|
||||
offset u64
|
||||
repo int
|
||||
before time.Time
|
||||
after time.Time
|
||||
exit_codes_whitelist []u8
|
||||
exit_codes_blacklist []u8
|
||||
arch string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,9 @@ pub fn server(conf Config) ? {
|
|||
}
|
||||
|
||||
db_file := os.join_path_single(conf.data_dir, server.db_file_name)
|
||||
db := db.init(db_file) or { util.exit_with_message(1, 'Failed to initialize database.') }
|
||||
db := db.init(db_file) or {
|
||||
util.exit_with_message(1, 'Failed to initialize database: $err.msg()')
|
||||
}
|
||||
|
||||
web.run(&App{
|
||||
logger: logger
|
||||
|
|
|
|||
Loading…
Reference in New Issue