From c0f58ddc77e2db11dcf7d54d07210934015aaffd Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Wed, 28 Dec 2022 16:09:00 +0100 Subject: [PATCH 1/9] feat(server): add metric collection --- src/server/api_metrics.v | 16 ++++++++++++++++ src/server/server.v | 2 ++ src/v.mod | 3 ++- src/web/web.v | 22 ++++++++++++++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/server/api_metrics.v diff --git a/src/server/api_metrics.v b/src/server/api_metrics.v new file mode 100644 index 0000000..af1b134 --- /dev/null +++ b/src/server/api_metrics.v @@ -0,0 +1,16 @@ +module server + +import metrics +import web + +['/api/v1/metrics'; get] +fn (mut app App) v1_metrics() web.Result { + mut exporter := metrics.new_prometheus_exporter([0.01, 0.05, 0.1, 0.5, 1, 100]) + exporter.load(app.collector) + + // TODO stream to connection instead + body := exporter.export_to_string() or { + return app.status(.internal_server_error) + } + return app.body(.ok, 'text/plain', body) +} diff --git a/src/server/server.v b/src/server/server.v index 178f657..9571b7b 100644 --- a/src/server/server.v +++ b/src/server/server.v @@ -8,6 +8,7 @@ import util import db import build { BuildJobQueue } import cron.expression +import metrics const ( log_file_name = 'vieter.log' @@ -107,6 +108,7 @@ pub fn server(conf Config) ! { repo: repo db: db job_queue: build.new_job_queue(global_ce, conf.base_image) + collector: metrics.new_default_collector() } app.init_job_queue() or { util.exit_with_message(1, 'Failed to inialize job queue: $err.msg()') diff --git a/src/v.mod b/src/v.mod index 710c976..461af6a 100644 --- a/src/v.mod +++ b/src/v.mod @@ -2,6 +2,7 @@ Module { dependencies: [ 'https://git.rustybever.be/vieter-v/conf', 'https://git.rustybever.be/vieter-v/docker', - 'https://git.rustybever.be/vieter-v/aur' + 'https://git.rustybever.be/vieter-v/aur', + 'https://git.rustybever.be/vieter-v/metrics' ] } diff --git a/src/web/web.v b/src/web/web.v index 565baff..95c91ed 100644 --- a/src/web/web.v +++ b/src/web/web.v @@ -11,6 +11,7 @@ import net.urllib import time import json import log +import metrics // The Context struct represents the Context which hold the HTTP request and response. // It has fields for the query, form, files. @@ -27,6 +28,8 @@ pub mut: conn &net.TcpConn = unsafe { nil } // Gives access to a shared logger object logger shared log.Log + // Used to collect metrics on the web server + collector &metrics.MetricsCollector // time.ticks() from start of web connection handle. // You can use it to determine how much time is spent on your request. page_gen_start i64 @@ -145,6 +148,14 @@ pub fn (ctx &Context) is_authenticated() bool { return false } +pub fn (mut ctx Context) body(status http.Status, content_type string, body string) Result { + ctx.status = status + ctx.content_type = content_type + ctx.send_response(body) + + return Result{} +} + // json HTTP_OK with json_s as payload with content-type `application/json` pub fn (mut ctx Context) json(status http.Status, j T) Result { ctx.status = status @@ -319,6 +330,16 @@ fn handle_conn(mut conn net.TcpConn, mut app T, routes map[string]Route) { app.logger.flush() } + // Record how long request took to process + labels := [ + ['method', app.req.method.str()]!, + ['path', app.req.url]!, + ['status', app.status.int().str()]! + ] + app.collector.counter_increment(name: 'http_requests_total', labels: labels) + app.collector.histogram_record(time.ticks() - app.page_gen_start, name: 'http_requests_time_ms', labels: labels) + /* app.collector.histogram_ */ + unsafe { free(app) } @@ -384,6 +405,7 @@ fn handle_conn(mut conn net.TcpConn, mut app T, routes map[string]Route) { static_mime_types: app.static_mime_types reader: reader logger: app.logger + collector: app.collector api_key: app.api_key } From 4ca2521937bc57bda4b6e45080dcd497687d8ec0 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Wed, 28 Dec 2022 17:39:45 +0100 Subject: [PATCH 2/9] feat(server): ability to disable metrics --- CHANGELOG.md | 2 ++ src/server/api_metrics.v | 13 ++++++++----- src/server/cli.v | 1 + src/server/server.v | 8 +++++++- src/web/web.v | 9 ++++++--- vieter.toml | 1 + 6 files changed, 25 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e4e228..72c5440 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://git.rustybever.be/vieter-v/vieter/src/branch/dev) +* Metrics endpoint for Prometheus integration + ## [0.5.0](https://git.rustybever.be/vieter-v/vieter/src/tag/0.5.0) ### Added diff --git a/src/server/api_metrics.v b/src/server/api_metrics.v index af1b134..8d6f654 100644 --- a/src/server/api_metrics.v +++ b/src/server/api_metrics.v @@ -3,14 +3,17 @@ module server import metrics import web -['/api/v1/metrics'; get] +// v1_metrics serves a Prometheus-compatible metrics endpoint. +['/api/v1/metrics'; get; markused] fn (mut app App) v1_metrics() web.Result { + if !app.conf.collect_metrics { + return app.status(.not_found) + } + mut exporter := metrics.new_prometheus_exporter([0.01, 0.05, 0.1, 0.5, 1, 100]) exporter.load(app.collector) - + // TODO stream to connection instead - body := exporter.export_to_string() or { - return app.status(.internal_server_error) - } + body := exporter.export_to_string() or { return app.status(.internal_server_error) } return app.body(.ok, 'text/plain', body) } diff --git a/src/server/cli.v b/src/server/cli.v index 21fb15e..9a8b144 100644 --- a/src/server/cli.v +++ b/src/server/cli.v @@ -15,6 +15,7 @@ pub: base_image string = 'archlinux:base-devel' max_log_age int [empty_default] log_removal_schedule string = '0 0' + collect_metrics bool [empty_default] } // cmd returns the cli submodule that handles starting the server diff --git a/src/server/server.v b/src/server/server.v index 9571b7b..76e7ad6 100644 --- a/src/server/server.v +++ b/src/server/server.v @@ -101,14 +101,20 @@ pub fn server(conf Config) ! { util.exit_with_message(1, 'Failed to initialize database: $err.msg()') } + collector := if conf.collect_metrics { + &metrics.MetricsCollector(metrics.new_default_collector()) + } else { + &metrics.MetricsCollector(metrics.new_null_collector()) + } + mut app := &App{ logger: logger api_key: conf.api_key conf: conf repo: repo db: db + collector: collector job_queue: build.new_job_queue(global_ce, conf.base_image) - collector: metrics.new_default_collector() } app.init_job_queue() or { util.exit_with_message(1, 'Failed to inialize job queue: $err.msg()') diff --git a/src/web/web.v b/src/web/web.v index 95c91ed..c44057e 100644 --- a/src/web/web.v +++ b/src/web/web.v @@ -148,6 +148,7 @@ pub fn (ctx &Context) is_authenticated() bool { return false } +// body sends the given body as an HTTP response. pub fn (mut ctx Context) body(status http.Status, content_type string, body string) Result { ctx.status = status ctx.content_type = content_type @@ -334,11 +335,13 @@ fn handle_conn(mut conn net.TcpConn, mut app T, routes map[string]Route) { labels := [ ['method', app.req.method.str()]!, ['path', app.req.url]!, - ['status', app.status.int().str()]! + ['status', app.status.int().str()]!, ] app.collector.counter_increment(name: 'http_requests_total', labels: labels) - app.collector.histogram_record(time.ticks() - app.page_gen_start, name: 'http_requests_time_ms', labels: labels) - /* app.collector.histogram_ */ + app.collector.histogram_record(time.ticks() - app.page_gen_start, + name: 'http_requests_time_ms' + labels: labels + ) unsafe { free(app) diff --git a/vieter.toml b/vieter.toml index 1f839f0..31eadc0 100644 --- a/vieter.toml +++ b/vieter.toml @@ -13,3 +13,4 @@ api_update_frequency = 2 image_rebuild_frequency = 1 max_concurrent_builds = 3 max_log_age = 64 +collect_metrics = true From 4ed4ef4a27b904180aa103b20c94fc32d2c87920 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Tue, 3 Jan 2023 09:29:55 +0100 Subject: [PATCH 3/9] chore: generate man pages using debug build --- .woodpecker/man.yml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/.woodpecker/man.yml b/.woodpecker/man.yml index 8c6ca06..8102443 100644 --- a/.woodpecker/man.yml +++ b/.woodpecker/man.yml @@ -8,15 +8,21 @@ branches: depends_on: - build -skip_clone: true - pipeline: - generate: + install-modules: image: *vlang_image pull: true commands: - - curl -o vieter -L "https://s3.rustybever.be/vieter/commits/$CI_COMMIT_SHA/vieter-linux-amd64" - - chmod +x vieter + - export VMODULES=$PWD/.vmodules + - 'cd src && v install' + + generate: + image: *vlang_image + commands: + # - curl -o vieter -L "https://s3.rustybever.be/vieter/commits/$CI_COMMIT_SHA/vieter-linux-amd64" + # - chmod +x vieter + - export VMODULES=$PWD/.vmodules + - make - ./vieter man man - cd man From 60d5fb77e04a697f07cfc8c5e42b17b4b3e641d4 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Wed, 4 Jan 2023 09:19:02 +0100 Subject: [PATCH 4/9] feat(metrics): add prefix; use base unit for time --- src/server/api_metrics.v | 5 +++-- src/web/web.v | 8 ++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/server/api_metrics.v b/src/server/api_metrics.v index 8d6f654..cde4437 100644 --- a/src/server/api_metrics.v +++ b/src/server/api_metrics.v @@ -10,8 +10,9 @@ fn (mut app App) v1_metrics() web.Result { return app.status(.not_found) } - mut exporter := metrics.new_prometheus_exporter([0.01, 0.05, 0.1, 0.5, 1, 100]) - exporter.load(app.collector) + mut exporter := metrics.new_prometheus_exporter([0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, + 10]) + exporter.load('vieter_', app.collector) // TODO stream to connection instead body := exporter.export_to_string() or { return app.status(.internal_server_error) } diff --git a/src/web/web.v b/src/web/web.v index c44057e..f0f3523 100644 --- a/src/web/web.v +++ b/src/web/web.v @@ -335,11 +335,15 @@ fn handle_conn(mut conn net.TcpConn, mut app T, routes map[string]Route) { labels := [ ['method', app.req.method.str()]!, ['path', app.req.url]!, + // Not all methods properly set this value yet I think ['status', app.status.int().str()]!, ] app.collector.counter_increment(name: 'http_requests_total', labels: labels) - app.collector.histogram_record(time.ticks() - app.page_gen_start, - name: 'http_requests_time_ms' + // Prometheus prefers metrics containing base units, as defined here + // https://prometheus.io/docs/practices/naming/ + app.collector.histogram_record(f64(time.ticks() - app.page_gen_start) / 1000, + + name: 'http_requests_duration_seconds' labels: labels ) From f8f611f5c55ff2cbcabe5d6fdaf2c396e11c6dc1 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Sat, 31 Dec 2022 10:19:59 +0100 Subject: [PATCH 5/9] feat(api): add search query to targets --- src/console/targets/targets.v | 11 ++++++++ src/db/targets.v | 53 +++++++++++++++++++++++++++-------- src/models/targets.v | 1 + src/server/api_targets.v | 3 ++ vieter.toml | 2 +- 5 files changed, 58 insertions(+), 12 deletions(-) diff --git a/src/console/targets/targets.v b/src/console/targets/targets.v index 3c0d755..dfc3792 100644 --- a/src/console/targets/targets.v +++ b/src/console/targets/targets.v @@ -40,6 +40,12 @@ pub fn cmd() cli.Command { description: 'Only return targets that publish to this repo.' flag: cli.FlagType.string }, + cli.Flag{ + name: 'query' + abbrev: 'q' + description: 'Search string to filter targets by.' + flag: cli.FlagType.string + }, ] execute: fn (cmd cli.Command) ! { config_file := cmd.flags.get_string('config-file')! @@ -62,6 +68,11 @@ pub fn cmd() cli.Command { filter.repo = repo } + query := cmd.flags.get_string('query')! + if query != '' { + filter.query = query + } + raw := cmd.flags.get_bool('raw')! list(conf, filter, raw)! diff --git a/src/db/targets.v b/src/db/targets.v index 41e56df..fba227e 100644 --- a/src/db/targets.v +++ b/src/db/targets.v @@ -1,24 +1,55 @@ module db import models { Target, TargetArch, TargetFilter } +import math // 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 + window_size := 32 + + mut out := []Target{} + mut targets := []Target{cap: window_size} + + mut offset := 0 + mut filtered_offset := u64(0) + + for out.len < filter.limit { + targets = sql db.conn { + select from Target order by id limit window_size offset offset + } + offset += targets.len + + if targets.len == 0 { + break } - return res + if filter.repo != '' { + targets = targets.filter(it.repo == filter.repo) + } + + if filter.query != '' { + targets = targets.filter(it.url.contains(filter.query) || it.path.contains(filter.query) + || it.branch.contains(filter.query)) + } + + if filtered_offset > filter.offset { + end_index := math.min(filter.limit - u64(out.len), u64(targets.len)) + + out << targets[0..end_index] + } + // We start counting targets in the middle of the current window + else if filtered_offset + u64(targets.len) > filter.offset { + start_index := filter.offset - filtered_offset + end_index := start_index + + math.min(filter.limit - u64(out.len), u64(targets.len) - start_index) + + out << targets[start_index..end_index] + } + + filtered_offset += u64(targets.len) } - res := sql db.conn { - select from Target order by id limit filter.limit offset filter.offset - } - - return res + return out } // get_target tries to return a specific target. diff --git a/src/models/targets.v b/src/models/targets.v index af3cb0d..612f7fa 100644 --- a/src/models/targets.v +++ b/src/models/targets.v @@ -73,4 +73,5 @@ pub mut: limit u64 = 25 offset u64 repo string + query string } diff --git a/src/server/api_targets.v b/src/server/api_targets.v index 4bb7d12..f04fdae 100644 --- a/src/server/api_targets.v +++ b/src/server/api_targets.v @@ -81,3 +81,6 @@ fn (mut app App) v1_patch_target(id int) web.Result { return app.json(.ok, new_data_response(target)) } + +['/api/v1/targets/search'; auth; get; markused] +fn (mut app App) v1_search_targets() diff --git a/vieter.toml b/vieter.toml index 31eadc0..7744a56 100644 --- a/vieter.toml +++ b/vieter.toml @@ -12,5 +12,5 @@ address = "http://localhost:8000" api_update_frequency = 2 image_rebuild_frequency = 1 max_concurrent_builds = 3 -max_log_age = 64 +# max_log_age = 64 collect_metrics = true From c9edb55abcf2bbee5848a5c27dc2cfd662325671 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Sat, 31 Dec 2022 16:10:47 +0100 Subject: [PATCH 6/9] feat(db): implemented iterator over targets --- CHANGELOG.md | 3 + src/db/targets.v | 50 ---------------- src/db/targets_iter.v | 123 +++++++++++++++++++++++++++++++++++++++ src/server/api_targets.v | 7 +-- src/server/server.v | 13 +---- 5 files changed, 130 insertions(+), 66 deletions(-) create mode 100644 src/db/targets_iter.v diff --git a/CHANGELOG.md b/CHANGELOG.md index 72c5440..2ab14e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://git.rustybever.be/vieter-v/vieter/src/branch/dev) +### Added + * Metrics endpoint for Prometheus integration +* Search in list of targets using API & CLI ## [0.5.0](https://git.rustybever.be/vieter-v/vieter/src/tag/0.5.0) diff --git a/src/db/targets.v b/src/db/targets.v index fba227e..e022a70 100644 --- a/src/db/targets.v +++ b/src/db/targets.v @@ -1,56 +1,6 @@ module db import models { Target, TargetArch, TargetFilter } -import math - -// get_targets returns all targets in the database. -pub fn (db &VieterDb) get_targets(filter TargetFilter) []Target { - window_size := 32 - - mut out := []Target{} - mut targets := []Target{cap: window_size} - - mut offset := 0 - mut filtered_offset := u64(0) - - for out.len < filter.limit { - targets = sql db.conn { - select from Target order by id limit window_size offset offset - } - offset += targets.len - - if targets.len == 0 { - break - } - - if filter.repo != '' { - targets = targets.filter(it.repo == filter.repo) - } - - if filter.query != '' { - targets = targets.filter(it.url.contains(filter.query) || it.path.contains(filter.query) - || it.branch.contains(filter.query)) - } - - if filtered_offset > filter.offset { - end_index := math.min(filter.limit - u64(out.len), u64(targets.len)) - - out << targets[0..end_index] - } - // We start counting targets in the middle of the current window - else if filtered_offset + u64(targets.len) > filter.offset { - start_index := filter.offset - filtered_offset - end_index := start_index + - math.min(filter.limit - u64(out.len), u64(targets.len) - start_index) - - out << targets[start_index..end_index] - } - - filtered_offset += u64(targets.len) - } - - return out -} // get_target tries to return a specific target. pub fn (db &VieterDb) get_target(target_id int) ?Target { diff --git a/src/db/targets_iter.v b/src/db/targets_iter.v new file mode 100644 index 0000000..16b1080 --- /dev/null +++ b/src/db/targets_iter.v @@ -0,0 +1,123 @@ +module db + +import models { Target, TargetFilter } +import sqlite + +// Iterator providing a filtered view into the list of targets currently stored +// in the database. It replaces functionality usually performed in the database +// using SQL queries that can't currently be used due to missing stuff in V's +// ORM. +pub struct TargetsIterator { + conn sqlite.DB + filter TargetFilter + window_size int = 32 +mut: + window []Target + window_index u64 + // Offset in entire list of unfiltered targets + offset int + // Offset in filtered list of targets + filtered_offset u64 + started bool + done bool +} + +// targets returns an iterator allowing filtered access to the list of targets. +pub fn (db &VieterDb) targets(filter TargetFilter) TargetsIterator { + window_size := 32 + + return TargetsIterator{ + conn: db.conn + filter: filter + window: []Target{cap: window_size} + window_size: window_size + } +} + +// advance_window moves the sliding window over the filtered list of targets +// until it either reaches the end of the list of targets, or has encountered a +// non-empty window. +fn (mut ti TargetsIterator) advance_window() { + for { + ti.window = sql ti.conn { + select from Target order by id limit ti.window_size offset ti.offset + } + ti.offset += ti.window.len + + if ti.window.len == 0 { + ti.done = true + + return + } + + if ti.filter.repo != '' { + ti.window = ti.window.filter(it.repo == ti.filter.repo) + } + + if ti.filter.query != '' { + ti.window = ti.window.filter(it.url.contains(ti.filter.query) + || it.path.contains(ti.filter.query) || it.branch.contains(ti.filter.query)) + } + + if ti.window.len > 0 { + break + } + } +} + +// next returns the next target, if possible. +pub fn (mut ti TargetsIterator) next() ?Target { + if ti.done { + return none + } + + // The first call to `next` will cause the sliding window to move to where the requested offset starts + if !ti.started { + ti.advance_window() + + // Skip all matched targets until the requested offset + for !ti.done && ti.filtered_offset + u64(ti.window.len) <= ti.filter.offset { + ti.filtered_offset += u64(ti.window.len) + ti.advance_window() + } + + if ti.done { + return none + } + + left_inside_window := ti.filter.offset - ti.filtered_offset + ti.window_index = left_inside_window + ti.filtered_offset += left_inside_window + + ti.started = true + } + + return_value := ti.window[ti.window_index] + + ti.window_index++ + ti.filtered_offset++ + + // Next call will be past the requested offset + if ti.filter.limit > 0 && ti.filtered_offset == ti.filter.offset + ti.filter.limit { + ti.done = true + } + + // Ensure the next call has a new valid window + if ti.window_index == u64(ti.window.len) { + ti.advance_window() + ti.window_index = 0 + } + + return return_value +} + +// collect consumes the entire iterator & returns the result as an array. +pub fn (mut ti TargetsIterator) collect() []Target { + mut out := []Target{} + + for t in ti { + out << t + } + + return out +} diff --git a/src/server/api_targets.v b/src/server/api_targets.v index f04fdae..f47467a 100644 --- a/src/server/api_targets.v +++ b/src/server/api_targets.v @@ -11,9 +11,9 @@ fn (mut app App) v1_get_targets() web.Result { filter := models.from_params(app.query) or { return app.json(.bad_request, new_response('Invalid query parameters.')) } - targets := app.db.get_targets(filter) + mut iter := app.db.targets(filter) - return app.json(.ok, new_data_response(targets)) + return app.json(.ok, new_data_response(iter.collect())) } // v1_get_single_target returns the information for a single target. @@ -81,6 +81,3 @@ fn (mut app App) v1_patch_target(id int) web.Result { return app.json(.ok, new_data_response(target)) } - -['/api/v1/targets/search'; auth; get; markused] -fn (mut app App) v1_search_targets() diff --git a/src/server/server.v b/src/server/server.v index 76e7ad6..5dd1a20 100644 --- a/src/server/server.v +++ b/src/server/server.v @@ -31,17 +31,8 @@ pub mut: // init_job_queue populates a fresh job queue with all the targets currently // stored in the database. fn (mut app App) init_job_queue() ! { - // Initialize build queues - mut targets := app.db.get_targets(limit: 25) - mut i := u64(0) - - for targets.len > 0 { - for target in targets { - app.job_queue.insert_all(target)! - } - - i += 25 - targets = app.db.get_targets(limit: 25, offset: i) + for target in app.db.targets(limit: 0) { + app.job_queue.insert_all(target)! } } From b0fe6b73846a8df7564c3a0c1f0b99d1fad860f4 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Mon, 2 Jan 2023 16:10:57 +0100 Subject: [PATCH 7/9] chore: ran formatter --- src/db/targets.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/targets.v b/src/db/targets.v index e022a70..2644f49 100644 --- a/src/db/targets.v +++ b/src/db/targets.v @@ -1,6 +1,6 @@ module db -import models { Target, TargetArch, TargetFilter } +import models { Target, TargetArch } // get_target tries to return a specific target. pub fn (db &VieterDb) get_target(target_id int) ?Target { From 39a026fdb3ff01aafb40cb19d7f825cd4c26e102 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Mon, 2 Jan 2023 16:38:43 +0100 Subject: [PATCH 8/9] feat: add filtering of targets by arch --- CHANGELOG.md | 1 + src/console/targets/targets.v | 10 ++++++++++ src/db/targets_iter.v | 4 ++++ src/models/targets.v | 1 + 4 files changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ab14e8..be5f445 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Metrics endpoint for Prometheus integration * Search in list of targets using API & CLI +* Allow filtering targets by arch value ## [0.5.0](https://git.rustybever.be/vieter-v/vieter/src/tag/0.5.0) diff --git a/src/console/targets/targets.v b/src/console/targets/targets.v index dfc3792..6152a53 100644 --- a/src/console/targets/targets.v +++ b/src/console/targets/targets.v @@ -46,6 +46,11 @@ pub fn cmd() cli.Command { description: 'Search string to filter targets by.' flag: cli.FlagType.string }, + cli.Flag{ + name: 'arch' + description: 'Only list targets that build for this arch.' + flag: cli.FlagType.string + }, ] execute: fn (cmd cli.Command) ! { config_file := cmd.flags.get_string('config-file')! @@ -73,6 +78,11 @@ pub fn cmd() cli.Command { filter.query = query } + arch := cmd.flags.get_string('arch')! + if arch != '' { + filter.arch = arch + } + raw := cmd.flags.get_bool('raw')! list(conf, filter, raw)! diff --git a/src/db/targets_iter.v b/src/db/targets_iter.v index 16b1080..190d906 100644 --- a/src/db/targets_iter.v +++ b/src/db/targets_iter.v @@ -54,6 +54,10 @@ fn (mut ti TargetsIterator) advance_window() { ti.window = ti.window.filter(it.repo == ti.filter.repo) } + if ti.filter.arch != '' { + ti.window = ti.window.filter(it.arch.any(it.value == ti.filter.arch)) + } + if ti.filter.query != '' { ti.window = ti.window.filter(it.url.contains(ti.filter.query) || it.path.contains(ti.filter.query) || it.branch.contains(ti.filter.query)) diff --git a/src/models/targets.v b/src/models/targets.v index 612f7fa..a0c88d0 100644 --- a/src/models/targets.v +++ b/src/models/targets.v @@ -74,4 +74,5 @@ pub mut: offset u64 repo string query string + arch string } From 398e2bd9ebc89b3ae1e2ac20f7757fb8d376ddbd Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Wed, 4 Jan 2023 14:37:41 +0100 Subject: [PATCH 9/9] chore: update docs; final read --- docs/api/source/includes/_targets.md | 2 ++ src/db/targets_iter.v | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/api/source/includes/_targets.md b/docs/api/source/includes/_targets.md index b71da84..1a5f3e0 100644 --- a/docs/api/source/includes/_targets.md +++ b/docs/api/source/includes/_targets.md @@ -55,6 +55,8 @@ Parameter | Description limit | Maximum amount of results to return. offset | Offset of results. repo | Limit results to targets that publish to the given repo. +query | Only return targets that have this substring in their URL, path or branch. +arch | Only return targets that publish to this arch. ## Get specific target diff --git a/src/db/targets_iter.v b/src/db/targets_iter.v index 190d906..081de1f 100644 --- a/src/db/targets_iter.v +++ b/src/db/targets_iter.v @@ -63,6 +63,7 @@ fn (mut ti TargetsIterator) advance_window() { || it.path.contains(ti.filter.query) || it.branch.contains(ti.filter.query)) } + // We break out of the loop once we found a non-empty window if ti.window.len > 0 { break } @@ -75,7 +76,8 @@ pub fn (mut ti TargetsIterator) next() ?Target { return none } - // The first call to `next` will cause the sliding window to move to where the requested offset starts + // The first call to `next` will cause the sliding window to move to where + // the requested offset starts if !ti.started { ti.advance_window()