feat: queue one-time builds from CLI
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline failed Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details

Jef Roosens 2022-12-13 22:03:04 +01:00
parent 6a208dbe6c
commit b3e26e5ffe
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
4 changed files with 70 additions and 18 deletions

View File

@ -7,7 +7,7 @@ import datatypes { MinHeap }
import util
struct BuildJob {
pub:
pub mut:
// Time at which this build job was created/queued
created time.Time
// Next timestamp from which point this job is allowed to be executed
@ -64,6 +64,8 @@ pub struct InsertConfig {
target Target [required]
arch string [required]
single bool
force bool
now bool
}
// insert a new target's job into the queue for the given architecture. This
@ -75,20 +77,8 @@ pub fn (mut q BuildJobQueue) insert(input InsertConfig) ! {
q.queues[input.arch] = MinHeap<BuildJob>{}
}
ce := if input.target.schedule != '' {
parse_expression(input.target.schedule) or {
return error("Error while parsing cron expression '$input.target.schedule' (id $input.target.id): $err.msg()")
}
} else {
q.default_schedule
}
timestamp := ce.next_from_now()!
job := BuildJob{
mut job := BuildJob{
created: time.now()
timestamp: timestamp
ce: ce
single: input.single
config: BuildConfig{
target_id: input.target.id
@ -98,9 +88,25 @@ pub fn (mut q BuildJobQueue) insert(input InsertConfig) ! {
repo: input.target.repo
// TODO make this configurable
base_image: q.default_base_image
force: input.force
}
}
if !input.now {
ce := if input.target.schedule != '' {
parse_expression(input.target.schedule) or {
return error("Error while parsing cron expression '$input.target.schedule' (id $input.target.id): $err.msg()")
}
} else {
q.default_schedule
}
job.timestamp = ce.next_from_now()!
job.ce = ce
} else {
job.timestamp = time.now()
}
q.queues[input.arch].insert(job)
}
}
@ -198,8 +204,10 @@ pub fn (mut q BuildJobQueue) pop_n(arch string, n int) []BuildJob {
if job.timestamp < time.now() {
job = q.queues[arch].pop() or { break }
// TODO idem
q.reschedule(job, arch) or {}
if !job.single {
// TODO idem
q.reschedule(job, arch) or {}
}
out << job
} else {

View File

@ -1,6 +1,7 @@
module client
import build { BuildConfig }
import web.response { Response }
// poll_jobs requests a list of new build jobs from the server.
pub fn (c &Client) poll_jobs(arch string, max int) ![]BuildConfig {
@ -11,3 +12,13 @@ pub fn (c &Client) poll_jobs(arch string, max int) ![]BuildConfig {
return data.data
}
pub fn (c &Client) queue_job(target_id int, arch string, force bool) !Response<string> {
data := c.send_request<string>(.post, '/api/v1/jobs/queue', {
'target': target_id.str()
'arch': arch
'force': force.str()
})!
return data
}

View File

@ -188,12 +188,39 @@ pub fn cmd() cli.Command {
description: 'Build the target without checking whether it needs to be renewed.'
flag: cli.FlagType.bool
},
cli.Flag{
name: 'remote'
description: 'Schedule the build on the server instead of running it locally.'
flag: cli.FlagType.bool
},
cli.Flag{
name: 'arch'
description: 'Architecture to schedule build for. Required when using -remote.'
flag: cli.FlagType.string
},
]
execute: fn (cmd cli.Command) ! {
config_file := cmd.flags.get_string('config-file')!
conf := vconf.load<Config>(prefix: 'VIETER_', default_path: config_file)!
build(conf, cmd.args[0].int(), cmd.flags.get_bool('force')!)!
remote := cmd.flags.get_bool('remote')!
force := cmd.flags.get_bool('force')!
target_id := cmd.args[0].int()
if remote {
arch := cmd.flags.get_string('arch')!
if arch == '' {
println('When scheduling the build remotely, you have to specify an architecture.')
exit(1)
}
c := client.new(conf.address, conf.api_key)
res := c.queue_job(target_id, arch, force)!
println(res.message)
} else {
build(conf, target_id, force)!
}
}
},
]

View File

@ -30,11 +30,17 @@ fn (mut app App) v1_queue_job() web.Result {
return app.json(.bad_request, new_response('Missing arch query arg.'))
}
if arch == '' {
app.json(.bad_request, new_response('Empty arch query arg.'))
}
force := 'force' in app.query
target := app.db.get_target(target_id) or {
return app.json(.bad_request, new_response('Unknown target id.'))
}
app.job_queue.insert(target: target, arch: arch, single: true) or {
app.job_queue.insert(target: target, arch: arch, single: true, now: true, force: force) or {
return app.status(.internal_server_error)
}