Compare commits

..

No commits in common. "c018aad14382fd32746d5f245290375b25501b98" and "7e5f0c5a539db49744337dace8a88481ddb395b8" have entirely different histories.

7 changed files with 38 additions and 106 deletions

View File

@ -11,9 +11,7 @@ pipeline:
- 'docker_password' - 'docker_password'
settings: settings:
repo: 'chewingbever/vieter' repo: 'chewingbever/vieter'
tags: tag: 'dev'
- 'dev'
- ${CI_COMMIT_SHA}
platforms: [ 'linux/arm64/v8', 'linux/amd64' ] platforms: [ 'linux/arm64/v8', 'linux/amd64' ]
build_args_from_env: build_args_from_env:
- 'CI_COMMIT_SHA' - 'CI_COMMIT_SHA'

View File

@ -11,12 +11,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Web API for adding & querying build logs * Web API for adding & querying build logs
* CLI commands to access build logs API * CLI commands to access build logs API
* Cron build logs are uploaded to above API
### Changed
- `vieter build` command now only builds a single repository & uploads the
build logs
## [0.3.0-alpha.1](https://git.rustybever.be/vieter/vieter/src/tag/0.3.0-alpha.1) ## [0.3.0-alpha.1](https://git.rustybever.be/vieter/vieter/src/tag/0.3.0-alpha.1)

View File

@ -15,7 +15,7 @@ repositories. After the image has been created, each repository returned by
previously created image as a base. Each container goes through the following steps: previously created image as a base. Each container goes through the following steps:
1. The repository is cloned 1. The repository is cloned
2. `makepkg --nobuild --syncdeps --needed --noconfirm` is ran to update the `pkgver` variable inside 2. `makepkg --nobuild --nodeps` is ran to update the `pkgver` variable inside
the `PKGBUILD` file the `PKGBUILD` file
3. A HEAD request is sent to the Vieter server to check whether the specific 3. A HEAD request is sent to the Vieter server to check whether the specific
version of the package is already present. If it is, the container exits. version of the package is already present. If it is, the container exits.

View File

@ -73,29 +73,21 @@ pub fn create_build_image(base_image string) ?string {
return image.id return image.id
} }
pub struct BuildResult {
pub:
start_time time.Time
end_time time.Time
exit_code int
logs string
}
// 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 GitRepo. 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.
pub fn build_repo(address string, api_key string, base_image_id string, repo &db.GitRepo) ?BuildResult { pub fn build_repo(address string, api_key string, base_image_id string, repo &db.GitRepo) ? {
build_arch := os.uname().machine build_arch := os.uname().machine
// TODO what to do with PKGBUILDs that build multiple packages? // TODO what to do with PKGBUILDs that build multiple packages?
commands := [ commands := [
'git clone --single-branch --depth 1 --branch $repo.branch $repo.url repo', 'git clone --single-branch --depth 1 --branch $repo.branch $repo.url repo',
'cd repo', 'cd repo',
'makepkg --nobuild --syncdeps --needed --noconfirm', 'makepkg --nobuild --nodeps',
'source PKGBUILD', 'source PKGBUILD',
// The build container checks whether the package is already // The build container checks whether the package is already
// present on the server // present on the server
'curl -s --head --fail $address/$repo.repo/$build_arch/\$pkgname-\$pkgver-\$pkgrel && exit 0', 'curl --head --fail $address/$repo.repo/$build_arch/\$pkgname-\$pkgver-\$pkgrel && exit 0',
'MAKEFLAGS="-j\$(nproc)" makepkg -s --noconfirm --needed && for pkg in \$(ls -1 *.pkg*); do curl -XPOST -T "\$pkg" -H "X-API-KEY: \$API_KEY" $address/$repo.repo/publish; done', 'MAKEFLAGS="-j\$(nproc)" makepkg -s --noconfirm --needed && for pkg in \$(ls -1 *.pkg*); do curl -XPOST -T "\$pkg" -H "X-API-KEY: \$API_KEY" $address/$repo.repo/publish; done',
] ]
@ -115,44 +107,43 @@ pub fn build_repo(address string, api_key string, base_image_id string, repo &db
id := docker.create_container(c) ? id := docker.create_container(c) ?
docker.start_container(id) ? docker.start_container(id) ?
mut data := docker.inspect_container(id) ?
// This loop waits until the container has stopped, so we can remove it after // This loop waits until the container has stopped, so we can remove it after
for data.state.running { for {
time.sleep(1 * time.second) data := docker.inspect_container(id) ?
data = docker.inspect_container(id) ? if !data.state.running {
break
} }
logs := docker.get_container_logs(id) ? time.sleep(1 * time.second)
}
docker.remove_container(id) ? docker.remove_container(id) ?
return BuildResult{
start_time: data.state.start_time
end_time: data.state.end_time
exit_code: data.state.exit_code
logs: logs
}
} }
// build builds every Git repo in the server's list. // build builds every Git repo in the server's list.
fn build(conf Config, repo_id int) ? { fn build(conf Config) ? {
c := client.new(conf.address, conf.api_key)
repo := c.get_git_repo(repo_id) ?
build_arch := os.uname().machine build_arch := os.uname().machine
println('Creating base image...') // We get the repos map from the Vieter instance
repos := client.new(conf.address, conf.api_key).get_git_repos() ?
// We filter out any repos that aren't allowed to be built on this
// architecture
filtered_repos := repos.filter(it.arch.map(it.value).contains(build_arch))
// No point in doing work if there's no repos present
if filtered_repos.len == 0 {
return
}
// First, we create a base image which has updated repos n stuff
image_id := create_build_image(conf.base_image) ? image_id := create_build_image(conf.base_image) ?
println('Running build...') for repo in filtered_repos {
res := build_repo(conf.address, conf.api_key, image_id, repo) ? build_repo(conf.address, conf.api_key, image_id, repo) ?
}
println('Removing build image...')
docker.remove_image(image_id) ? // Finally, we remove the builder image
docker.remove_image(image_id) ?
println('Uploading logs to Vieter...')
c.add_build_log(repo.id, res.start_time, res.end_time, build_arch, res.exit_code,
res.logs) ?
} }

View File

@ -14,16 +14,12 @@ pub:
pub fn cmd() cli.Command { pub fn cmd() cli.Command {
return cli.Command{ return cli.Command{
name: 'build' name: 'build'
required_args: 1 description: 'Run the build process.'
usage: 'id'
description: 'Build the repository with the given ID.'
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 := env.load<Config>(config_file) ? conf := env.load<Config>(config_file) ?
id := cmd.args[0].int() build(conf) ?
build(conf, id) ?
} }
} }
} }

View File

@ -3,7 +3,6 @@ module daemon
import time import time
import sync.stdatomic import sync.stdatomic
import build import build
import os
const ( const (
build_empty = 0 build_empty = 0
@ -78,20 +77,13 @@ fn (mut d Daemon) run_build(build_index int, sb ScheduledBuild) {
// 0 means success, 1 means failure // 0 means success, 1 means failure
mut status := 0 mut status := 0
res := build.build_repo(d.client.address, d.client.api_key, d.builder_images.last(), build.build_repo(d.client.address, d.client.api_key, d.builder_images.last(), &sb.repo) or {
&sb.repo) or {
d.ldebug('build_repo error: $err.msg()') d.ldebug('build_repo error: $err.msg()')
status = 1 status = 1
build.BuildResult{}
} }
if status == 0 { if status == 0 {
d.linfo('finished build: $sb.repo.url $sb.repo.branch; uploading logs...') d.linfo('finished build: $sb.repo.url $sb.repo.branch')
build_arch := os.uname().machine
d.client.add_build_log(sb.repo.id, res.start_time, res.end_time, build_arch, res.exit_code,
res.logs) or { d.lerror('Failed to upload logs for $sb.repo.url $sb.repo.arch') }
} else { } else {
d.linfo('failed build: $sb.repo.url $sb.repo.branch') d.linfo('failed build: $sb.repo.url $sb.repo.branch')
} }

View File

@ -2,7 +2,6 @@ module docker
import json import json
import net.urllib import net.urllib
import time
struct Container { struct Container {
id string [json: Id] id string [json: Id]
@ -50,21 +49,13 @@ pub fn start_container(id string) ?bool {
} }
struct ContainerInspect { struct ContainerInspect {
pub mut: pub:
state ContainerState [json: State] state ContainerState [json: State]
} }
struct ContainerState { struct ContainerState {
pub: pub:
running bool [json: Running] running bool [json: Running]
status string [json: Status]
exit_code int [json: ExitCode]
// These use a rather specific format so they have to be parsed later
start_time_str string [json: StartedAt]
end_time_str string [json: FinishedAt]
pub mut:
start_time time.Time [skip]
end_time time.Time [skip]
} }
// inspect_container returns the result of inspecting a container with a given // inspect_container returns the result of inspecting a container with a given
@ -76,15 +67,7 @@ pub fn inspect_container(id string) ?ContainerInspect {
return error('Failed to inspect container.') return error('Failed to inspect container.')
} }
mut data := json.decode(ContainerInspect, res.text) ? return json.decode(ContainerInspect, res.text) or {}
data.state.start_time = time.parse_rfc3339(data.state.start_time_str) ?
if data.state.status == 'exited' {
data.state.end_time = time.parse_rfc3339(data.state.end_time_str) ?
}
return data
} }
// remove_container removes a container with a given ID. // remove_container removes a container with a given ID.
@ -93,25 +76,3 @@ pub fn remove_container(id string) ?bool {
return res.status_code == 204 return res.status_code == 204
} }
// get_container_logs retrieves the logs for a Docker container, both stdout &
// stderr.
pub fn get_container_logs(id string) ?string {
res := request('GET', urllib.parse('/v1.41/containers/$id/logs?stdout=true&stderr=true') ?) ?
mut res_bytes := res.text.bytes()
// Docker uses a special "stream" format for their logs, so we have to
// clean up the data.
mut index := 0
for index < res_bytes.len {
// The reverse is required because V reads in the bytes differently
t := res_bytes[index + 4..index + 8].reverse()
len_length := unsafe { *(&u32(&t[0])) }
res_bytes.delete_many(index, 8)
index += int(len_length)
}
return res_bytes.bytestr()
}