Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 82 additions & 14 deletions src/api/handlers/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,19 @@
@ns.route('/')
@api.doc(responses={403: 'Not Authorized'})
class Builds(Resource):
@api.marshal_list_with(build_model)
def get(self, project_id):
'''
Returns builds
Returns build summaries with aggregated state, dates, commit and PR info.
Accepts: from, to, sha, branch, cronjob, state, size/build_limit
'''

build_from = request.args.get('from', None)
build_to = request.args.get('to', None)
sha = request.args.get('sha', None)
branch = request.args.get('branch', None)
cronjob = request.args.get('cronjob', None)
size = request.args.get('size', 10)
state = request.args.get('state', None)
size = request.args.get('build_limit', request.args.get('size', 10))

if cronjob == "true":
cronjob = True
Expand Down Expand Up @@ -62,21 +63,57 @@ def get(self, project_id):
if not build_from:
build_from = 0

#if build_to - build_from > 500:
# build_from = max(build_to - 500, 0)

p = g.db.execute_many_dict('''
SELECT b.id, b.build_number, b.restart_counter, b.is_cronjob
rows = g.db.execute_many_dict('''
SELECT
b.id,
b.build_number,
b.restart_counter,
b.is_cronjob,
CASE
WHEN bool_or(j.state IN ('queued', 'scheduled', 'running')
AND j.restarted IS NOT TRUE) THEN 'running'
WHEN bool_or(j.state = 'killed' AND j.restarted IS NOT TRUE) THEN 'killed'
WHEN bool_or(j.state = 'error' AND j.restarted IS NOT TRUE) THEN 'error'
WHEN bool_or(j.state = 'failure' AND j.restarted IS NOT TRUE) THEN 'failure'
WHEN bool_or(j.state = 'unstable' AND j.restarted IS NOT TRUE) THEN 'unstable'
ELSE 'finished'
END AS state,
to_char(min(j.start_date), 'YYYY-MM-DD HH24:MI:SS') AS start_date,
to_char(max(j.end_date), 'YYYY-MM-DD HH24:MI:SS') AS end_date,
c.id AS commit_id,
c.branch AS commit_branch,
c.author_name AS commit_author_name,
c.tag AS commit_tag,
c.url AS commit_url,
su.filename AS source_upload_filename,
pr.title AS pull_request_title,
pr.url AS pull_request_url
FROM build b
LEFT OUTER JOIN commit c
ON b.commit_id = c.id
INNER JOIN job j ON j.build_id = b.id
LEFT JOIN commit c ON b.commit_id = c.id
LEFT JOIN source_upload su ON b.source_upload_id = su.id
LEFT JOIN pull_request pr ON c.pull_request_id = pr.id
WHERE b.project_id = %(pid)s
AND b.build_number < %(to)s
AND b.build_number >= %(from)s
AND (%(sha)s IS NULL OR c.id = %(sha)s)
AND (%(branch)s IS NULL OR c.branch = %(branch)s)
AND (%(sha)s IS NULL OR c.id = %(sha)s)
AND (%(branch)s IS NULL OR c.branch = %(branch)s)
AND (%(cronjob)s IS NULL OR b.is_cronjob = %(cronjob)s)
ORDER BY build_number DESC, restart_counter DESC
GROUP BY b.id, b.build_number, b.restart_counter, b.is_cronjob,
c.id, c.branch, c.author_name, c.tag, c.url,
su.filename, pr.title, pr.url
HAVING (%(state)s IS NULL OR
CASE
WHEN bool_or(j.state IN ('queued', 'scheduled', 'running')
AND j.restarted IS NOT TRUE) THEN 'running'
WHEN bool_or(j.state = 'killed' AND j.restarted IS NOT TRUE) THEN 'killed'
WHEN bool_or(j.state = 'error' AND j.restarted IS NOT TRUE) THEN 'error'
WHEN bool_or(j.state = 'failure' AND j.restarted IS NOT TRUE) THEN 'failure'
WHEN bool_or(j.state = 'unstable' AND j.restarted IS NOT TRUE) THEN 'unstable'
ELSE 'finished'
END = %(state)s
)
ORDER BY b.build_number DESC, b.restart_counter DESC
LIMIT %(size)s
''', {
'pid': project_id,
Expand All @@ -85,10 +122,41 @@ def get(self, project_id):
'sha': sha,
'branch': branch,
'cronjob': cronjob,
'state': state,
'size': size,
})

return p
result = []
for b in rows:
o = {
'id': b['id'],
'build_number': b['build_number'],
'restart_counter': b['restart_counter'],
'is_cronjob': b['is_cronjob'],
'state': b['state'],
'start_date': b['start_date'],
'end_date': b['end_date'],
'commit': None,
'source_upload': None,
'pull_request': None,
}
if b['commit_id']:
o['commit'] = {
'id': b['commit_id'],
'branch': b['commit_branch'],
'author_name': b['commit_author_name'],
'tag': b['commit_tag'],
'url': b['commit_url'],
}
if b['source_upload_filename']:
o['source_upload'] = {'filename': b['source_upload_filename']}
if b['pull_request_title']:
o['pull_request'] = {
'title': b['pull_request_title'],
'url': b['pull_request_url'],
}
result.append(o)
return result

@ns.route('/<build_id>')
@api.doc(responses={403: 'Not Authorized'})
Expand Down
14 changes: 7 additions & 7 deletions src/dashboard-client/src/models/Project.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export default class Project {
}

loadBuilds (from, to, sha, branch, cronjob, buildLimit) {
let url = `projects/${this.id}/jobs/?from=${from}&to=${to}`
let url = `projects/${this.id}/builds/?from=${from}&to=${to}`

if (sha) {
url += `&sha=${sha}`
Expand All @@ -75,8 +75,8 @@ export default class Project {
}

return NewAPIService.get(url)
.then((jobs) => {
this._addJobs(jobs)
.then((builds) => {
store.commit('addBuilds', { projectId: this.id, builds })
})
.catch((err) => {
NotificationService.$emit('NOTIFICATION', new Notification(err))
Expand All @@ -86,7 +86,7 @@ export default class Project {
getBuild (number, restartCounter) {
const b = this._getBuild(number, restartCounter)

if (b) {
if (b && b.jobs.length > 0) {
return new Promise((resolve) => { resolve(b) })
}

Expand Down Expand Up @@ -178,9 +178,9 @@ export default class Project {
}

_loadJobs () {
return NewAPIService.get(`projects/${this.id}/jobs/`)
.then((response) => {
store.commit('addJobs', response)
return NewAPIService.get(`projects/${this.id}/builds/`)
.then((builds) => {
store.commit('addBuilds', { projectId: this.id, builds })
events.listenJobs(this)
})
}
Expand Down
35 changes: 35 additions & 0 deletions src/dashboard-client/src/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,40 @@ function handleJobUpdate (state, event) {
project._updateState()
}

function addBuilds (state, payload) {
const project = findProject(state, payload.projectId)
if (!project) return

for (const b of payload.builds) {
let build = findBuild(project, b.id)
if (!build) {
build = new Build(
b.id, b.build_number, b.restart_counter, b.is_cronjob,
b.commit || null, b.pull_request || null, project
)
let builds = [build]
for (let ex of project.builds) {
builds.push(ex)
}
builds = _(builds)
.chain()
.sortBy((x) => x.restartCounter)
.sortBy((x) => x.number)
.value()
.reverse()
project.builds = builds
}
build.state = b.state
build.startDate = b.start_date ? toDate(b.start_date) : null
build.endDate = b.end_date ? toDate(b.end_date) : null
if (b.commit) build.commit = b.commit
if (b.pull_request) build.pull_request = b.pull_request
}
if (project.builds.length > 0) {
project._updateState()
}
}

function addProjects (state, projects) {
for (const project of projects) {
let p = findProject(state, project.id)
Expand Down Expand Up @@ -353,6 +387,7 @@ function setAdminGlobalTokens (state, tokens) {
const mutations = {
addProjects,
addJobs,
addBuilds,
setSecrets,
setCronJobs,
setSSHKeys,
Expand Down
Loading