Compare commits

..

1 Commits

Author SHA1 Message Date
Jef Roosens 97d000f18f
feat(server): no longer calculate md5 hashes for packages
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-06-15 22:15:11 +02:00
32 changed files with 353 additions and 383 deletions

View File

@ -7,16 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased](https://git.rustybever.be/vieter/vieter/src/branch/dev) ## [Unreleased](https://git.rustybever.be/vieter/vieter/src/branch/dev)
### Added
* Server port can now be configured
### Changed
* Moved all API routes under `/v1` namespace
* Renamed `vieter repos` to `vieter targets`
* Renamed `/api/v1/repos` namespace to `/api/v1/targets`
### Removed ### Removed
* md5 hashes are no longer calculated for packages * md5 hashes are no longer calculated for packages

View File

@ -1,4 +1,4 @@
# Targets # Git Repositories
<aside class="notice"> <aside class="notice">
@ -6,14 +6,15 @@ All routes in this section require authentication.
</aside> </aside>
Endpoints for interacting with the list of targets stored on the server. Endpoints for interacting with the list of Git repositories stored on the
server.
## List targets ## List repos
```shell ```shell
curl \ curl \
-H 'X-Api-Key: secret' \ -H 'X-Api-Key: secret' \
https://example.com/api/v1/targets?offset=10&limit=20 https://example.com/api/repos?offset=10&limit=20
``` ```
> JSON output format > JSON output format
@ -31,7 +32,7 @@ curl \
"arch": [ "arch": [
{ {
"id": 1, "id": 1,
"target_id": 1, "repo_id": 1,
"value": "x86_64" "value": "x86_64"
} }
] ]
@ -40,11 +41,11 @@ curl \
} }
``` ```
Retrieve a list of targets. Retrieve a list of Git repositories.
### HTTP Request ### HTTP Request
`GET /api/v1/targets` `GET /api/repos`
### Query Parameters ### Query Parameters
@ -52,14 +53,14 @@ Parameter | Description
--------- | ----------- --------- | -----------
limit | Maximum amount of results to return. limit | Maximum amount of results to return.
offset | Offset of results. offset | Offset of results.
repo | Limit results to targets that publish to the given repo. repo | Limit results to repositories that publish to the given repo.
## Get specific target ## Get a repo
```shell ```shell
curl \ curl \
-H 'X-Api-Key: secret' \ -H 'X-Api-Key: secret' \
https://example.com/api/v1/targets/1 https://example.com/api/repos/15
``` ```
> JSON output format > JSON output format
@ -76,7 +77,7 @@ curl \
"arch": [ "arch": [
{ {
"id": 1, "id": 1,
"target_id": 1, "repo_id": 1,
"value": "x86_64" "value": "x86_64"
} }
] ]
@ -84,25 +85,25 @@ curl \
} }
``` ```
Get info about a specific target. Get info about a specific Git repository.
### HTTP Request ### HTTP Request
`GET /api/v1/targets/:id` `GET /api/repos/:id`
### URL Parameters ### URL Parameters
Parameter | Description Parameter | Description
--------- | ----------- --------- | -----------
id | id of requested target id | ID of requested repo
## Create a new target ## Create a new repo
Create a new target with the given data. Create a new Git repository with the given data.
### HTTP Request ### HTTP Request
`POST /api/v1/targets` `POST /api/repos`
### Query Parameters ### Query Parameters
@ -114,19 +115,19 @@ repo | Vieter repository to publish built packages to.
schedule | Cron build schedule (syntax explained [here](https://rustybever.be/docs/vieter/usage/builds/schedule/)) schedule | Cron build schedule (syntax explained [here](https://rustybever.be/docs/vieter/usage/builds/schedule/))
arch | Comma-separated list of architectures to build package on. arch | Comma-separated list of architectures to build package on.
## Modify a target ## Modify a repo
Modify the data of an existing target. Modify the data of an existing Git repository.
### HTTP Request ### HTTP Request
`PATCH /api/v1/targets/:id` `PATCH /api/repos/:id`
### URL Parameters ### URL Parameters
Parameter | Description Parameter | Description
--------- | ----------- --------- | -----------
id | id of target to modify id | ID of requested repo
### Query Parameters ### Query Parameters
@ -138,16 +139,16 @@ repo | Vieter repository to publish built packages to.
schedule | Cron build schedule schedule | Cron build schedule
arch | Comma-separated list of architectures to build package on. arch | Comma-separated list of architectures to build package on.
## Remove a target ## Remove a repo
Remove a target from the server. Remove a Git repository from the server.
### HTTP Request ### HTTP Request
`DELETE /api/v1/targets/:id` `DELETE /api/repos/:id`
### URL Parameters ### URL Parameters
Parameter | Description Parameter | Description
--------- | ----------- --------- | -----------
id | id of target to remove id | ID of repo to remove

View File

@ -13,7 +13,7 @@ Endpoints for interacting with stored build logs.
```shell ```shell
curl \ curl \
-H 'X-Api-Key: secret' \ -H 'X-Api-Key: secret' \
https://example.com/api/v1/logs?offset=10&limit=20 https://example.com/api/logs?offset=10&limit=20
``` ```
> JSON output format > JSON output format
@ -24,7 +24,7 @@ curl \
"data": [ "data": [
{ {
"id": 1, "id": 1,
"target_id": 3, "repo_id": 3,
"start_time": 1652008554, "start_time": 1652008554,
"end_time": 1652008559, "end_time": 1652008559,
"arch": "x86_64", "arch": "x86_64",
@ -38,7 +38,7 @@ Retrieve a list of build logs.
### HTTP Request ### HTTP Request
`GET /api/v1/logs` `GET /api/logs`
### Query Parameters ### Query Parameters
@ -46,7 +46,7 @@ Parameter | Description
--------- | ----------- --------- | -----------
limit | Maximum amount of results to return. limit | Maximum amount of results to return.
offset | Offset of results. offset | Offset of results.
target | Only return builds for this target id. repo | Only return builds published to this repository.
before | Only return logs started before this time (UTC epoch) before | Only return logs started before this time (UTC epoch)
after | Only return logs started after this time (UTC epoch) after | Only return logs started after this time (UTC epoch)
arch | Only return logs built on this architecture arch | Only return logs built on this architecture
@ -58,7 +58,7 @@ exit_codes | Comma-separated list of exit codes to limit result to; using `!` as
```shell ```shell
curl \ curl \
-H 'X-Api-Key: secret' \ -H 'X-Api-Key: secret' \
https://example.com/api/v1/logs/1 https://example.com/api/logs/15
``` ```
> JSON output format > JSON output format
@ -68,7 +68,7 @@ curl \
"message": "", "message": "",
"data": { "data": {
"id": 1, "id": 1,
"target_id": 3, "repo_id": 3,
"start_time": 1652008554, "start_time": 1652008554,
"end_time": 1652008559, "end_time": 1652008559,
"arch": "x86_64", "arch": "x86_64",
@ -81,7 +81,7 @@ Retrieve info about a specific build log.
### HTTP Request ### HTTP Request
`GET /api/v1/logs/:id` `GET /api/logs/:id`
### URL Parameters ### URL Parameters
@ -94,7 +94,7 @@ id | ID of requested log
```shell ```shell
curl \ curl \
-H 'X-Api-Key: secret' \ -H 'X-Api-Key: secret' \
https://example.com/api/v1/logs/15/content https://example.com/api/logs/15/content
``` ```
Retrieve the contents of a build log. The response is the build log in Retrieve the contents of a build log. The response is the build log in
@ -102,7 +102,7 @@ plaintext.
### HTTP Request ### HTTP Request
`GET /api/v1/logs/:id/content` `GET /api/logs/:id/content`
### URL Parameters ### URL Parameters
@ -123,17 +123,17 @@ Publish a new build log to the server.
### HTTP Request ### HTTP Request
`POST /api/v1/logs` `POST /api/logs`
### Query parameters ### Query parameters
Parameter | Description Parameter | Description
--------- | ----------- --------- | -----------
id | ID of requested log
startTime | Start time of the build (UTC epoch) startTime | Start time of the build (UTC epoch)
endTime | End time of the build (UTC epoch) endTime | End time of the build (UTC epoch)
arch | Architecture on which the build was done arch | Architecture on which the build was done
exitCode | Exit code of the build container exitCode | Exit code of the build container
target | id of target this build is for
### Request body ### Request body

View File

@ -9,7 +9,7 @@ toc_footers:
includes: includes:
- repository - repository
- targets - git
- logs - logs
search: true search: true

View File

@ -26,7 +26,7 @@ secrets file.
## Commands ## Commands
The first argument passed to Vieter determines which command you wish to use. The first argument passed to Vieter determines which command you wish to use.
Each of these can contain subcommands (e.g. `vieter targets list`), but all Each of these can contain subcommands (e.g. `vieter repos list`), but all
subcommands will use the same configuration. Below you can find the subcommands will use the same configuration. Below you can find the
configuration variable required for each command. configuration variable required for each command.
@ -45,8 +45,7 @@ configuration variable required for each command.
This prevents the server from being confused when an `any` package is This prevents the server from being confused when an `any` package is
published as the very first package for a repository. published as the very first package for a repository.
* Git repositories added without an `arch` value use this value instead. * Git repositories added without an `arch` value use this value instead.
* `port`: HTTP port to run on
* Default: `8000`
### `vieter cron` ### `vieter cron`
@ -89,11 +88,11 @@ configuration variable required for each command.
* `api_key`: the API key to use when authenticating requests. * `api_key`: the API key to use when authenticating requests.
* `address`: Base URL of your Vieter instance, e.g. https://example.com * `address`: Base URL of your Vieter instance, e.g. https://example.com
### `vieter targets` ### `vieter repos`
* `api_key`: the API key to use when authenticating requests. * `api_key`: the API key to use when authenticating requests.
* `address`: Base URL of your Vieter instance, e.g. https://example.com * `address`: Base URL of your Vieter instance, e.g. https://example.com
* `base_image`: image to use when building a package using `vieter targets * `base_image`: image to use when building a package using `vieter repos
build`. build`.
* Default: `archlinux:base-devel` * Default: `archlinux:base-devel`

View File

@ -16,11 +16,11 @@ info to the system. The Vieter repository server exposes an HTTP API for this
info). For ease of use, the Vieter binary contains a CLI interface for info). For ease of use, the Vieter binary contains a CLI interface for
interacting with this API (see [Configuration](/configuration) for interacting with this API (see [Configuration](/configuration) for
configuration details). The [man configuration details). The [man
pages](https://rustybever.be/man/vieter/vieter-targets.1.html) describe this in pages](https://rustybever.be/man/vieter/vieter-repos.1.html) describe this in
greater detail, but the basic usage is as follows: greater detail, but the basic usage is as follows:
``` ```
vieter targets add some-url some-branch some-repository vieter repos add some-url some-branch some-repository
``` ```
Here, `some-url` is the URL of the Git repository containing the PKGBUILD. This Here, `some-url` is the URL of the Git repository containing the PKGBUILD. This
@ -37,7 +37,7 @@ should be published.
The above command intentionally leaves out a few parameters to make the CLI The above command intentionally leaves out a few parameters to make the CLI
more useable. For information on how to modify all parameters using the CLI, more useable. For information on how to modify all parameters using the CLI,
see see
[vieter-targets-edit(1)](https://rustybever.be/man/vieter/vieter-targets-edit.1.html). [vieter-repos-edit(1)](https://rustybever.be/man/vieter/vieter-repos-edit.1.html).
## Reading logs ## Reading logs

View File

@ -6,7 +6,7 @@ import time
import os import os
import strings import strings
import util import util
import models { Target } import models { GitRepo }
const ( const (
container_build_dir = '/build' container_build_dir = '/build'
@ -91,9 +91,9 @@ pub:
} }
// build_repo builds, packages & publishes a given Arch package based on the // build_repo builds, packages & publishes a given Arch package based on the
// provided target. The base image ID should be of an image previously created // provided GitRepo. The base image ID should be of an image previously created
// by create_build_image. It returns the logs of the container. // by create_build_image. It returns the logs of the container.
pub fn build_repo(address string, api_key string, base_image_id string, repo &Target) ?BuildResult { pub fn build_repo(address string, api_key string, base_image_id string, repo &GitRepo) ?BuildResult {
mut dd := docker.new_conn()? mut dd := docker.new_conn()?
defer { defer {

View File

@ -1,6 +1,6 @@
module build module build
import models { Target } import models { GitRepo }
// escape_shell_string escapes any characters that could be interpreted // escape_shell_string escapes any characters that could be interpreted
// incorrectly by a shell. The resulting value should be safe to use inside an // incorrectly by a shell. The resulting value should be safe to use inside an
@ -22,8 +22,8 @@ pub fn echo_commands(cmds []string) []string {
return out return out
} }
// create_build_script generates a shell script that builds a given Target. // create_build_script generates a shell script that builds a given GitRepo.
fn create_build_script(address string, repo &Target, build_arch string) string { fn create_build_script(address string, repo &GitRepo, build_arch string) string {
repo_url := '$address/$repo.repo' repo_url := '$address/$repo.repo'
commands := echo_commands([ commands := echo_commands([

View File

@ -1,15 +1,15 @@
module build module build
import models { Target } import models { GitRepo }
fn test_create_build_script() { fn test_create_build_script() {
target := Target{ repo := GitRepo{
id: 1 id: 1
url: 'https://examplerepo.com' url: 'https://examplerepo.com'
branch: 'main' branch: 'main'
repo: 'vieter' repo: 'vieter'
} }
build_script := create_build_script('https://example.com', target, 'x86_64') build_script := create_build_script('https://example.com', repo, 'x86_64')
expected := $embed_file('build_script.sh') expected := $embed_file('build_script.sh')
assert build_script == expected.to_string().trim_space() assert build_script == expected.to_string().trim_space()

73
src/client/git.v 100644
View File

@ -0,0 +1,73 @@
module client
import models { GitRepo, GitRepoFilter }
import net.http { Method }
import response { Response }
// 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)?
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', {})?
return data.data
}
// add_git_repo adds a new repo to the server.
pub fn (c &Client) add_git_repo(url string, branch string, repo string, arch []string) ?Response<string> {
mut params := {
'url': url
'branch': branch
'repo': repo
}
if arch.len > 0 {
params['arch'] = arch.join(',')
}
data := c.send_request<string>(Method.post, '/api/repos', params)?
return data
}
// remove_git_repo removes the repo with the given ID from the server.
pub fn (c &Client) remove_git_repo(id int) ?Response<string> {
data := c.send_request<string>(Method.delete, '/api/repos/$id', {})?
return data
}
// patch_git_repo sends a PATCH request to the given repo with the params as
// payload.
pub fn (c &Client) patch_git_repo(id int, params map[string]string) ?Response<string> {
data := c.send_request<string>(Method.patch, '/api/repos/$id', params)?
return data
}

View File

@ -8,47 +8,47 @@ import time
// get_build_logs returns all build logs. // get_build_logs returns all build logs.
pub fn (c &Client) get_build_logs(filter BuildLogFilter) ?Response<[]BuildLog> { pub fn (c &Client) get_build_logs(filter BuildLogFilter) ?Response<[]BuildLog> {
params := models.params_from(filter) params := models.params_from(filter)
data := c.send_request<[]BuildLog>(Method.get, '/api/v1/logs', params)? data := c.send_request<[]BuildLog>(Method.get, '/api/logs', params)?
return data return data
} }
// get_build_logs_for_target returns all build logs for a given target. // get_build_logs_for_repo returns all build logs for a given repo.
pub fn (c &Client) get_build_logs_for_target(target_id int) ?Response<[]BuildLog> { pub fn (c &Client) get_build_logs_for_repo(repo_id int) ?Response<[]BuildLog> {
params := { params := {
'repo': target_id.str() 'repo': repo_id.str()
} }
data := c.send_request<[]BuildLog>(Method.get, '/api/v1/logs', params)? data := c.send_request<[]BuildLog>(Method.get, '/api/logs', params)?
return data return data
} }
// get_build_log returns a specific build log. // get_build_log returns a specific build log.
pub fn (c &Client) get_build_log(id int) ?Response<BuildLog> { pub fn (c &Client) get_build_log(id int) ?Response<BuildLog> {
data := c.send_request<BuildLog>(Method.get, '/api/v1/logs/$id', {})? data := c.send_request<BuildLog>(Method.get, '/api/logs/$id', {})?
return data return data
} }
// get_build_log_content returns the contents of the build log file. // get_build_log_content returns the contents of the build log file.
pub fn (c &Client) get_build_log_content(id int) ?string { pub fn (c &Client) get_build_log_content(id int) ?string {
data := c.send_request_raw_response(Method.get, '/api/v1/logs/$id/content', {}, '')? data := c.send_request_raw_response(Method.get, '/api/logs/$id/content', {}, '')?
return data return data
} }
// add_build_log adds a new build log to the server. // add_build_log adds a new build log to the server.
pub fn (c &Client) add_build_log(target_id int, start_time time.Time, end_time time.Time, arch string, exit_code int, content string) ?Response<string> { pub fn (c &Client) add_build_log(repo_id int, start_time time.Time, end_time time.Time, arch string, exit_code int, content string) ?Response<string> {
params := { params := {
'target': target_id.str() 'repo': repo_id.str()
'startTime': start_time.unix_time().str() 'startTime': start_time.unix_time().str()
'endTime': end_time.unix_time().str() 'endTime': end_time.unix_time().str()
'arch': arch 'arch': arch
'exitCode': exit_code.str() 'exitCode': exit_code.str()
} }
data := c.send_request_with_body<string>(Method.post, '/api/v1/logs', params, content)? data := c.send_request_with_body<string>(Method.post, '/api/logs', params, content)?
return data return data
} }

View File

@ -1,73 +0,0 @@
module client
import models { Target, TargetFilter }
import net.http { Method }
import response { Response }
// get_targets returns a list of targets, given a filter object.
pub fn (c &Client) get_targets(filter TargetFilter) ?[]Target {
params := models.params_from(filter)
data := c.send_request<[]Target>(Method.get, '/api/v1/targets', params)?
return data.data
}
// get_all_targets retrieves *all* targs from the API using the default
// limit.
pub fn (c &Client) get_all_targets() ?[]Target {
mut targets := []Target{}
mut offset := u64(0)
for {
sub_targets := c.get_targets(offset: offset)?
if sub_targets.len == 0 {
break
}
targets << sub_targets
offset += u64(sub_targets.len)
}
return targets
}
// get_target returns the target for a specific id.
pub fn (c &Client) get_target(id int) ?Target {
data := c.send_request<Target>(Method.get, '/api/v1/targets/$id', {})?
return data.data
}
// add_target adds a new target to the server.
pub fn (c &Client) add_target(url string, branch string, repo string, arch []string) ?Response<string> {
mut params := {
'url': url
'branch': branch
'repo': repo
}
if arch.len > 0 {
params['arch'] = arch.join(',')
}
data := c.send_request<string>(Method.post, '/api/v1/targets', params)?
return data
}
// remove_target removes the target with the given id from the server.
pub fn (c &Client) remove_target(id int) ?Response<string> {
data := c.send_request<string>(Method.delete, '/api/v1/targets/$id', {})?
return data
}
// patch_target sends a PATCH request to the given target with the params as
// payload.
pub fn (c &Client) patch_target(id int, params map[string]string) ?Response<string> {
data := c.send_request<string>(Method.patch, '/api/v1/targets/$id', params)?
return data
}

View File

@ -1,14 +1,14 @@
module targets module git
import client import client
import docker import docker
import os import os
import build import build
// build locally builds the target with the given id. // build builds every Git repo in the server's list.
fn build(conf Config, repo_id int) ? { fn build(conf Config, repo_id int) ? {
c := client.new(conf.address, conf.api_key) c := client.new(conf.address, conf.api_key)
repo := c.get_target(repo_id)? repo := c.get_git_repo(repo_id)?
build_arch := os.uname().machine build_arch := os.uname().machine

View File

@ -1,11 +1,11 @@
module targets module git
import cli import cli
import vieter.vconf import vieter.vconf
import cron.expression { parse_expression } import cron.expression { parse_expression }
import client import client
import console import console
import models { TargetFilter } import models { GitRepoFilter }
struct Config { struct Config {
address string [required] address string [required]
@ -16,12 +16,12 @@ struct Config {
// cmd returns the cli submodule that handles the repos API interaction // cmd returns the cli submodule that handles the repos API interaction
pub fn cmd() cli.Command { pub fn cmd() cli.Command {
return cli.Command{ return cli.Command{
name: 'targets' name: 'repos'
description: 'Interact with the targets API.' description: 'Interact with the repos API.'
commands: [ commands: [
cli.Command{ cli.Command{
name: 'list' name: 'list'
description: 'List the current targets.' description: 'List the current repos.'
flags: [ flags: [
cli.Flag{ cli.Flag{
name: 'limit' name: 'limit'
@ -35,15 +35,15 @@ pub fn cmd() cli.Command {
}, },
cli.Flag{ cli.Flag{
name: 'repo' name: 'repo'
description: 'Only return targets that publish to this repo.' description: 'Only return Git repos that publish to this repo.'
flag: cli.FlagType.string flag: cli.FlagType.string
}, },
] ]
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := vconf.load<Config>(prefix: 'VIETER_', default_path: config_file)? conf := vconf.load<Config>(default_path: config_file)?
mut filter := TargetFilter{} mut filter := GitRepoFilter{}
limit := cmd.flags.get_int('limit')? limit := cmd.flags.get_int('limit')?
if limit != 0 { if limit != 0 {
@ -67,10 +67,10 @@ pub fn cmd() cli.Command {
name: 'add' name: 'add'
required_args: 3 required_args: 3
usage: 'url branch repo' usage: 'url branch repo'
description: 'Add a new Git repository target.' description: 'Add a new repository.'
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := vconf.load<Config>(prefix: 'VIETER_', default_path: config_file)? conf := vconf.load<Config>(default_path: config_file)?
add(conf, cmd.args[0], cmd.args[1], cmd.args[2])? add(conf, cmd.args[0], cmd.args[1], cmd.args[2])?
} }
@ -79,10 +79,10 @@ pub fn cmd() cli.Command {
name: 'remove' name: 'remove'
required_args: 1 required_args: 1
usage: 'id' usage: 'id'
description: 'Remove a target that matches the given id.' description: 'Remove a repository that matches the given ID prefix.'
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := vconf.load<Config>(prefix: 'VIETER_', default_path: config_file)? conf := vconf.load<Config>(default_path: config_file)?
remove(conf, cmd.args[0])? remove(conf, cmd.args[0])?
} }
@ -91,10 +91,10 @@ pub fn cmd() cli.Command {
name: 'info' name: 'info'
required_args: 1 required_args: 1
usage: 'id' usage: 'id'
description: 'Show detailed information for the target matching the id.' description: 'Show detailed information for the repo matching the ID prefix.'
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := vconf.load<Config>(prefix: 'VIETER_', default_path: config_file)? conf := vconf.load<Config>(default_path: config_file)?
info(conf, cmd.args[0])? info(conf, cmd.args[0])?
} }
@ -103,7 +103,7 @@ pub fn cmd() cli.Command {
name: 'edit' name: 'edit'
required_args: 1 required_args: 1
usage: 'id' usage: 'id'
description: 'Edit the Git repository target that matches the given id.' description: 'Edit the repository that matches the given ID prefix.'
flags: [ flags: [
cli.Flag{ cli.Flag{
name: 'url' name: 'url'
@ -133,7 +133,7 @@ pub fn cmd() cli.Command {
] ]
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := vconf.load<Config>(prefix: 'VIETER_', default_path: config_file)? conf := vconf.load<Config>(default_path: config_file)?
found := cmd.flags.get_all_found() found := cmd.flags.get_all_found()
@ -152,10 +152,10 @@ pub fn cmd() cli.Command {
name: 'build' name: 'build'
required_args: 1 required_args: 1
usage: 'id' usage: 'id'
description: 'Build the target with the given id & publish it.' description: 'Build the repo with the given id & publish it.'
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := vconf.load<Config>(prefix: 'VIETER_', default_path: config_file)? conf := vconf.load<Config>(default_path: config_file)?
build(conf, cmd.args[0].int())? build(conf, cmd.args[0].int())?
} }
@ -168,9 +168,9 @@ pub fn cmd() cli.Command {
// ID. If multiple or none are found, an error is raised. // ID. If multiple or none are found, an error is raised.
// list prints out a list of all repositories. // list prints out a list of all repositories.
fn list(conf Config, filter TargetFilter) ? { fn list(conf Config, filter GitRepoFilter) ? {
c := client.new(conf.address, conf.api_key) c := client.new(conf.address, conf.api_key)
repos := c.get_targets(filter)? repos := c.get_git_repos(filter)?
data := repos.map([it.id.str(), it.url, it.branch, it.repo]) data := repos.map([it.id.str(), it.url, it.branch, it.repo])
println(console.pretty_table(['id', 'url', 'branch', 'repo'], data)?) println(console.pretty_table(['id', 'url', 'branch', 'repo'], data)?)
@ -179,7 +179,7 @@ fn list(conf Config, filter TargetFilter) ? {
// add adds a new repository to the server's list. // add adds a new repository to the server's list.
fn add(conf Config, url string, branch string, repo string) ? { fn add(conf Config, url string, branch string, repo string) ? {
c := client.new(conf.address, conf.api_key) c := client.new(conf.address, conf.api_key)
res := c.add_target(url, branch, repo, [])? res := c.add_git_repo(url, branch, repo, [])?
println(res.message) println(res.message)
} }
@ -191,7 +191,7 @@ fn remove(conf Config, id string) ? {
if id_int != 0 { if id_int != 0 {
c := client.new(conf.address, conf.api_key) c := client.new(conf.address, conf.api_key)
res := c.remove_target(id_int)? res := c.remove_git_repo(id_int)?
println(res.message) println(res.message)
} }
} }
@ -209,7 +209,7 @@ fn patch(conf Config, id string, params map[string]string) ? {
id_int := id.int() id_int := id.int()
if id_int != 0 { if id_int != 0 {
c := client.new(conf.address, conf.api_key) c := client.new(conf.address, conf.api_key)
res := c.patch_target(id_int, params)? res := c.patch_git_repo(id_int, params)?
println(res.message) println(res.message)
} }
@ -224,6 +224,6 @@ fn info(conf Config, id string) ? {
} }
c := client.new(conf.address, conf.api_key) c := client.new(conf.address, conf.api_key)
repo := c.get_target(id_int)? repo := c.get_git_repo(id_int)?
println(repo) println(repo)
} }

View File

@ -12,7 +12,7 @@ struct Config {
api_key string [required] api_key string [required]
} }
// cmd returns the cli module that handles the build logs API. // cmd returns the cli module that handles the build repos API.
pub fn cmd() cli.Command { pub fn cmd() cli.Command {
return cli.Command{ return cli.Command{
name: 'logs' name: 'logs'
@ -33,8 +33,8 @@ pub fn cmd() cli.Command {
flag: cli.FlagType.int flag: cli.FlagType.int
}, },
cli.Flag{ cli.Flag{
name: 'target' name: 'repo'
description: 'Only return logs for this target id.' description: 'Only return logs for this repo id.'
flag: cli.FlagType.int flag: cli.FlagType.int
}, },
cli.Flag{ cli.Flag{
@ -65,7 +65,7 @@ pub fn cmd() cli.Command {
] ]
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := vconf.load<Config>(prefix: 'VIETER_', default_path: config_file)? conf := vconf.load<Config>(default_path: config_file)?
mut filter := BuildLogFilter{} mut filter := BuildLogFilter{}
@ -79,9 +79,9 @@ pub fn cmd() cli.Command {
filter.offset = u64(offset) filter.offset = u64(offset)
} }
target_id := cmd.flags.get_int('target')? repo_id := cmd.flags.get_int('repo')?
if target_id != 0 { if repo_id != 0 {
filter.target = target_id filter.repo = repo_id
} }
tz_offset := time.offset() tz_offset := time.offset()
@ -143,7 +143,7 @@ pub fn cmd() cli.Command {
description: 'Show all info for a specific build log.' description: 'Show all info for a specific build log.'
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := vconf.load<Config>(prefix: 'VIETER_', default_path: config_file)? conf := vconf.load<Config>(default_path: config_file)?
id := cmd.args[0].int() id := cmd.args[0].int()
info(conf, id)? info(conf, id)?
@ -156,7 +156,7 @@ pub fn cmd() cli.Command {
description: 'Output the content of a build log to stdout.' description: 'Output the content of a build log to stdout.'
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := vconf.load<Config>(prefix: 'VIETER_', default_path: config_file)? conf := vconf.load<Config>(default_path: config_file)?
id := cmd.args[0].int() id := cmd.args[0].int()
content(conf, id)? content(conf, id)?
@ -168,10 +168,10 @@ pub fn cmd() cli.Command {
// print_log_list prints a list of logs. // print_log_list prints a list of logs.
fn print_log_list(logs []BuildLog) ? { fn print_log_list(logs []BuildLog) ? {
data := logs.map([it.id.str(), it.target_id.str(), it.start_time.local().str(), data := logs.map([it.id.str(), it.repo_id.str(), it.start_time.local().str(),
it.exit_code.str()]) it.exit_code.str()])
println(console.pretty_table(['id', 'target', 'start time', 'exit code'], data)?) println(console.pretty_table(['id', 'repo', 'start time', 'exit code'], data)?)
} }
// list prints a list of all build logs. // list prints a list of all build logs.
@ -182,10 +182,10 @@ fn list(conf Config, filter BuildLogFilter) ? {
print_log_list(logs)? print_log_list(logs)?
} }
// list prints a list of all build logs for a given target. // list prints a list of all build logs for a given repo.
fn list_for_target(conf Config, target_id int) ? { fn list_for_repo(conf Config, repo_id int) ? {
c := client.new(conf.address, conf.api_key) c := client.new(conf.address, conf.api_key)
logs := c.get_build_logs_for_target(target_id)?.data logs := c.get_build_logs_for_repo(repo_id)?.data
print_log_list(logs)? print_log_list(logs)?
} }

View File

@ -24,7 +24,7 @@ pub fn cmd() cli.Command {
description: 'Start the cron service that periodically runs builds.' description: 'Start the cron service that periodically runs builds.'
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := vconf.load<Config>(prefix: 'VIETER_', default_path: config_file)? conf := vconf.load<Config>(default_path: config_file)?
cron(conf)? cron(conf)?
} }

View File

@ -9,7 +9,7 @@ import build
import docker import docker
import os import os
import client import client
import models { Target } import models { GitRepo }
const ( const (
// How many seconds to wait before retrying to update API if failed // How many seconds to wait before retrying to update API if failed
@ -20,7 +20,7 @@ const (
struct ScheduledBuild { struct ScheduledBuild {
pub: pub:
repo Target repo GitRepo
timestamp time.Time timestamp time.Time
} }
@ -38,7 +38,7 @@ mut:
api_update_frequency int api_update_frequency int
image_rebuild_frequency int image_rebuild_frequency int
// Repos currently loaded from API. // Repos currently loaded from API.
repos []Target repos []GitRepo
// At what point to update the list of repositories. // At what point to update the list of repositories.
api_update_timestamp time.Time api_update_timestamp time.Time
image_build_timestamp time.Time image_build_timestamp time.Time
@ -149,7 +149,7 @@ pub fn (mut d Daemon) run() {
} }
// schedule_build adds the next occurence of the given repo build to the queue. // schedule_build adds the next occurence of the given repo build to the queue.
fn (mut d Daemon) schedule_build(repo Target) { fn (mut d Daemon) schedule_build(repo GitRepo) {
ce := if repo.schedule != '' { ce := if repo.schedule != '' {
parse_expression(repo.schedule) or { parse_expression(repo.schedule) or {
// TODO This shouldn't return an error if the expression is empty. // TODO This shouldn't return an error if the expression is empty.
@ -178,7 +178,7 @@ fn (mut d Daemon) schedule_build(repo Target) {
fn (mut d Daemon) renew_repos() { fn (mut d Daemon) renew_repos() {
d.linfo('Renewing repos...') d.linfo('Renewing repos...')
mut new_repos := d.client.get_all_targets() 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.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) d.api_update_timestamp = time.now().add_seconds(daemon.api_update_retry_timeout)

View File

@ -13,12 +13,8 @@ struct MigrationVersion {
} }
const ( const (
migrations_up = [ migrations_up = [$embed_file('migrations/001-initial/up.sql')]
$embed_file('migrations/001-initial/up.sql'), migrations_down = [$embed_file('migrations/001-initial/down.sql')]
$embed_file('migrations/002-rename-to-targets/up.sql'),
]
migrations_down = [$embed_file('migrations/001-initial/down.sql'),
$embed_file('migrations/002-rename-to-targets/down.sql')]
) )
// init initializes a database & adds the correct tables. // init initializes a database & adds the correct tables.

99
src/db/git.v 100644
View File

@ -0,0 +1,99 @@
module db
import models { GitRepo, GitRepoArch, GitRepoFilter }
// get_git_repos returns all GitRepo's in the database.
pub fn (db &VieterDb) get_git_repos(filter GitRepoFilter) []GitRepo {
// This seems to currently be blocked by a bug in the ORM, I'll have to ask
// around.
if filter.repo != '' {
res := sql db.conn {
select from GitRepo where repo == filter.repo order by id limit filter.limit offset filter.offset
}
return res
}
res := sql db.conn {
select from GitRepo order by id limit filter.limit offset filter.offset
}
return res
}
// get_git_repo tries to return a specific GitRepo.
pub fn (db &VieterDb) get_git_repo(repo_id int) ?GitRepo {
res := sql db.conn {
select from GitRepo where id == repo_id
}
// If a select statement fails, it returns a zeroed object. By
// checking one of the required fields, we can see whether the query
// returned a result or not.
if res.id == 0 {
return none
}
return res
}
// add_git_repo inserts the given GitRepo into the database.
pub fn (db &VieterDb) add_git_repo(repo GitRepo) {
sql db.conn {
insert repo into GitRepo
}
}
// delete_git_repo deletes the repo with the given ID from the database.
pub fn (db &VieterDb) delete_git_repo(repo_id int) {
sql db.conn {
delete from GitRepo where id == repo_id
delete from GitRepoArch where repo_id == repo_id
}
}
// update_git_repo updates any non-array values for a given GitRepo.
pub fn (db &VieterDb) update_git_repo(repo_id int, params map[string]string) {
mut values := []string{}
// TODO does this allow for SQL injection?
$for field in GitRepo.fields {
if field.name in params {
// Any fields that are array types require their own update method
$if field.typ is string {
values << "$field.name = '${params[field.name]}'"
}
}
}
values_str := values.join(', ')
// I think this is actual SQL & not the ORM language
query := 'update GitRepo set $values_str where id == $repo_id'
db.conn.exec_none(query)
}
// update_git_repo_archs updates a given GitRepo's arch value.
pub fn (db &VieterDb) update_git_repo_archs(repo_id int, archs []GitRepoArch) {
archs_with_id := archs.map(GitRepoArch{
...it
repo_id: repo_id
})
sql db.conn {
delete from GitRepoArch where repo_id == repo_id
}
for arch in archs_with_id {
sql db.conn {
insert arch into GitRepoArch
}
}
}
// git_repo_exists is a utility function that checks whether a repo with the
// given id exists.
pub fn (db &VieterDb) git_repo_exists(repo_id int) bool {
db.get_git_repo(repo_id) or { return false }
return true
}

View File

@ -7,8 +7,8 @@ import time
pub fn (db &VieterDb) get_build_logs(filter BuildLogFilter) []BuildLog { pub fn (db &VieterDb) get_build_logs(filter BuildLogFilter) []BuildLog {
mut where_parts := []string{} mut where_parts := []string{}
if filter.target != 0 { if filter.repo != 0 {
where_parts << 'target_id == $filter.target' where_parts << 'repo_id == $filter.repo'
} }
if filter.before != time.Time{} { if filter.before != time.Time{} {
@ -55,11 +55,11 @@ pub fn (db &VieterDb) get_build_logs(filter BuildLogFilter) []BuildLog {
return res return res
} }
// get_build_logs_for_target returns all BuildLog's in the database for a given // get_build_logs_for_repo returns all BuildLog's in the database for a given
// target. // repo.
pub fn (db &VieterDb) get_build_logs_for_target(target_id int) []BuildLog { pub fn (db &VieterDb) get_build_logs_for_repo(repo_id int) []BuildLog {
res := sql db.conn { res := sql db.conn {
select from BuildLog where target_id == target_id order by id select from BuildLog where repo_id == repo_id order by id
} }
return res return res

View File

@ -1,5 +0,0 @@
ALTER TABLE Target RENAME TO GitRepo;
ALTER TABLE TargetArch RENAME TO GitRepoArch;
ALTER TABLE GitRepoArch RENAME COLUMN target_id TO repo_id;
ALTER TABLE BuildLog RENAME COLUMN target_id TO repo_id;

View File

@ -1,5 +0,0 @@
ALTER TABLE GitRepo RENAME TO Target;
ALTER TABLE GitRepoArch RENAME TO TargetArch;
ALTER TABLE TargetArch RENAME COLUMN repo_id TO target_id;
ALTER TABLE BuildLog RENAME COLUMN repo_id TO target_id;

View File

@ -1,99 +0,0 @@
module db
import models { Target, TargetArch, TargetFilter }
// get_targets returns all targets in the database.
pub fn (db &VieterDb) get_targets(filter TargetFilter) []Target {
// This seems to currently be blocked by a bug in the ORM, I'll have to ask
// around.
if filter.repo != '' {
res := sql db.conn {
select from Target where repo == filter.repo order by id limit filter.limit offset filter.offset
}
return res
}
res := sql db.conn {
select from Target order by id limit filter.limit offset filter.offset
}
return res
}
// get_target tries to return a specific target.
pub fn (db &VieterDb) get_target(target_id int) ?Target {
res := sql db.conn {
select from Target where id == target_id
}
// If a select statement fails, it returns a zeroed object. By
// checking one of the required fields, we can see whether the query
// returned a result or not.
if res.id == 0 {
return none
}
return res
}
// add_target inserts the given target into the database.
pub fn (db &VieterDb) add_target(repo Target) {
sql db.conn {
insert repo into Target
}
}
// delete_target deletes the target with the given id from the database.
pub fn (db &VieterDb) delete_target(target_id int) {
sql db.conn {
delete from Target where id == target_id
delete from TargetArch where target_id == target_id
}
}
// update_target updates any non-array values for a given target.
pub fn (db &VieterDb) update_target(target_id int, params map[string]string) {
mut values := []string{}
// TODO does this allow for SQL injection?
$for field in Target.fields {
if field.name in params {
// Any fields that are array types require their own update method
$if field.typ is string {
values << "$field.name = '${params[field.name]}'"
}
}
}
values_str := values.join(', ')
// I think this is actual SQL & not the ORM language
query := 'update Target set $values_str where id == $target_id'
db.conn.exec_none(query)
}
// update_target_archs updates a given target's arch value.
pub fn (db &VieterDb) update_target_archs(target_id int, archs []TargetArch) {
archs_with_id := archs.map(TargetArch{
...it
target_id: target_id
})
sql db.conn {
delete from TargetArch where target_id == target_id
}
for arch in archs_with_id {
sql db.conn {
insert arch into TargetArch
}
}
}
// target_exists is a utility function that checks whether a target with the
// given id exists.
pub fn (db &VieterDb) target_exists(target_id int) bool {
db.get_target(target_id) or { return false }
return true
}

View File

@ -3,7 +3,7 @@ module main
import os import os
import server import server
import cli import cli
import console.targets import console.git
import console.logs import console.logs
import console.schedule import console.schedule
import console.man import console.man
@ -26,7 +26,7 @@ fn main() {
] ]
commands: [ commands: [
server.cmd(), server.cmd(),
targets.cmd(), git.cmd(),
cron.cmd(), cron.cmd(),
logs.cmd(), logs.cmd(),
schedule.cmd(), schedule.cmd(),

View File

@ -1,18 +1,18 @@
module models module models
pub struct TargetArch { pub struct GitRepoArch {
pub: pub:
id int [primary; sql: serial] id int [primary; sql: serial]
target_id int [nonull] repo_id int [nonull]
value string [nonull] value string [nonull]
} }
// str returns a string representation. // str returns a string representation.
pub fn (gra &TargetArch) str() string { pub fn (gra &GitRepoArch) str() string {
return gra.value return gra.value
} }
pub struct Target { pub struct GitRepo {
pub mut: pub mut:
id int [primary; sql: serial] id int [primary; sql: serial]
// URL of the Git repository // URL of the Git repository
@ -25,11 +25,11 @@ pub mut:
schedule string schedule string
// On which architectures the package is allowed to be built. In reality, // On which architectures the package is allowed to be built. In reality,
// this controls which builders will periodically build the image. // this controls which builders will periodically build the image.
arch []TargetArch [fkey: 'target_id'] arch []GitRepoArch [fkey: 'repo_id']
} }
// str returns a string representation. // str returns a string representation.
pub fn (gr &Target) str() string { pub fn (gr &GitRepo) str() string {
mut parts := [ mut parts := [
'id: $gr.id', 'id: $gr.id',
'url: $gr.url', 'url: $gr.url',
@ -44,7 +44,7 @@ pub fn (gr &Target) str() string {
} }
[params] [params]
pub struct TargetFilter { pub struct GitRepoFilter {
pub mut: pub mut:
limit u64 = 25 limit u64 = 25
offset u64 offset u64

View File

@ -5,7 +5,7 @@ import time
pub struct BuildLog { pub struct BuildLog {
pub mut: pub mut:
id int [primary; sql: serial] id int [primary; sql: serial]
target_id int [nonull] repo_id int [nonull]
start_time time.Time [nonull] start_time time.Time [nonull]
end_time time.Time [nonull] end_time time.Time [nonull]
arch string [nonull] arch string [nonull]
@ -16,7 +16,7 @@ pub mut:
pub fn (bl &BuildLog) str() string { pub fn (bl &BuildLog) str() string {
mut parts := [ mut parts := [
'id: $bl.id', 'id: $bl.id',
'target id: $bl.target_id', 'repo id: $bl.repo_id',
'start time: $bl.start_time.local()', 'start time: $bl.start_time.local()',
'end time: $bl.end_time.local()', 'end time: $bl.end_time.local()',
'duration: ${bl.end_time - bl.start_time}', 'duration: ${bl.end_time - bl.start_time}',
@ -33,7 +33,7 @@ pub struct BuildLogFilter {
pub mut: pub mut:
limit u64 = 25 limit u64 = 25
offset u64 offset u64
target int repo int
before time.Time before time.Time
after time.Time after time.Time
arch string arch string

View File

@ -23,8 +23,8 @@ pub fn patch_from_params<T>(mut o T, params map[string]string) ? {
o.$(field.name) = params[field.name].int() o.$(field.name) = params[field.name].int()
} $else $if field.typ is u64 { } $else $if field.typ is u64 {
o.$(field.name) = params[field.name].u64() o.$(field.name) = params[field.name].u64()
} $else $if field.typ is []TargetArch { } $else $if field.typ is []GitRepoArch {
o.$(field.name) = params[field.name].split(',').map(TargetArch{ value: it }) o.$(field.name) = params[field.name].split(',').map(GitRepoArch{ value: it })
} $else $if field.typ is time.Time { } $else $if field.typ is time.Time {
o.$(field.name) = time.unix(params[field.name].int()) o.$(field.name) = time.unix(params[field.name].int())
} $else $if field.typ is []string { } $else $if field.typ is []string {

View File

@ -1,6 +0,0 @@
This module contains the Vieter HTTP server, consisting of the repository
implementation & the REST API.
**NOTE**: vweb defines the priority order of routes by the file names in this
module. Therefore, it's very important that all API routes are defined in files
prefixed with `api_`, as this is before the word `routes` alphabetically.

View File

@ -10,7 +10,6 @@ pub:
data_dir string data_dir string
api_key string api_key string
default_arch string default_arch string
port int = 8000
} }
// cmd returns the cli submodule that handles starting the server // cmd returns the cli submodule that handles starting the server
@ -20,7 +19,7 @@ pub fn cmd() cli.Command {
description: 'Start the Vieter server.' description: 'Start the Vieter server.'
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')? config_file := cmd.flags.get_string('config-file')?
conf := vconf.load<Config>(prefix: 'VIETER_', default_path: config_file)? conf := vconf.load<Config>(default_path: config_file)?
server(conf)? server(conf)?
} }

View File

@ -4,38 +4,38 @@ import web
import net.http import net.http
import response { new_data_response, new_response } import response { new_data_response, new_response }
import db import db
import models { Target, TargetArch, TargetFilter } import models { GitRepo, GitRepoArch, GitRepoFilter }
// v1_get_targets returns the current list of targets. // get_repos returns the current list of repos.
['/api/v1/targets'; get] ['/api/repos'; get]
fn (mut app App) v1_get_targets() web.Result { fn (mut app App) get_repos() web.Result {
if !app.is_authorized() { if !app.is_authorized() {
return app.json(http.Status.unauthorized, new_response('Unauthorized.')) return app.json(http.Status.unauthorized, new_response('Unauthorized.'))
} }
filter := models.from_params<TargetFilter>(app.query) or { filter := models.from_params<GitRepoFilter>(app.query) or {
return app.json(http.Status.bad_request, new_response('Invalid query parameters.')) return app.json(http.Status.bad_request, new_response('Invalid query parameters.'))
} }
repos := app.db.get_targets(filter) repos := app.db.get_git_repos(filter)
return app.json(http.Status.ok, new_data_response(repos)) return app.json(http.Status.ok, new_data_response(repos))
} }
// v1_get_single_target returns the information for a single target. // get_single_repo returns the information for a single repo.
['/api/v1/targets/:id'; get] ['/api/repos/:id'; get]
fn (mut app App) v1_get_single_target(id int) web.Result { fn (mut app App) get_single_repo(id int) web.Result {
if !app.is_authorized() { if !app.is_authorized() {
return app.json(http.Status.unauthorized, new_response('Unauthorized.')) return app.json(http.Status.unauthorized, new_response('Unauthorized.'))
} }
repo := app.db.get_target(id) or { return app.not_found() } repo := app.db.get_git_repo(id) or { return app.not_found() }
return app.json(http.Status.ok, new_data_response(repo)) return app.json(http.Status.ok, new_data_response(repo))
} }
// v1_post_target creates a new target from the provided query string. // post_repo creates a new repo from the provided query string.
['/api/v1/targets'; post] ['/api/repos'; post]
fn (mut app App) v1_post_target() web.Result { fn (mut app App) post_repo() web.Result {
if !app.is_authorized() { if !app.is_authorized() {
return app.json(http.Status.unauthorized, new_response('Unauthorized.')) return app.json(http.Status.unauthorized, new_response('Unauthorized.'))
} }
@ -48,40 +48,40 @@ fn (mut app App) v1_post_target() web.Result {
params['arch'] = app.conf.default_arch params['arch'] = app.conf.default_arch
} }
new_repo := models.from_params<Target>(params) or { new_repo := models.from_params<GitRepo>(params) or {
return app.json(http.Status.bad_request, new_response(err.msg())) return app.json(http.Status.bad_request, new_response(err.msg()))
} }
app.db.add_target(new_repo) app.db.add_git_repo(new_repo)
return app.json(http.Status.ok, new_response('Repo added successfully.')) return app.json(http.Status.ok, new_response('Repo added successfully.'))
} }
// v1_delete_target removes a given target from the server's list. // delete_repo removes a given repo from the server's list.
['/api/v1/targets/:id'; delete] ['/api/repos/:id'; delete]
fn (mut app App) v1_delete_target(id int) web.Result { fn (mut app App) delete_repo(id int) web.Result {
if !app.is_authorized() { if !app.is_authorized() {
return app.json(http.Status.unauthorized, new_response('Unauthorized.')) return app.json(http.Status.unauthorized, new_response('Unauthorized.'))
} }
app.db.delete_target(id) app.db.delete_git_repo(id)
return app.json(http.Status.ok, new_response('Repo removed successfully.')) return app.json(http.Status.ok, new_response('Repo removed successfully.'))
} }
// v1_patch_target updates a target's data with the given query params. // patch_repo updates a repo's data with the given query params.
['/api/v1/targets/:id'; patch] ['/api/repos/:id'; patch]
fn (mut app App) v1_patch_target(id int) web.Result { fn (mut app App) patch_repo(id int) web.Result {
if !app.is_authorized() { if !app.is_authorized() {
return app.json(http.Status.unauthorized, new_response('Unauthorized.')) return app.json(http.Status.unauthorized, new_response('Unauthorized.'))
} }
app.db.update_target(id, app.query) app.db.update_git_repo(id, app.query)
if 'arch' in app.query { if 'arch' in app.query {
arch_objs := app.query['arch'].split(',').map(TargetArch{ value: it }) arch_objs := app.query['arch'].split(',').map(GitRepoArch{ value: it })
app.db.update_target_archs(id, arch_objs) app.db.update_git_repo_archs(id, arch_objs)
} }
return app.json(http.Status.ok, new_response('Repo updated successfully.')) return app.json(http.Status.ok, new_response('Repo updated successfully.'))

View File

@ -10,10 +10,10 @@ import os
import util import util
import models { BuildLog, BuildLogFilter } import models { BuildLog, BuildLogFilter }
// v1_get_logs returns all build logs in the database. A 'target' query param can // get_logs returns all build logs in the database. A 'repo' query param can
// optionally be added to limit the list of build logs to that repository. // optionally be added to limit the list of build logs to that repository.
['/api/v1/logs'; get] ['/api/logs'; get]
fn (mut app App) v1_get_logs() web.Result { fn (mut app App) get_logs() web.Result {
if !app.is_authorized() { if !app.is_authorized() {
return app.json(http.Status.unauthorized, new_response('Unauthorized.')) return app.json(http.Status.unauthorized, new_response('Unauthorized.'))
} }
@ -26,9 +26,9 @@ fn (mut app App) v1_get_logs() web.Result {
return app.json(http.Status.ok, new_data_response(logs)) return app.json(http.Status.ok, new_data_response(logs))
} }
// v1_get_single_log returns the build log with the given id. // get_single_log returns the build log with the given id.
['/api/v1/logs/:id'; get] ['/api/logs/:id'; get]
fn (mut app App) v1_get_single_log(id int) web.Result { fn (mut app App) get_single_log(id int) web.Result {
if !app.is_authorized() { if !app.is_authorized() {
return app.json(http.Status.unauthorized, new_response('Unauthorized.')) return app.json(http.Status.unauthorized, new_response('Unauthorized.'))
} }
@ -38,16 +38,16 @@ fn (mut app App) v1_get_single_log(id int) web.Result {
return app.json(http.Status.ok, new_data_response(log)) return app.json(http.Status.ok, new_data_response(log))
} }
// v1_get_log_content returns the actual build log file for the given id. // get_log_content returns the actual build log file for the given id.
['/api/v1/logs/:id/content'; get] ['/api/logs/:id/content'; get]
fn (mut app App) v1_get_log_content(id int) web.Result { fn (mut app App) get_log_content(id int) web.Result {
if !app.is_authorized() { if !app.is_authorized() {
return app.json(http.Status.unauthorized, new_response('Unauthorized.')) return app.json(http.Status.unauthorized, new_response('Unauthorized.'))
} }
log := app.db.get_build_log(id) or { return app.not_found() } log := app.db.get_build_log(id) or { return app.not_found() }
file_name := log.start_time.custom_format('YYYY-MM-DD_HH-mm-ss') file_name := log.start_time.custom_format('YYYY-MM-DD_HH-mm-ss')
full_path := os.join_path(app.conf.data_dir, logs_dir_name, log.target_id.str(), log.arch, full_path := os.join_path(app.conf.data_dir, logs_dir_name, log.repo_id.str(), log.arch,
file_name) file_name)
return app.file(full_path) return app.file(full_path)
@ -62,9 +62,9 @@ fn parse_query_time(query string) ?time.Time {
return t return t
} }
// v1_post_log adds a new log to the database. // post_log adds a new log to the database.
['/api/v1/logs'; post] ['/api/logs'; post]
fn (mut app App) v1_post_log() web.Result { fn (mut app App) post_log() web.Result {
if !app.is_authorized() { if !app.is_authorized() {
return app.json(http.Status.unauthorized, new_response('Unauthorized.')) return app.json(http.Status.unauthorized, new_response('Unauthorized.'))
} }
@ -96,15 +96,15 @@ fn (mut app App) v1_post_log() web.Result {
arch := app.query['arch'] arch := app.query['arch']
target_id := app.query['target'].int() repo_id := app.query['repo'].int()
if !app.db.target_exists(target_id) { if !app.db.git_repo_exists(repo_id) {
return app.json(http.Status.bad_request, new_response('Unknown target.')) return app.json(http.Status.bad_request, new_response('Unknown Git repo.'))
} }
// Store log in db // Store log in db
log := BuildLog{ log := BuildLog{
target_id: target_id repo_id: repo_id
start_time: start_time start_time: start_time
end_time: end_time end_time: end_time
arch: arch arch: arch
@ -113,7 +113,7 @@ fn (mut app App) v1_post_log() web.Result {
app.db.add_build_log(log) app.db.add_build_log(log)
repo_logs_dir := os.join_path(app.conf.data_dir, logs_dir_name, target_id.str(), arch) repo_logs_dir := os.join_path(app.conf.data_dir, logs_dir_name, repo_id.str(), arch)
// Create the logs directory of it doesn't exist // Create the logs directory of it doesn't exist
if !os.exists(repo_logs_dir) { if !os.exists(repo_logs_dir) {

View File

@ -8,6 +8,7 @@ import util
import db import db
const ( const (
port = 8000
log_file_name = 'vieter.log' log_file_name = 'vieter.log'
repo_dir_name = 'repos' repo_dir_name = 'repos'
db_file_name = 'vieter.sqlite' db_file_name = 'vieter.sqlite'
@ -76,5 +77,5 @@ pub fn server(conf Config) ? {
conf: conf conf: conf
repo: repo repo: repo
db: db db: db
}, conf.port) }, server.port)
} }