diff --git a/.woodpecker/.docker.yml b/.woodpecker/.docker.yml index f31490a..bab869b 100644 --- a/.woodpecker/.docker.yml +++ b/.woodpecker/.docker.yml @@ -11,9 +11,7 @@ pipeline: - 'docker_password' settings: repo: 'chewingbever/vieter' - tags: - - 'dev' - - ${CI_COMMIT_SHA} + tag: 'dev' platforms: [ 'linux/arm64/v8', 'linux/amd64' ] build_args_from_env: - 'CI_COMMIT_SHA' diff --git a/CHANGELOG.md b/CHANGELOG.md index da09449..7d9eb4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 * 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) diff --git a/docs/content/builder.md b/docs/content/builder.md index 659717d..6a1bc3a 100644 --- a/docs/content/builder.md +++ b/docs/content/builder.md @@ -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: 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 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. diff --git a/src/build/build.v b/src/build/build.v index 7bd63a7..6f033e6 100644 --- a/src/build/build.v +++ b/src/build/build.v @@ -73,29 +73,21 @@ pub fn create_build_image(base_image string) ?string { 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 // provided GitRepo. The base image ID should be of an image previously created -// 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 &db.GitRepo) ?BuildResult { +// by create_build_image. +pub fn build_repo(address string, api_key string, base_image_id string, repo &db.GitRepo) ? { build_arch := os.uname().machine // TODO what to do with PKGBUILDs that build multiple packages? commands := [ 'git clone --single-branch --depth 1 --branch $repo.branch $repo.url repo', 'cd repo', - 'makepkg --nobuild --syncdeps --needed --noconfirm', + 'makepkg --nobuild --nodeps', 'source PKGBUILD', // The build container checks whether the package is already // 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', ] @@ -115,44 +107,43 @@ pub fn build_repo(address string, api_key string, base_image_id string, repo &db id := docker.create_container(c) ? docker.start_container(id) ? - mut data := docker.inspect_container(id) ? - // This loop waits until the container has stopped, so we can remove it after - for data.state.running { + for { + data := docker.inspect_container(id) ? + + if !data.state.running { + break + } + time.sleep(1 * time.second) - - data = docker.inspect_container(id) ? } - logs := docker.get_container_logs(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. -fn build(conf Config, repo_id int) ? { - c := client.new(conf.address, conf.api_key) - repo := c.get_git_repo(repo_id) ? - +fn build(conf Config) ? { 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) ? - println('Running build...') - res := build_repo(conf.address, conf.api_key, image_id, repo) ? + for repo in filtered_repos { + build_repo(conf.address, conf.api_key, image_id, repo) ? + } - println('Removing build image...') + // 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) ? } diff --git a/src/build/cli.v b/src/build/cli.v index 5247e87..0131396 100644 --- a/src/build/cli.v +++ b/src/build/cli.v @@ -14,16 +14,12 @@ pub: pub fn cmd() cli.Command { return cli.Command{ name: 'build' - required_args: 1 - usage: 'id' - description: 'Build the repository with the given ID.' + description: 'Run the build process.' execute: fn (cmd cli.Command) ? { config_file := cmd.flags.get_string('config-file') ? conf := env.load(config_file) ? - id := cmd.args[0].int() - - build(conf, id) ? + build(conf) ? } } } diff --git a/src/cron/daemon/build.v b/src/cron/daemon/build.v index aa08f9f..d107fd3 100644 --- a/src/cron/daemon/build.v +++ b/src/cron/daemon/build.v @@ -3,7 +3,6 @@ module daemon import time import sync.stdatomic import build -import os const ( build_empty = 0 @@ -78,20 +77,13 @@ fn (mut d Daemon) run_build(build_index int, sb ScheduledBuild) { // 0 means success, 1 means failure mut status := 0 - res := build.build_repo(d.client.address, d.client.api_key, d.builder_images.last(), - &sb.repo) or { + build.build_repo(d.client.address, d.client.api_key, d.builder_images.last(), &sb.repo) or { d.ldebug('build_repo error: $err.msg()') status = 1 - - build.BuildResult{} } if status == 0 { - d.linfo('finished build: $sb.repo.url $sb.repo.branch; uploading logs...') - - 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') } + d.linfo('finished build: $sb.repo.url $sb.repo.branch') } else { d.linfo('failed build: $sb.repo.url $sb.repo.branch') } diff --git a/src/docker/containers.v b/src/docker/containers.v index 2258f3b..d0f5a4d 100644 --- a/src/docker/containers.v +++ b/src/docker/containers.v @@ -2,7 +2,6 @@ module docker import json import net.urllib -import time struct Container { id string [json: Id] @@ -50,21 +49,13 @@ pub fn start_container(id string) ?bool { } struct ContainerInspect { -pub mut: +pub: state ContainerState [json: State] } struct ContainerState { pub: - 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] + running bool [json: Running] } // 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.') } - mut data := json.decode(ContainerInspect, res.text) ? - - 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 + return json.decode(ContainerInspect, res.text) or {} } // 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 } - -// 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() -}