aboutsummaryrefslogtreecommitdiff
path: root/guix-qa-frontpage
diff options
context:
space:
mode:
Diffstat (limited to 'guix-qa-frontpage')
-rw-r--r--guix-qa-frontpage/branch.scm322
-rw-r--r--guix-qa-frontpage/database.scm162
-rw-r--r--guix-qa-frontpage/debbugs.scm1
-rw-r--r--guix-qa-frontpage/derivation-changes.scm116
-rw-r--r--guix-qa-frontpage/git-repository.scm25
-rw-r--r--guix-qa-frontpage/guix-data-service.scm286
-rw-r--r--guix-qa-frontpage/issue.scm158
-rw-r--r--guix-qa-frontpage/manage-builds.scm583
-rw-r--r--guix-qa-frontpage/manage-patch-branches.scm121
-rw-r--r--guix-qa-frontpage/mumi.scm184
-rw-r--r--guix-qa-frontpage/patchwork.scm122
-rw-r--r--guix-qa-frontpage/server.scm229
-rw-r--r--guix-qa-frontpage/utils.scm213
-rw-r--r--guix-qa-frontpage/view/branch.scm12
-rw-r--r--guix-qa-frontpage/view/branches.scm22
-rw-r--r--guix-qa-frontpage/view/home.scm47
-rw-r--r--guix-qa-frontpage/view/issue.scm9
-rw-r--r--guix-qa-frontpage/view/patches.scm46
-rw-r--r--guix-qa-frontpage/view/shared.scm107
-rw-r--r--guix-qa-frontpage/view/util.scm17
20 files changed, 1553 insertions, 1229 deletions
diff --git a/guix-qa-frontpage/branch.scm b/guix-qa-frontpage/branch.scm
index 5874120..6276476 100644
--- a/guix-qa-frontpage/branch.scm
+++ b/guix-qa-frontpage/branch.scm
@@ -23,11 +23,17 @@
#:use-module (ice-9 match)
#:use-module (ice-9 regex)
#:use-module (ice-9 threads)
+ #:use-module (web uri)
#:use-module (prometheus)
#:use-module ((guix-build-coordinator utils)
#:select (with-time-logging))
+ #:use-module ((guix-build-coordinator utils fibers)
+ #:select (retry-on-error))
#:use-module ((guix build syscalls)
#:select (set-thread-name))
+ #:use-module (fibers)
+ #:use-module (knots non-blocking)
+ #:use-module (guix-qa-frontpage utils)
#:use-module (guix-qa-frontpage mumi)
#:use-module (guix-qa-frontpage git-repository)
#:use-module (guix-qa-frontpage guix-data-service)
@@ -36,12 +42,14 @@
#:use-module (guix-qa-frontpage manage-builds)
#:export (list-non-master-branches
+ branch-derivation-changes-data
+ branch-derivation-changes-data/all-systems
branch-data
master-branch-data
get-systems-with-low-substitute-availability
- start-refresh-non-patch-branches-data-thread))
+ start-refresh-non-patch-branches-data-fiber))
(define (list-non-master-branches)
(define (issue-title->branch title)
@@ -49,7 +57,7 @@
(lambda (m)
(match:substring m 1))))
- (define merge-issues-by-branch
+ (define (merge-issues-by-branch)
(filter-map
(lambda (issue)
(let ((branch (issue-title->branch
@@ -60,109 +68,149 @@
(cons branch
`(("issue_number" . ,issue-number)
("issue_date" . ,(assoc-ref issue "date"))
- ("blocked_by" . ,(assoc-ref issue "blocked_by")))))))
+ ("blocked_by"
+ . ,(list->vector
+ (map (lambda (issue)
+ (assoc-ref issue "number"))
+ (or (and=> (assoc-ref issue "blocked_by")
+ vector->list)
+ '())))))))))
(vector->list
(mumi-search-issues
;; TODO: subject: doesn't seem to work for issues where the
;; subject/title has changed
"\"Request for merging\" is:open"))))
- (let ((branches
- (map
- (lambda (branch)
- (let ((name (assoc-ref branch "name")))
- (cons name
- (append
- (or (assoc-ref merge-issues-by-branch name)
- '())
- (alist-delete "name" branch)))))
- (remove
- (lambda (branch)
- (or (string=? (assoc-ref branch "name")
- "master")
- (string-prefix? "version-"
- (assoc-ref branch "name"))
- (string=? (assoc-ref branch "commit")
- "")))
- (list-branches
- (list-branches-url 2))))))
- (let* ((initial-ordered-branches
- (stable-sort
- branches
- (lambda (a b)
- (let ((a-has-issue
- (->bool (assoc-ref (cdr a) "issue_number")))
- (b-has-issue
- (->bool (assoc-ref (cdr b) "issue_number"))))
- (if (and a-has-issue b-has-issue)
- (let ((a-date
- (assoc-ref (cdr a) "issue_date"))
- (b-date
- (assoc-ref (cdr b) "issue_date")))
- (string<? a-date b-date))
- a-has-issue)))))
- (initial-ordering-index-by-branch
- (map (lambda (index branch)
- (cons (car branch) index))
- (iota (length initial-ordered-branches))
- initial-ordered-branches))
- (initial-ordering-index-by-issue-number
- (filter-map
- (lambda (index branch)
- (and=> (assoc-ref (cdr branch) "issue_number")
- (lambda (issue-number)
- (cons issue-number index))))
- (iota (length initial-ordered-branches))
- initial-ordered-branches)))
-
- ;; The idea with issues blocking others is to create a linked list,
- ;; however I think it's possible to have a loop in the blocking directed
- ;; graph, so try to not completely fail if this is the case.
- (stable-sort
- initial-ordered-branches
- (lambda (a b)
- (let ((a-initial-ordering-index
- (assq-ref initial-ordering-index-by-branch
- (car a)))
- (b-initial-ordering-index
- (assq-ref initial-ordering-index-by-branch
- (car b)))
-
- (a-blocked-by
- (map (lambda (issue)
- (assoc-ref issue "number"))
- (or (and=> (assoc-ref (cdr a) "blocked_by")
- vector->list)
- '())))
- (b-blocked-by
- (map (lambda (issue)
- (assoc-ref issue "number"))
- (or (and=> (assoc-ref (cdr b) "blocked_by")
- vector->list)
- '()))))
- (<
- (if (null? a-blocked-by)
- a-initial-ordering-index
- (let ((ordering-indexes
- (filter-map
- (lambda (blocking-issue)
- (assq-ref initial-ordering-index-by-issue-number
- blocking-issue))
- a-blocked-by)))
- (if (null? ordering-indexes)
- a-initial-ordering-index
- (apply max ordering-indexes))))
- (if (null? b-blocked-by)
- b-initial-ordering-index
- (let ((ordering-indexes
- (filter-map
- (lambda (blocking-issue)
- (assq-ref initial-ordering-index-by-issue-number
- blocking-issue))
- b-blocked-by)))
- (if (null? ordering-indexes)
- b-initial-ordering-index
- (apply max ordering-indexes)))))))))))
+ (with-exception-handler
+ (lambda (exn)
+ (simple-format #t "exception listing non master branches: ~A\n" exn)
+ `((exception . ,(simple-format #f "~A" exn))))
+ (lambda ()
+ (with-throw-handler #t
+ (lambda ()
+ (let* ((merge-issues
+ (merge-issues-by-branch))
+ (branches
+ (map
+ (lambda (branch)
+ (let ((name (assoc-ref branch "name")))
+ (cons name
+ (append
+ (or (assoc-ref merge-issues name)
+ '())
+ (alist-delete "name" branch)))))
+ (remove
+ (lambda (branch)
+ (or (string=? (assoc-ref branch "name")
+ "master")
+ (string-prefix? "version-"
+ (assoc-ref branch "name"))
+ (string-prefix? "wip-"
+ (assoc-ref branch "name"))
+ (string=? (assoc-ref branch "commit")
+ "")))
+ (get-git-remote-branches "origin")))))
+ (let* ((initial-ordered-branches
+ (stable-sort
+ branches
+ (lambda (a b)
+ (let ((a-has-issue
+ (->bool (assoc-ref (cdr a) "issue_number")))
+ (b-has-issue
+ (->bool (assoc-ref (cdr b) "issue_number"))))
+ (if (and a-has-issue b-has-issue)
+ (let ((a-date
+ (assoc-ref (cdr a) "issue_date"))
+ (b-date
+ (assoc-ref (cdr b) "issue_date")))
+ (string<? a-date b-date))
+ a-has-issue)))))
+ (initial-ordering-index-by-branch
+ (map (lambda (index branch)
+ (cons (car branch) index))
+ (iota (length initial-ordered-branches))
+ initial-ordered-branches))
+ (initial-ordering-index-by-issue-number
+ (filter-map
+ (lambda (index branch)
+ (and=> (assoc-ref (cdr branch) "issue_number")
+ (lambda (issue-number)
+ (cons issue-number index))))
+ (iota (length initial-ordered-branches))
+ initial-ordered-branches)))
+
+ ;; The idea with issues blocking others is to create a linked
+ ;; list, however I think it's possible to have a loop in the
+ ;; blocking directed graph, so try to not completely fail if
+ ;; this is the case.
+ (stable-sort
+ initial-ordered-branches
+ (lambda (a b)
+ (let ((a-initial-ordering-index
+ (assq-ref initial-ordering-index-by-branch
+ (car a)))
+ (b-initial-ordering-index
+ (assq-ref initial-ordering-index-by-branch
+ (car b)))
+ (a-blocked-by
+ (vector->list
+ (or (assoc-ref (cdr a) "blocked_by") #())))
+ (b-blocked-by
+ (vector->list
+ (or (assoc-ref (cdr b) "blocked_by") #()))))
+ (<
+ (if (null? a-blocked-by)
+ a-initial-ordering-index
+ (let ((ordering-indexes
+ (filter-map
+ (lambda (blocking-issue)
+ (and=>
+ (assq-ref
+ initial-ordering-index-by-issue-number
+ blocking-issue)
+ 1+))
+ a-blocked-by)))
+ (if (null? ordering-indexes)
+ a-initial-ordering-index
+ (apply max ordering-indexes))))
+ (if (null? b-blocked-by)
+ b-initial-ordering-index
+ (let ((ordering-indexes
+ (filter-map
+ (lambda (blocking-issue)
+ (and=>
+ (assq-ref
+ initial-ordering-index-by-issue-number
+ blocking-issue)
+ 1+))
+ b-blocked-by)))
+ (if (null? ordering-indexes)
+ b-initial-ordering-index
+ (apply max ordering-indexes)))))))))))
+ (lambda args
+ (display (backtrace) (current-error-port))
+ (newline (current-error-port)))))
+ #:unwind? #t))
+
+(define (branch-derivation-changes-data revisions system)
+ (with-exception-handler guix-data-service-error->sexp
+ (lambda ()
+ (compare-package-derivations
+ (compare-package-derivations-url
+ revisions
+ #:systems (list system))))
+ #:unwind? #t
+ #:unwind-for-type &guix-data-service-error))
+
+(define (branch-derivation-changes-data/all-systems revisions)
+ (with-exception-handler guix-data-service-error->sexp
+ (lambda ()
+ (compare-package-derivations
+ (compare-package-derivations-url
+ revisions
+ #:systems %systems-to-submit-builds-for)))
+ #:unwind? #t
+ #:unwind-for-type &guix-data-service-error))
(define* (branch-data branch-name)
(define branch-commit
@@ -199,24 +247,20 @@
#:unwind? #t
#:unwind-for-type &guix-data-service-error))
- (derivation-changes-data
- (with-exception-handler guix-data-service-error->sexp
- (lambda ()
- (let ((data
- (compare-package-derivations
- (compare-package-derivations-url
- revisions
- #:systems %systems-to-submit-builds-for))))
-
- (with-throw-handler #t
- (lambda ()
- (derivation-changes
- data
- %systems-to-submit-builds-for))
- (lambda _
- (backtrace)))))
- #:unwind? #t
- #:unwind-for-type &guix-data-service-error))
+ (derivation-changes-counts
+ (append-map
+ (lambda (system)
+ (let ((derivation-changes-data
+ (retry-on-error
+ (lambda ()
+ (branch-derivation-changes-data revisions system))
+ #:times 1)))
+ (if (assq-ref derivation-changes-data 'exception)
+ derivation-changes-data
+ (derivation-changes-counts
+ derivation-changes-data
+ (list system)))))
+ %systems-to-submit-builds-for))
(substitute-availability
(with-exception-handler guix-data-service-error->sexp
@@ -232,7 +276,7 @@
(package-reproducibility-url branch-commit))))
(values
revisions
- derivation-changes-data
+ derivation-changes-counts
substitute-availability
package-reproducibility
up-to-date-with-master?))
@@ -242,11 +286,19 @@
(define* (master-branch-data)
(let* ((substitute-availability
(package-substitute-availability
- "https://data.qa.guix.gnu.org/repository/2/branch/master/latest-processed-revision/package-substitute-availability.json"))
+ (string-append
+ %data-service-url-base
+ "/repository/"
+ (number->string %data-service-guix-repository-id)
+ "/branch/master/latest-processed-revision/package-substitute-availability.json")))
(package-reproducibility
(guix-data-service-request
- "https://data.qa.guix.gnu.org/repository/2/branch/master/latest-processed-revision/package-reproducibility.json"))
+ (string-append
+ %data-service-url-base
+ "/repository/"
+ (number->string %data-service-guix-repository-id)
+ "/branch/master/latest-processed-revision/package-reproducibility.json")))
(systems-with-low-substitute-availability
(get-systems-with-low-substitute-availability
@@ -285,15 +337,17 @@
(lambda (details)
;; TODO: Don't hardcode this
(string=?
- "https://bordeaux.guix.gnu.org"
- (assoc-ref
- (assoc-ref details "server")
- "url")))
+ "bordeaux.guix.gnu.org"
+ (uri-host
+ (string->uri
+ (assoc-ref
+ (assoc-ref details "server")
+ "url")))))
(vector->list substitute-availability))
"availability"))))
-(define (start-refresh-non-patch-branches-data-thread database
- metrics-registry)
+(define (start-refresh-non-patch-branches-data-fiber database
+ metrics-registry)
(define frequency
(* 30 60))
@@ -345,7 +399,9 @@
(define (refresh-data)
(simple-format (current-error-port)
"refreshing non-patch branches data...\n")
- (update-repository!)
+ (non-blocking
+ (lambda ()
+ (update-repository!)))
(let ((branches
(with-sqlite-cache
@@ -359,11 +415,10 @@
(string-prefix? "version-"
(assoc-ref branch "name"))))
(list-branches
- (list-branches-url 2))))
+ (list-branches-url %data-service-guix-repository-id))))
#:ttl 0)))
- (n-par-for-each
- 1
+ (for-each
(lambda (branch)
(let ((branch-name
(assoc-ref branch "name")))
@@ -421,13 +476,8 @@
"master"
master-branch-substitute-availability)))
- (call-with-new-thread
+ (spawn-fiber
(lambda ()
- (catch 'system-error
- (lambda ()
- (set-thread-name "branch data refresh"))
- (const #t))
-
(while #t
(let ((start-time (current-time)))
(with-exception-handler
diff --git a/guix-qa-frontpage/database.scm b/guix-qa-frontpage/database.scm
index 5649c36..06ce3bd 100644
--- a/guix-qa-frontpage/database.scm
+++ b/guix-qa-frontpage/database.scm
@@ -28,15 +28,15 @@
#:use-module (sqlite3)
#:use-module (fibers)
#:use-module (prometheus)
+ #:use-module (knots queue)
+ #:use-module (knots thread-pool)
#:use-module (guix narinfo)
#:use-module (guix derivations)
#:use-module ((guix-build-coordinator utils)
#:select (log-delay
call-with-delay-logging))
#:use-module ((guix-build-coordinator utils fibers)
- #:select (retry-on-error
- make-worker-thread-channel
- call-with-worker-thread))
+ #:select (retry-on-error))
#:use-module (guix-qa-frontpage guix-data-service)
#:export (setup-database
@@ -57,13 +57,16 @@
delete-create-branch-for-issue-log))
(define-record-type <database>
- (make-database database-file reader-thread-channel writer-thread-channel
+ (make-database database-file reader-thread-set writer-thread-set
+ writer-thread-set-channel
metrics-registry)
database?
(database-file database-file)
- (reader-thread-channel database-reader-thread-channel)
- (writer-thread-channel database-writer-thread-channel)
- (metrics-registry database-metrics-registry))
+ (reader-thread-set database-reader-thread-set)
+ (writer-thread-set database-writer-thread-set)
+ (writer-thread-set-channel database-writer-thread-set-channel
+ set-database-writer-thread-set-channel!)
+ (metrics-registry database-metrics-registry))
(define* (db-open database
#:key (write? #t))
@@ -143,28 +146,28 @@ CREATE TABLE IF NOT EXISTS create_branch_for_issue_logs (
(sqlite-close db))
- (let ((reader-thread-channel
- (make-worker-thread-channel
+ (let ((reader-thread-pool
+ (make-thread-pool
+ (min (max (current-processor-count)
+ 32)
+ 128)
+ #:thread-initializer
(lambda ()
(let ((db
(db-open database-file #:write? #f)))
(sqlite-exec db "PRAGMA busy_timeout = 5000;")
(list db)))
- #:destructor
+ #:thread-destructor
(lambda (db)
(sqlite-close db))
- #:lifetime 50000
+ #:thread-lifetime 50000
#:name "db read"
- #:parallelism
- (min (max (current-processor-count)
- 32)
- 128)
#:delay-logger (let ((delay-metric
(make-histogram-metric
metrics-registry
"datastore_read_delay_seconds")))
- (lambda (seconds-delayed)
+ (lambda (seconds-delayed proc)
(metric-observe delay-metric
;; TODO exact->inexact to work around
;; a bug in guile-prometheus where
@@ -180,30 +183,31 @@ CREATE TABLE IF NOT EXISTS create_branch_for_issue_logs (
#:log-exception?
(lambda (exn)
(not (guix-data-service-error? exn)))))
- (writer-thread-channel
- (make-worker-thread-channel
+ (writer-thread-pool
+ (make-thread-pool
+ ;; SQLite doesn't support parallel writes
+ 1
+ #:thread-initializer
(lambda ()
(let ((db
(db-open database-file)))
(sqlite-exec db "PRAGMA busy_timeout = 5000;")
(sqlite-exec db "PRAGMA foreign_keys = ON;")
(list db)))
- #:destructor
+ #:thread-destructor
(lambda (db)
(db-optimize db
database-file)
(sqlite-close db))
- #:lifetime 500
+ #:thread-lifetime 500
#:name "db write"
- ;; SQLite doesn't support parallel writes
- #:parallelism 1
#:delay-logger (let ((delay-metric
(make-histogram-metric
metrics-registry
"datastore_write_delay_seconds")))
- (lambda (seconds-delayed)
+ (lambda (seconds-delayed proc)
(metric-observe delay-metric
;; TODO exact->inexact to work around
;; a bug in guile-prometheus where
@@ -218,8 +222,9 @@ CREATE TABLE IF NOT EXISTS create_branch_for_issue_logs (
seconds-delayed)))))))
(make-database database-file
- reader-thread-channel
- writer-thread-channel
+ reader-thread-pool
+ writer-thread-pool
+ (thread-pool-channel writer-thread-pool)
metrics-registry)))
(define (db-optimize db db-filename)
@@ -244,8 +249,8 @@ PRAGMA optimize;")))
(define (database-optimize database)
(retry-on-error
(lambda ()
- (call-with-worker-thread
- (database-writer-thread-channel database)
+ (call-with-thread
+ (database-writer-thread-set database)
(lambda (db)
(db-optimize
db
@@ -254,6 +259,14 @@ PRAGMA optimize;")))
#:delay 5))
(define (database-spawn-fibers database)
+ ;; Queue messages to the writer thread, so that they're handled in a first
+ ;; come first served manor
+ (set-database-writer-thread-set-channel!
+ database
+ (spawn-queueing-fiber
+ (thread-pool-channel
+ (database-writer-thread-set database))))
+
(spawn-fiber
(lambda ()
(while #t
@@ -313,10 +326,10 @@ PRAGMA optimize;")))
(apply values vals))))
#:unwind? #t))))
- (match (call-with-worker-thread
+ (match (call-with-thread
((if readonly?
- database-reader-thread-channel
- database-writer-thread-channel)
+ database-reader-thread-set
+ database-writer-thread-set)
database)
(lambda (db)
(let ((start-time (get-internal-real-time)))
@@ -337,7 +350,11 @@ PRAGMA optimize;")))
duration-seconds)
(current-error-port)))
- (cons duration-seconds vals)))))))
+ (cons duration-seconds vals))))))
+ #:channel
+ (if readonly?
+ #f
+ (database-writer-thread-set-channel database)))
((duration vals ...)
(apply values vals))))
@@ -421,8 +438,8 @@ DELETE FROM cache WHERE key = :key"
(error "must specify a ttl"))
(let ((cached-values
- (call-with-worker-thread
- (database-reader-thread-channel database)
+ (call-with-thread
+ (database-reader-thread-set database)
(lambda (db)
(let ((statement
(sqlite-prepare
@@ -455,49 +472,56 @@ SELECT data, timestamp FROM cache WHERE key = :key"
(if (eq? cached-values 'noval)
(call-with-values
(lambda ()
- (call-with-worker-thread
- (database-reader-thread-channel database)
+ (call-with-thread
+ (database-reader-thread-set database)
(lambda (db)
- (call-with-delay-logging
- proc
- #:args args))))
+ (with-throw-handler #t
+ (lambda ()
+ (call-with-delay-logging
+ proc
+ #:args args))
+ (lambda args
+ (display (backtrace) (current-error-port))
+ (newline (current-error-port)))))))
(lambda vals
(when (if (procedure? store-computed-value?)
(apply store-computed-value? vals)
store-computed-value?)
- (database-call-with-transaction
- database
- (lambda (db)
- (let ((cleanup-statement
- (sqlite-prepare
- db
- "
+ (let ((vals-string
+ (call-with-output-string
+ (lambda (port)
+ (write vals port)))))
+ (database-call-with-transaction
+ database
+ (lambda (db)
+ (let ((cleanup-statement
+ (sqlite-prepare
+ db
+ "
DELETE FROM cache WHERE key = :key"
- #:cache? #t))
- (insert-statement
- (sqlite-prepare
- db
- "
+ #:cache? #t))
+ (insert-statement
+ (sqlite-prepare
+ db
+ "
INSERT INTO cache (key, timestamp, data)
VALUES (:key, :timestamp, :data)"
- #:cache? #t)))
+ #:cache? #t)))
- (sqlite-bind-arguments
- cleanup-statement
- #:key string-key)
- (sqlite-step cleanup-statement)
- (sqlite-reset cleanup-statement)
+ (sqlite-bind-arguments
+ cleanup-statement
+ #:key string-key)
+ (sqlite-step cleanup-statement)
+ (sqlite-reset cleanup-statement)
- (sqlite-bind-arguments
- insert-statement
- #:key string-key
- #:timestamp (time-second (current-time))
- #:data (call-with-output-string
- (lambda (port)
- (write vals port))))
+ (sqlite-bind-arguments
+ insert-statement
+ #:key string-key
+ #:timestamp (time-second (current-time))
+ #:data vals-string)
- (sqlite-step insert-statement)
- (sqlite-reset insert-statement)))))
+ (sqlite-step insert-statement)
+ (sqlite-reset insert-statement))))))
(apply values vals)))
(apply values cached-values))))
@@ -546,8 +570,8 @@ WHERE category_name = :name AND category_value = :value"
#t)
(define (select-from-builds-to-cancel-later database category-name)
- (call-with-worker-thread
- (database-reader-thread-channel database)
+ (call-with-thread
+ (database-reader-thread-set database)
(lambda (db)
(let ((statement
(sqlite-prepare
@@ -602,8 +626,8 @@ VALUES (:issue, :log)"
(sqlite-reset insert-statement)))))
(define (select-create-branch-for-issue-log database issue)
- (call-with-worker-thread
- (database-reader-thread-channel database)
+ (call-with-thread
+ (database-reader-thread-set database)
(lambda (db)
(let ((statement
(sqlite-prepare
diff --git a/guix-qa-frontpage/debbugs.scm b/guix-qa-frontpage/debbugs.scm
index 656865d..b1614db 100644
--- a/guix-qa-frontpage/debbugs.scm
+++ b/guix-qa-frontpage/debbugs.scm
@@ -24,6 +24,7 @@
fetch-issues-with-guix-tag))
(define (debbugs-get-issues-with-guix-usertag)
+ ;; TODO Ideally this would be non-blocking
(soap-invoke (%gnu) get-usertag "guix"))
(define (fetch-issues-with-guix-tag tag)
diff --git a/guix-qa-frontpage/derivation-changes.scm b/guix-qa-frontpage/derivation-changes.scm
index cda0084..eab021e 100644
--- a/guix-qa-frontpage/derivation-changes.scm
+++ b/guix-qa-frontpage/derivation-changes.scm
@@ -21,7 +21,7 @@
#:use-module (srfi srfi-43)
#:use-module (ice-9 match)
#:export (categorise-packages
- derivation-changes))
+ derivation-changes-counts))
(define (categorise-packages derivation-changes side)
(define (vector-member? s v)
@@ -82,7 +82,7 @@
'()
derivation-changes))
-(define (derivation-changes derivation-changes all-systems)
+(define (derivation-changes-counts derivation-changes all-systems)
(define categorised-base-packages-by-system
(categorise-packages (assoc-ref derivation-changes
"derivation_changes")
@@ -93,61 +93,57 @@
"derivation_changes")
"target"))
- (define counts
- (if (null? categorised-target-packages-by-system)
- '()
- (map
- (match-lambda
- ((system . categorised-target-builds)
- (let ((categorised-base-builds
- (assoc-ref categorised-base-packages-by-system
- system)))
- (cons
- system
- (map (lambda (side)
- (cons side
- (map (lambda (status)
- (cons status
- (length
- (or
- (assoc-ref
- (if (eq? side 'base)
- categorised-base-builds
- categorised-target-builds)
- status)
- '()))))
- '(succeeding failing blocked unknown))))
- '(base target))))))
- (sort
- (append categorised-target-packages-by-system
- (filter-map
- (lambda (system)
- (if (assoc-ref categorised-target-packages-by-system
- system)
- #f
- (cons system '())))
- all-systems))
- (lambda (a b)
- (let ((a-key (car a))
- (b-key (car b)))
- (cond
- ((and (string? a-key)
- (string? b-key))
- (< (or (list-index
- (lambda (s)
- (string=? (car a) s))
- all-systems)
- 10)
- (or (list-index
- (lambda (s)
- (string=? (car b) s))
- all-systems)
- 10)))
- ((and (pair? a-key)
- (pair? b-key))
- (string<? (cdr a-key)
- (cdr b-key)))
- (else #f))))))))
-
- `(,@derivation-changes
- (counts . ,counts)))
+ (if (null? categorised-target-packages-by-system)
+ '()
+ (map
+ (match-lambda
+ ((system . categorised-target-builds)
+ (let ((categorised-base-builds
+ (assoc-ref categorised-base-packages-by-system
+ system)))
+ (cons
+ system
+ (map (lambda (side)
+ (cons side
+ (map (lambda (status)
+ (cons status
+ (length
+ (or
+ (assoc-ref
+ (if (eq? side 'base)
+ categorised-base-builds
+ categorised-target-builds)
+ status)
+ '()))))
+ '(succeeding failing blocked unknown))))
+ '(base target))))))
+ (sort
+ (append categorised-target-packages-by-system
+ (filter-map
+ (lambda (system)
+ (if (assoc-ref categorised-target-packages-by-system
+ system)
+ #f
+ (cons system '())))
+ all-systems))
+ (lambda (a b)
+ (let ((a-key (car a))
+ (b-key (car b)))
+ (cond
+ ((and (string? a-key)
+ (string? b-key))
+ (< (or (list-index
+ (lambda (s)
+ (string=? (car a) s))
+ all-systems)
+ 10)
+ (or (list-index
+ (lambda (s)
+ (string=? (car b) s))
+ all-systems)
+ 10)))
+ ((and (pair? a-key)
+ (pair? b-key))
+ (string<? (cdr a-key)
+ (cdr b-key)))
+ (else #f))))))))
diff --git a/guix-qa-frontpage/git-repository.scm b/guix-qa-frontpage/git-repository.scm
index ec6996f..6ff3eb2 100644
--- a/guix-qa-frontpage/git-repository.scm
+++ b/guix-qa-frontpage/git-repository.scm
@@ -23,7 +23,8 @@
get-commit
get-git-branch-head-committer-date
- get-git-merge-base))
+ get-git-merge-base
+ get-git-remote-branches))
(define %git-repository-location
(make-parameter #f))
@@ -50,7 +51,7 @@
(invoke "git" "remote" "add" "origin"
"https://git.savannah.gnu.org/git/guix.git")
(invoke "git" "remote" "add" "patches"
- "git@git.guix-patches.cbaines.net:guix-patches")
+ "git@git.qa.guix.gnu.org:guix-patches")
(invoke "git" "config" "user.name" "Guix Patches Tester")
(invoke "git" "config" "user.email" "")))))))
@@ -135,3 +136,23 @@
(first lines)))
(loop (read-line pipe)
(cons line lines))))))))
+
+(define (get-git-remote-branches remote)
+ (with-bare-git-repository
+ (lambda ()
+ (let ((pipe (open-pipe* OPEN_READ
+ "git" "ls-remote" "--heads" remote)))
+ (let loop ((line (read-line pipe))
+ (result '()))
+ (if (eof-object? line)
+ (begin
+ (close-pipe pipe)
+
+ result)
+ (let ((commit (string-take line 40))
+ (branch (string-drop line 52)))
+ (loop (read-line pipe)
+ (cons
+ `(("name" . ,branch)
+ ("commit" . ,commit))
+ result)))))))))
diff --git a/guix-qa-frontpage/guix-data-service.scm b/guix-qa-frontpage/guix-data-service.scm
index 9bf7997..8540524 100644
--- a/guix-qa-frontpage/guix-data-service.scm
+++ b/guix-qa-frontpage/guix-data-service.scm
@@ -4,22 +4,33 @@
#:use-module (srfi srfi-71)
#:use-module (ice-9 match)
#:use-module (ice-9 exceptions)
+ #:use-module (ice-9 binary-ports)
#:use-module (web uri)
#:use-module (web client)
#:use-module (web response)
#:use-module (rnrs bytevectors)
#:use-module (zlib)
#:use-module (json)
+ #:use-module (fibers)
+ #:use-module (knots timeout)
+ #:use-module (knots non-blocking)
#:use-module ((guix-build-coordinator utils fibers) #:select (retry-on-error))
#:use-module (guix-qa-frontpage utils)
#:use-module (guix-qa-frontpage patchwork)
#:use-module (guix-qa-frontpage manage-patch-branches)
- #:export (&guix-data-service-error
+ #:export (%data-service-url-base
+ %data-service-guix-repository-id
+
+ &guix-data-service-error
guix-data-service-error?
guix-data-service-error-response-body
guix-data-service-error-response-code
+ guix-data-service-error-url
guix-data-service-error->sexp
+ guix-data-service-error-summary
+ guix-data-service-error-sexp->error
+ guix-data-service-error-invalid-query?
guix-data-service-request
@@ -51,84 +62,122 @@
package-reproducibility-url))
+(define %data-service-url-base
+ "https://data.qa.guix.gnu.org")
+
+(define %data-service-guix-repository-id 1)
+
(define-exception-type &guix-data-service-error &error
make-guix-data-service-error
guix-data-service-error?
(response-body guix-data-service-error-response-body)
- (response-code guix-data-service-error-response-code))
+ (response-code guix-data-service-error-response-code)
+ (url guix-data-service-error-url))
(define (guix-data-service-error->sexp exn)
- `((exception . guix-data-service-invalid-parameters)
- (invalid_query_parameters
- .
- ,(filter-map
- (match-lambda
- ((param . val)
- (and=>
- (assoc-ref val "invalid_value")
- (lambda (value)
- (let ((message
- (assoc-ref val "message")))
- (cons
- param
- `((value . ,value)
- (error
- ;; Convert the HTML error messages
- ;; to something easier to handle
- . ,(cond
- ((string-contains message
- "failed to process revision")
- 'failed-to-process-revision)
- ((string-contains message
- "yet to process revision")
- 'yet-to-process-revision)
- ((string=? message "unknown commit")
- 'unknown-commit)
- (else
- 'unknown-error))))))))))
- (assoc-ref
- (guix-data-service-error-response-body exn)
- "query_parameters")))))
-
-;; Returns the port as well as the raw socket
-(define* (open-socket-for-uri* uri
- #:key (verify-certificate? #t))
- (define tls-wrap
- (@@ (web client) tls-wrap))
-
- (define https?
- (eq? 'https (uri-scheme uri)))
-
- (define plain-uri
- (if https?
- (build-uri
- 'http
- #:userinfo (uri-userinfo uri)
- #:host (uri-host uri)
- #:port (or (uri-port uri) 443)
- #:path (uri-path uri)
- #:query (uri-query uri)
- #:fragment (uri-fragment uri))
- uri))
-
- (let ((s (open-socket-for-uri plain-uri)))
- (values
- (if https?
- (tls-wrap s (uri-host uri)
- #:verify-certificate? verify-certificate?)
- s)
- s)))
+ (cond
+ ((string=? (or (assoc-ref (guix-data-service-error-response-body exn)
+ "error")
+ "")
+ "invalid query")
+ `((exception . guix-data-service-invalid-parameters)
+ (invalid_query_parameters
+ .
+ ,(filter-map
+ (match-lambda
+ ((param . val)
+ (and=>
+ (assoc-ref val "invalid_value")
+ (lambda (value)
+ (let ((message
+ (assoc-ref val "message")))
+ (cons
+ param
+ `((value . ,value)
+ (error
+ ;; Convert the HTML error messages
+ ;; to something easier to handle
+ . ,(cond
+ ((string-contains message
+ "failed to process revision")
+ 'failed-to-process-revision)
+ ((string-contains message
+ "yet to process revision")
+ 'yet-to-process-revision)
+ ((string=? message "unknown commit")
+ 'unknown-commit)
+ (else
+ 'unknown-error))))))))))
+ (assoc-ref
+ (guix-data-service-error-response-body exn)
+ "query_parameters")))))
+ (else
+ `((exception . guix-data-service-exception)
+ (body . ,(guix-data-service-error-response-body exn))
+ (url . ,(guix-data-service-error-url exn))))))
+
+(define (guix-data-service-error-summary exn)
+ (cond
+ ((string=? (or (assoc-ref (guix-data-service-error-response-body exn)
+ "error")
+ "")
+ "invalid query")
+ (string-join
+ (filter-map
+ (match-lambda
+ ((param . val)
+ (and=>
+ (assoc-ref val "invalid_value")
+ (lambda (value)
+ (let ((message
+ (assoc-ref val "message")))
+ (simple-format
+ #f
+ "~A: ~A"
+ param
+ ;; Convert the HTML error messages
+ ;; to something easier to handle
+ (cond
+ ((string-contains message
+ "failed to process revision")
+ 'failed-to-process-revision)
+ ((string-contains message
+ "yet to process revision")
+ 'yet-to-process-revision)
+ ((string=? message "unknown commit")
+ 'unknown-commit)
+ (else
+ 'unknown-error))))))))
+ (assoc-ref
+ (guix-data-service-error-response-body exn)
+ "query_parameters"))
+ ", "))
+ (else
+ (simple-format #f "~A" (guix-data-service-error-response-body exn)))))
+
+(define (guix-data-service-error-sexp->error sexp)
+ (make-guix-data-service-error
+ (if (eq? (assq-ref sexp 'exception)
+ 'guix-data-service-invalid-parameters)
+ `(("error" . "invalid-query")
+ ,@sexp)
+ sexp)
+ #f
+ #f))
+
+(define (guix-data-service-error-invalid-query? exn)
+ (and
+ (guix-data-service-error? exn)
+ (string=?
+ (or (assoc-ref (guix-data-service-error-response-body exn)
+ "error")
+ "")
+ "invalid-query")))
(define* (guix-data-service-request url #:key (retry-times 0) (retry-delay 5))
(define (make-request)
(let ((port
- socket
- (open-socket-for-uri* (string->uri url))))
-
- ;; This can't be done earlier as tls-wrap/guile-gnutls doesn't support
- ;; handshake on a non blocking socket
- (let ((flags (fcntl socket F_GETFL)))
- (fcntl socket F_SETFL (logior O_NONBLOCK flags)))
+ (non-blocking-open-socket-for-uri (string->uri url))))
(let ((response
body
@@ -137,35 +186,49 @@
'((accept-encoding . ((1 . "gzip"))))
#:streaming? #t
#:port port)))
- (if (eq? (response-code response)
- 404)
- #f
- (let ((json-body
- (match (response-content-encoding response)
- (('gzip)
- ;; Stop fibers from triggering dynamic-wind in (zlib)
- (call-with-blocked-asyncs
- (lambda ()
- (call-with-zlib-input-port
- body
- json->scm
- #:format 'gzip))))
- (_
- (json->scm body)))))
- (if (or (> (response-code response)
- 400)
- (assoc-ref json-body "error"))
- (raise-exception
- (make-guix-data-service-error json-body
- (response-code response)))
- (values json-body
- response)))))))
+ (cond
+ ((eq? (response-code response) 404)
+ #f)
+ ((not (eq? (first (response-content-type response))
+ 'application/json))
+ (raise-exception
+ (make-guix-data-service-error
+ (utf8->string
+ (match (response-content-encoding response)
+ (('gzip)
+ (call-with-zlib-input-port*
+ body
+ get-bytevector-all
+ #:format 'gzip))
+ (_
+ (get-bytevector-all body))))
+ (response-code response)
+ url)))
+ (else
+ (let ((json-body
+ (match (response-content-encoding response)
+ (('gzip)
+ (call-with-zlib-input-port*
+ body
+ json->scm
+ #:format 'gzip))
+ (_
+ (json->scm body)))))
+ (if (or (> (response-code response)
+ 400)
+ (assoc-ref json-body "error"))
+ (raise-exception
+ (make-guix-data-service-error json-body
+ (response-code response)
+ url))
+ (values json-body
+ response))))))))
(if (= 0 retry-times)
(make-request)
(retry-on-error
(lambda ()
- (with-fibers-port-timeouts
+ (with-port-timeouts
make-request
#:timeout 120))
#:times retry-times
@@ -179,12 +242,13 @@
#:key system target
no-build-from-build-server)
(string-append
- "https://data.qa.guix.gnu.org/revision/"
+ %data-service-url-base
+ "/revision/"
commit
"/package-derivations.json?"
"system=" system
"&target=" target
- "&field=" "(no-additional-fields)"
+ "&field=" "no-additional-fields"
"&all_results=" "on"
(if no-build-from-build-server
(string-append
@@ -193,7 +257,8 @@
(define* (compare-package-derivations-url base-and-target-refs #:key systems)
(string-append
- "https://data.qa.guix.gnu.org/compare/package-derivations.json?"
+ %data-service-url-base
+ "/compare/package-derivations.json?"
"base_commit=" (assq-ref base-and-target-refs 'base)
"&target_commit=" (assq-ref base-and-target-refs 'target)
(string-join
@@ -206,7 +271,8 @@
(define* (compare-package-cross-derivations-url base-and-target-refs #:key systems)
(string-append
- "https://data.qa.guix.gnu.org/compare/package-derivations.json?"
+ %data-service-url-base
+ "/compare/package-derivations.json?"
"base_commit=" (assq-ref base-and-target-refs 'base)
"&target_commit=" (assq-ref base-and-target-refs 'target)
(string-join
@@ -225,7 +291,8 @@
(define* (revision-comparison-url base-and-target-refs #:key (json? #t))
(string-append
- "https://data.qa.guix.gnu.org/compare"
+ %data-service-url-base
+ "/compare"
(if json? ".json" "")
"?"
"base_commit=" (assq-ref base-and-target-refs 'base)
@@ -235,7 +302,8 @@
(guix-data-service-request url))
(define (list-branches-url repository-id)
- (simple-format #f "https://data.qa.guix.gnu.org/repository/~A.json"
+ (simple-format #f "~A/repository/~A.json"
+ %data-service-url-base
repository-id))
(define (list-branches url)
@@ -248,8 +316,9 @@
(let ((json-body
(guix-data-service-request
(string-append
- "https://data.qa.guix.gnu.org"
- "/repository/2"
+ %data-service-url-base
+ "/repository/"
+ (number->string %data-service-guix-repository-id)
"/branch/" branch
"/latest-processed-revision.json"))))
(assoc-ref
@@ -259,7 +328,8 @@
(define (branch-revisions-url repository-id branch-name)
(simple-format
#f
- "https://data.qa.guix.gnu.org/repository/~A/branch/~A.json"
+ "~A/repository/~A/branch/~A.json"
+ %data-service-url-base
repository-id
branch-name))
@@ -272,7 +342,8 @@
(define* (revision-details-url commit)
(simple-format
#f
- "https://data.qa.guix.gnu.org/revision/~A.json"
+ "~A/revision/~A.json"
+ %data-service-url-base
commit))
(define (revision-details url)
@@ -281,7 +352,8 @@
(define* (revision-system-tests-url commit #:key (system "x86_64-linux"))
(simple-format
#f
- "https://data.qa.guix.gnu.org/revision/~A/system-tests.json?system=~A"
+ "~A/revision/~A/system-tests.json?system=~A"
+ %data-service-url-base
commit
system))
@@ -294,7 +366,8 @@
(define* (package-substitute-availability-url commit)
(simple-format
#f
- "https://data.qa.guix.gnu.org/revision/~A/package-substitute-availability.json"
+ "~A/revision/~A/package-substitute-availability.json"
+ %data-service-url-base
commit))
(define (package-substitute-availability url)
@@ -307,5 +380,6 @@
(define* (package-reproducibility-url commit)
(simple-format
#f
- "https://data.qa.guix.gnu.org/revision/~A/package-reproducibility.json"
+ "~A/revision/~A/package-reproducibility.json"
+ %data-service-url-base
commit))
diff --git a/guix-qa-frontpage/issue.scm b/guix-qa-frontpage/issue.scm
index 6ceb733..beed41f 100644
--- a/guix-qa-frontpage/issue.scm
+++ b/guix-qa-frontpage/issue.scm
@@ -23,9 +23,13 @@
#:use-module (ice-9 threads)
#:use-module (prometheus)
#:use-module ((guix-build-coordinator utils)
- #:select (with-time-logging))
+ #:select (with-time-logging call-with-delay-logging))
#:use-module ((guix build syscalls)
#:select (set-thread-name))
+ #:use-module (fibers)
+ #:use-module (knots non-blocking)
+ #:use-module (knots parallelism)
+ #:use-module (guix-qa-frontpage utils)
#:use-module (guix-qa-frontpage database)
#:use-module (guix-qa-frontpage manage-builds)
#:use-module (guix-qa-frontpage manage-patch-branches)
@@ -40,7 +44,7 @@
issue-patches-overall-status
issue-data
- start-refresh-patch-branches-data-thread))
+ start-refresh-patch-branches-data-fiber))
(define reviewed-looks-good-status 'reviewed-looks-good)
(define good-status 'important-checks-passing)
@@ -177,79 +181,73 @@
(with-exception-handler
(lambda (exn)
(if (guix-data-service-error? exn)
- `((exception . guix-data-service-invalid-parameters)
- (invalid_query_parameters
- .
- ,(filter-map
- (match-lambda
- ((param . val)
- (and=>
- (assoc-ref val "invalid_value")
- (lambda (value)
- (let ((message
- (assoc-ref val "message")))
- (cons
- param
- `((value . ,value)
- (error
- ;; Convert the HTML error messages
- ;; to something easier to handle
- . ,(cond
- ((string-contains message
- "failed to process revision")
- 'failed-to-process-revision)
- ((string-contains message
- "yet to process revision")
- 'yet-to-process-revision)
- (else
- 'unknown))))))))))
- (assoc-ref
- (guix-data-service-error-response-body exn)
- "query_parameters"))))
+ (guix-data-service-error->sexp exn)
`((exception . ,(simple-format #f "~A" exn)))))
thunk
#:unwind? #t))
(let* ((base-and-target-refs
- (get-issue-branch-base-and-target-refs
- number))
+ (call-with-delay-logging
+ get-issue-branch-base-and-target-refs
+ #:args (list number)))
(derivation-changes-raw-data
(if base-and-target-refs
(call-with-data-service-error-handling
(lambda ()
- (compare-package-derivations
- (compare-package-derivations-url
- base-and-target-refs
- #:systems %systems-to-submit-builds-for))))
+ (call-with-delay-logging
+ compare-package-derivations
+ #:args
+ (list
+ (compare-package-derivations-url
+ base-and-target-refs
+ #:systems %systems-to-submit-builds-for)))))
#f))
(derivation-changes-data
(if (and derivation-changes-raw-data
(not (assq-ref derivation-changes-raw-data 'exception)))
- (derivation-changes
- derivation-changes-raw-data
- %systems-to-submit-builds-for)
+ (cons
+ (cons 'counts
+ (call-with-delay-logging
+ derivation-changes-counts
+ #:args
+ (list
+ derivation-changes-raw-data
+ %systems-to-submit-builds-for)))
+ derivation-changes-raw-data)
#f))
(cross-derivation-changes-raw-data
(if base-and-target-refs
(call-with-data-service-error-handling
(lambda ()
- (compare-package-derivations
- (compare-package-cross-derivations-url
- base-and-target-refs
- #:systems %systems-to-submit-builds-for))))
+ (call-with-delay-logging
+ compare-package-derivations
+ #:args
+ (list
+ (compare-package-cross-derivations-url
+ base-and-target-refs
+ #:systems %systems-to-submit-builds-for)))))
#f))
(cross-derivation-changes-data
(if (and cross-derivation-changes-raw-data
(not (assq-ref cross-derivation-changes-raw-data 'exception)))
- (derivation-changes
- cross-derivation-changes-raw-data
- %systems-to-submit-builds-for)
+ (cons
+ (cons 'counts
+ (call-with-delay-logging
+ derivation-changes-counts
+ #:args
+ (list
+ cross-derivation-changes-raw-data
+ %systems-to-submit-builds-for)))
+ cross-derivation-changes-raw-data)
#f))
(builds-missing?
(if derivation-changes-data
- (builds-missing-for-derivation-changes?
- (assoc-ref derivation-changes-raw-data
- "derivation_changes"))
+ (call-with-delay-logging
+ builds-missing-for-derivation-changes?
+ #:args
+ (list
+ (assoc-ref derivation-changes-raw-data
+ "derivation_changes")))
#t))
(comparison-details
(and
@@ -288,9 +286,11 @@
"query_parameters"))))
`((exception . ,(simple-format #f "~A" exn)))))
(lambda ()
- (revision-comparison
- (revision-comparison-url
- base-and-target-refs)))
+ (call-with-delay-logging
+ revision-comparison
+ #:args (list
+ (revision-comparison-url
+ base-and-target-refs))))
#:unwind? #t))))
(values
@@ -303,12 +303,25 @@
builds-missing?
comparison-details)))
-(define* (start-refresh-patch-branches-data-thread
+(define* (start-refresh-patch-branches-data-fiber
database
metrics-registry
#:key number-of-series-to-refresh)
(define frequency
- (* 15 60))
+ (* 30 60))
+
+ (define issue-data/fiberized+cached
+ (fiberize
+ (lambda (issue-number)
+ (with-sqlite-cache
+ database
+ 'issue-data
+ issue-data
+ #:args
+ (list issue-number)
+ #:version 3
+ #:ttl (/ frequency 2)))
+ #:parallelism 2))
(define (refresh-data)
(simple-format (current-error-port)
@@ -326,10 +339,22 @@
(take latest-series number-of-series-to-refresh)
latest-series)))
- (update-repository!)
+ (for-each
+ (match-lambda
+ ((issue-number . data)
+ (with-sqlite-cache
+ database
+ 'latest-patchwork-series-for-issue
+ (const data)
+ #:args (list issue-number)
+ #:ttl 0)))
+ latest-series)
- (n-par-for-each
- 5
+ (non-blocking
+ (lambda ()
+ (update-repository!)))
+
+ (fibers-batch-for-each
(match-lambda
((issue-number . series-data)
(with-exception-handler
@@ -348,14 +373,7 @@
change-details
builds-missing?
comparison-details
- (with-sqlite-cache
- database
- 'issue-data
- issue-data
- #:args
- (list issue-number)
- #:version 3
- #:ttl (/ frequency 2))))
+ (issue-data/fiberized+cached issue-number)))
(with-sqlite-cache
database
@@ -385,15 +403,11 @@
#:args (list issue-number)
#:ttl 0)))
#:unwind? #t)))
+ 50
series-to-refresh)))
- (call-with-new-thread
+ (spawn-fiber
(lambda ()
- (catch 'system-error
- (lambda ()
- (set-thread-name "data refresh"))
- (const #t))
-
(while #t
(let ((start-time (current-time)))
(with-exception-handler
diff --git a/guix-qa-frontpage/manage-builds.scm b/guix-qa-frontpage/manage-builds.scm
index b8b0189..82e2675 100644
--- a/guix-qa-frontpage/manage-builds.scm
+++ b/guix-qa-frontpage/manage-builds.scm
@@ -7,6 +7,9 @@
#:use-module (ice-9 threads)
#:use-module (ice-9 exceptions)
#:use-module (fibers)
+ #:use-module (knots parallelism)
+ #:use-module (knots non-blocking)
+ #:use-module (knots timeout)
#:use-module (prometheus)
#:use-module (guix sets)
#:use-module ((guix build syscalls)
@@ -33,8 +36,11 @@
default-branch-priority-for-change
submit-builds-for-branch
+ submit-build
+ %fiberized-submit-build
+
start-submit-patch-builds-fiber
- start-submit-branch-builds-thread
+ start-submit-branch-builds-fiber
start-submit-master-branch-system-tests-thread))
(define %systems-to-submit-builds-for
@@ -42,11 +48,18 @@
"i686-linux"
"aarch64-linux"
"armhf-linux"
- "powerpc64le-linux"
- "i586-gnu"))
+ "riscv64-linux"
+ ;; Don't submit powerpc64le-linux builds as the single build machine
+ ;; available isn't running enough at the moment
+ ;; "powerpc64le-linux"
+ ;; Builds for the hurd can't be reliably done at the moment, so skip
+ ;; submitting them
+ ;; "i586-gnu"
+ ))
(define %systems-with-expected-low-substitute-availability
- '("i586-gnu"
+ '("armhf-linux"
+ "i586-gnu"
"riscv64-linux"
"powerpc64le-linux"))
@@ -54,6 +67,9 @@
(* (length %systems-to-submit-builds-for)
600))
+(define %fiberized-submit-build
+ (make-parameter #f))
+
(define* (submit-builds-for-issue
database
build-coordinator
@@ -83,8 +99,10 @@
(current-error-port)
"failed fetching derivation changes for issue ~A: ~A\n"
issue-number
- exn)
-
+ (if (and (guix-data-service-error? exn)
+ (= (guix-data-service-error-response-code exn) 200))
+ (guix-data-service-error-summary exn)
+ exn))
#f)
(lambda ()
(with-sqlite-cache
@@ -185,7 +203,7 @@
issues-with-builds-to-cancel))
(simple-format #t "submitting patch builds\n")
- (for-each
+ (fibers-batch-for-each
(lambda (issue-number)
(submit-builds-for-issue
database
@@ -194,6 +212,7 @@
issue-number
#:priority priority-for-change
#:build-limit %patches-builds-limit))
+ 2
first-n-series-issue-numbers)))
(spawn-fiber
@@ -203,7 +222,7 @@
(lambda (exn)
(simple-format
(current-error-port)
- "exception in submit patch builds thread: ~A\n"
+ "exception in submit patch builds fiber: ~A\n"
exn))
(lambda ()
(with-throw-handler #t
@@ -233,6 +252,12 @@
(sleep 300)))))
+(define (shuffle-derivations-and-priorities! derivations-and-priorities)
+ (sort!
+ derivations-and-priorities
+ (lambda (a b) ; less
+ (string<? (first a) (first b)))))
+
(define* (submit-builds-for-branch database
build-coordinator
guix-data-service
@@ -248,114 +273,123 @@
(get-commit
(string-append "origin/" branch)))
(merge-base
- (get-git-merge-base
- (get-commit "origin/master")
- branch-commit))
+ (non-blocking
+ (lambda ()
+ (get-git-merge-base
+ (get-commit "origin/master")
+ branch-commit))))
(revisions
`((base . ,merge-base)
- (target . ,branch-commit)))
-
- (derivation-changes-url
- (compare-package-derivations-url
- revisions
- #:systems %systems-to-submit-builds-for)))
-
- (if derivation-changes-url
- (let ((derivation-changes-data
- (with-exception-handler
- (lambda (exn)
- (simple-format
- (current-error-port)
- "failed fetching derivation changes for branch ~A: ~A\n"
- branch
- exn)
-
- #f)
- (lambda ()
- (with-sqlite-cache
- database
- 'branch-derivation-changes
- compare-package-derivations
- #:args
- (list derivation-changes-url)
- #:ttl 0))
- #:unwind? #t)))
-
- (if derivation-changes-data
- (let ((target-commit
- (assoc-ref
- (assoc-ref
- (assoc-ref derivation-changes-data
- "revisions")
- "target")
- "commit")))
-
- (insert-into-builds-to-cancel-later database
- "branch"
- branch)
- (let ((derivations-and-priorities
- build-ids-to-keep-set
- (derivation-changes->builds-to-keep-and-submit
- derivation-changes-data
- priority)))
- (submit-builds-for-category build-coordinator
- guix-data-service
- 'branch
- branch
- derivations-and-priorities
- build-ids-to-keep-set
- target-commit
- #:threads 4)))
- (begin
- (simple-format
- (current-error-port)
- "attempting to submit builds for all derivations for branch ~A\n"
- branch)
+ (target . ,branch-commit))))
+
+ (let ((derivation-changes-vectors
+ (with-exception-handler
+ (lambda (exn)
+ (simple-format
+ (current-error-port)
+ "failed fetching derivation changes for branch ~A: ~A\n"
+ branch
+ exn)
+
+ #f)
+ (lambda ()
+ (map (lambda (system)
+ (retry-on-error
+ (lambda ()
+ (let ((data
+ (branch-derivation-changes-data revisions system)))
+ (if (assq-ref data 'exception)
+ (raise-exception
+ (guix-data-service-error-sexp->error data))
+ (assoc-ref data "derivation_changes"))))
+ #:no-retry guix-data-service-error-invalid-query?
+ #:times 2
+ #:delay 15))
+ %systems-to-submit-builds-for))
+ #:unwind? #t)))
+
+ (if derivation-changes-vectors
+ (begin
+ (insert-into-builds-to-cancel-later database
+ "branch"
+ branch)
+ (let ((derivations-and-priorities
+ build-ids-to-keep-set
+ (derivation-changes-vectors->builds-to-keep-and-submit
+ derivation-changes-vectors
+ priority)))
+ (submit-builds-for-category build-coordinator
+ guix-data-service
+ 'branch
+ branch
+ (shuffle-derivations-and-priorities!
+ derivations-and-priorities)
+ build-ids-to-keep-set
+ branch-commit
+ #:skip-updating-derived-priorities? #t)))
+ (begin
+ (simple-format
+ (current-error-port)
+ "attempting to submit builds for all derivations for branch ~A\n"
+ branch)
+
+ (let ((derivations-and-priorities
+ (shuffle-derivations-and-priorities!
+ (fold
+ (lambda (system result)
+ (let ((package-derivations
+ ;; This can be #f for unprcessed revisions as
+ ;; the data service gives a 404
+ (guix-data-service-request
+ (package-derivations-url
+ branch-commit
+ #:system system
+ #:target ""
+ #:no-build-from-build-server "2"))))
+ (if (eq? package-derivations #f)
+ (begin
+ (simple-format
+ (current-error-port)
+ "missing package derivation data for ~A\n"
+ branch)
+ '())
+ (vector-fold-right
+ (lambda (_ result derivation)
+ (cons
+ (list
+ (assoc-ref derivation "derivation")
+ (if (number? priority)
+ priority
+ (priority derivation)))
+ result))
+ result
+ (assoc-ref package-derivations
+ "derivations")))))
+ '()
+ %systems-to-submit-builds-for))))
+ (insert-into-builds-to-cancel-later database
+ "branch"
+ branch)
- (let ((derivations-and-priorities
- (fold
- (lambda (system result)
- (vector-fold-right
- (lambda (_ result derivation)
- (cons
- (list
- (assoc-ref derivation "derivation")
- (if (number? priority)
- priority
- (priority derivation)))
- result))
- result
- (assoc-ref
- (guix-data-service-request
- (package-derivations-url
- branch-commit
- #:system system
- #:target ""
- #:no-build-from-build-server "2"))
- "derivations")))
- '()
- %systems-to-submit-builds-for)))
- (submit-builds-for-category build-coordinator
- guix-data-service
- 'branch
- branch
- derivations-and-priorities
- (set)
- branch-commit
- #:threads 4)))))
- (simple-format #t "no derivation changes url for branch ~A\n"
- branch))))
+ (submit-builds-for-category build-coordinator
+ guix-data-service
+ 'branch
+ branch
+ derivations-and-priorities
+ (set)
+ branch-commit
+ #:skip-updating-derived-priorities? #t)))))))
(define (take* lst n)
(if (< (length lst) n)
lst
(take lst n)))
-(define (start-submit-branch-builds-thread database
- build-coordinator
- guix-data-service
- metrics-registry)
+(define (start-submit-branch-builds-fiber database
+ build-coordinator
+ guix-data-service
+ metrics-registry)
(define (cancel-branch-builds branches)
(for-each
(lambda (branch)
@@ -390,61 +424,66 @@
branches))
(define (submit-branch-builds)
- (let* ((branches
- (take*
- (filter
- (match-lambda
- ((name . details)
- (->bool (assoc-ref details "issue_number"))))
- (with-sqlite-cache
- database
- 'list-non-master-branches
- list-non-master-branches
- #:ttl 0))
- 2))
- (branch-names
- (map car branches)))
-
- (let* ((branches-with-builds-previously-submitted
- (select-from-builds-to-cancel-later
- database
- "branch"))
- (branches-with-builds-to-cancel
- (lset-difference
- string=?
- branches-with-builds-previously-submitted
- branch-names)))
- (unless (null? branches-with-builds-to-cancel)
- (cancel-branch-builds branches-with-builds-to-cancel)))
-
- (let* ((substitute-availability
- systems-with-low-substitute-availability
- package-reproducibility
- (with-sqlite-cache
- database
- 'master-branch-data
- master-branch-data
- #:ttl 6000
- #:version 2)))
- (if (null? systems-with-low-substitute-availability)
- (submit-builds branch-names)
- (simple-format
- (current-error-port)
- "waiting for master branch substitutes before submitting branch builds\n")))))
+ (let ((all-branches
+ (with-sqlite-cache
+ database
+ 'list-non-master-branches
+ list-non-master-branches
+ #:ttl 0)))
+ (if (assq-ref all-branches 'exception)
+ (simple-format
+ (current-error-port)
+ "unable to submit branch builds, exception in list-non-master-branches: ~A\n"
+ (assq-ref all-branches 'exception))
+
+ (let* ((branches
+ (take*
+ (filter
+ (match-lambda
+ ((name . details)
+ (->bool (assoc-ref details "issue_number"))))
+ all-branches)
+ ;; TODO The builds for the first branch should be mostly
+ ;; complete before submitting builds for any others
+ 1))
+ (branch-names
+ (map car branches)))
+
+ (let* ((branches-with-builds-previously-submitted
+ (select-from-builds-to-cancel-later
+ database
+ "branch"))
+ (branches-with-builds-to-cancel
+ (lset-difference
+ string=?
+ branches-with-builds-previously-submitted
+ branch-names)))
+ (unless (null? branches-with-builds-to-cancel)
+ (cancel-branch-builds branches-with-builds-to-cancel)))
+
+ (let* ((substitute-availability
+ systems-with-low-substitute-availability
+ package-reproducibility
+ (with-sqlite-cache
+ database
+ 'master-branch-data
+ master-branch-data
+ #:ttl 6000
+ #:version 2)))
+ (if (null? systems-with-low-substitute-availability)
+ (submit-builds branch-names)
+ (simple-format
+ (current-error-port)
+ "waiting for master branch substitutes before submitting branch builds\n")))))))
- (call-with-new-thread
+ (spawn-fiber
(lambda ()
- (catch 'system-error
- (lambda ()
- (set-thread-name "branch builds"))
- (const #t))
-
(while #t
(with-exception-handler
(lambda (exn)
(simple-format
(current-error-port)
- "exception in submit branch builds thread: ~A\n"
+ "exception in submit branch builds fiber: ~A\n"
exn))
(lambda ()
(with-throw-handler #t
@@ -462,11 +501,12 @@
(sleep 3600)))))
(define* (submit-build build-coordinator guix-data-service derivation
- #:key (priority 0) (tags '()))
+ #:key (priority 0) (tags '())
+ skip-updating-derived-priorities?)
(retry-on-error
(lambda ()
(let ((response
- (with-fibers-port-timeouts
+ (with-port-timeouts
(lambda ()
(send-submit-build-request
build-coordinator
@@ -477,8 +517,10 @@
#t
#t
#t
- tags))
- #:timeout 60)))
+ tags
+ #:skip-updating-derived-priorities?
+ skip-updating-derived-priorities?))
+ #:timeout 240)))
(let ((no-build-submitted-response
(assoc-ref response "no-build-submitted")))
(if no-build-submitted-response
@@ -514,7 +556,7 @@
"canceling builds for ~A ~A\n"
category-name
category-value)
- (with-fibers-port-timeouts
+ (with-port-timeouts
(lambda ()
(let loop ((uuids-batch (fetch-build-uuids)))
(for-each
@@ -542,7 +584,7 @@
(unless (null? uuids-batch)
(loop (fetch-build-uuids)))))
- #:timeout 60)
+ #:timeout 120)
(simple-format (current-error-port)
"finshed canceling builds for ~A ~A\n"
category-name
@@ -576,7 +618,7 @@
category-name
category-value
revision)
- (with-fibers-port-timeouts
+ (with-port-timeouts
(lambda ()
(let loop ((uuids-batch (fetch-build-uuids)))
(let ((builds-to-cancel
@@ -606,7 +648,7 @@
(unless (null? builds-to-cancel)
(loop (fetch-build-uuids))))))
- #:timeout 60)
+ #:timeout 120)
(simple-format (current-error-port)
"finished canceling builds for ~A ~A and not revision ~A\n"
category-name
@@ -635,71 +677,103 @@
'()
derivation-changes)))
+
(define (derivation-changes->builds-to-keep-and-submit derivation-changes
priority)
- (let loop ((changes
- (vector-fold
- (lambda (_ result package)
- (append! result
- (vector->list
- (assoc-ref package "target"))))
- '()
- (assoc-ref derivation-changes "derivation_changes")))
- (builds-to-submit-details '())
- (build-ids-to-keep-set (set)))
-
- (if (null? changes)
+ (derivation-changes-vectors->builds-to-keep-and-submit
+ (list (assoc-ref derivation-changes "derivation_changes"))
+ priority))
+
+(define (derivation-changes-vectors->builds-to-keep-and-submit all-derivation-changes-vectors
+ priority)
+ (define (process-change? change)
+ (and (string=? (assoc-ref change "target")
+ "")
+ (member (assoc-ref change "system")
+ %systems-to-submit-builds-for)))
+
+ (define (skip-submitting-build? change)
+ (vector-any
+ (lambda (build)
+ (let ((build-status
+ (assoc-ref build "status")))
+ (if (string=? build-status "scheduled")
+ (not (assoc-ref
+ build
+ "build_for_equivalent_derivation"))
+ (member build-status
+ '("started" "succeeded" "failed")))))
+ (assoc-ref change "builds")))
+
+ ;; So bad, but hopefully keeps memory usage down compared to converting to
+ ;; lists and flattening
+ (let loop1 ((derivation-changes-vectors all-derivation-changes-vectors)
+ (builds-to-submit-details '())
+ (build-ids-to-keep-set (set)))
+ (if (null? derivation-changes-vectors)
(values builds-to-submit-details
build-ids-to-keep-set)
- (let ((change (first changes)))
- (if (and (string=? (assoc-ref change "target")
- "")
- (member (assoc-ref change "system")
- %systems-to-submit-builds-for))
- (loop (cdr changes)
- (if (vector-any
- (lambda (build)
- (let ((build-status
- (assoc-ref build "status")))
- (if (string=? build-status "scheduled")
- (not (assoc-ref
- build
- "build_for_equivalent_derivation"))
- (member build-status
- '("started" "succeeded" "failed")))))
- (assoc-ref change "builds"))
- builds-to-submit-details ; build exists
- (cons
- (list (assoc-ref change "derivation-file-name")
- (if (number? priority)
- priority
- (priority change)))
- builds-to-submit-details))
- (fold (lambda (build result)
- (let ((build-status
- (assoc-ref build "status")))
- (if (or (string=? build-status "started")
- (and (string=? build-status "scheduled")
- ;; Cancel and replace builds for
- ;; equivalent derivations, since
- ;; the derivation might be removed
- ;; from the data service preventing
- ;; the build from starting.
- (not
- (assoc-ref
- build
- "build_for_equivalent_derivation"))))
- (set-insert
- (assoc-ref build "build_server_build_id")
- result)
- result)))
- build-ids-to-keep-set
- (vector->list
- (assoc-ref change "builds"))))
-
- (loop (cdr changes)
- builds-to-submit-details
- build-ids-to-keep-set))))))
+ (let* ((changes-vector
+ (car derivation-changes-vectors))
+ (changes-vector-length
+ (vector-length changes-vector)))
+ (let loop2 ((changes-index 0)
+ (builds-to-submit-details builds-to-submit-details)
+ (build-ids-to-keep-set build-ids-to-keep-set))
+ (if (= changes-index changes-vector-length)
+ (loop1 (cdr derivation-changes-vectors)
+ builds-to-submit-details
+ build-ids-to-keep-set)
+ (let* ((change-target-vector
+ (assoc-ref (vector-ref changes-vector changes-index)
+ "target"))
+ (change-target-vector-length
+ (vector-length change-target-vector)))
+ (let loop3 ((change-target-index 0)
+ (builds-to-submit-details builds-to-submit-details)
+ (build-ids-to-keep-set build-ids-to-keep-set))
+ (if (= change-target-index change-target-vector-length)
+ (loop2 (1+ changes-index)
+ builds-to-submit-details
+ build-ids-to-keep-set)
+ (let ((change
+ (vector-ref change-target-vector
+ change-target-index)))
+ (if (process-change? change)
+ (loop3 (1+ change-target-index)
+ (if (skip-submitting-build? change)
+ builds-to-submit-details ; build exists
+ (cons
+ (list (assoc-ref change "derivation-file-name")
+ (if (number? priority)
+ priority
+ (priority change)))
+ builds-to-submit-details))
+ (fold (lambda (build result)
+ (let ((build-status
+ (assoc-ref build "status")))
+ (if (or (string=? build-status "started")
+ (and (string=? build-status "scheduled")
+ ;; Cancel and replace builds for
+ ;; equivalent derivations, since
+ ;; the derivation might be removed
+ ;; from the data service preventing
+ ;; the build from starting.
+ (not
+ (assoc-ref
+ build
+ "build_for_equivalent_derivation"))))
+ (set-insert
+ (assoc-ref build "build_server_build_id")
+ result)
+ result)))
+ build-ids-to-keep-set
+ (vector->list
+ (assoc-ref change "builds"))))
+
+ (loop3 (1+ change-target-index)
+ builds-to-submit-details
+ build-ids-to-keep-set))))))))))))
(define* (submit-builds-for-category build-coordinator
guix-data-service
@@ -710,32 +784,30 @@
target-commit
#:key build-limit
(build-count-priority-penalty (const 0))
- (threads 1))
+ skip-updating-derived-priorities?)
(define (submit-builds build-details
build-ids-to-keep-set)
+ (define submit-build/fiberized
+ (%fiberized-submit-build))
+
(define submit-single
(match-lambda
((derivation priority)
- (submit-build build-coordinator
- guix-data-service
- derivation
- #:priority priority
- #:tags
- `(((key . category)
- (value . package))
- ((key . ,category-name)
- (value . ,category-value))
- ((key . revision)
- (value . ,target-commit)))))))
-
- (if (= threads 1)
- (for-each
- submit-single
- build-details)
- (n-par-for-each
- threads
- submit-single
- build-details)))
+ (submit-build/fiberized build-coordinator
+ guix-data-service
+ derivation
+ #:priority priority
+ #:tags
+ `(((key . category)
+ (value . package))
+ ((key . ,category-name)
+ (value . ,category-value))
+ ((key . revision)
+ (value . ,target-commit)))
+ #:skip-updating-derived-priorities?
+ skip-updating-derived-priorities?))))
+
+ (fibers-for-each submit-single build-details))
(let ((builds-to-submit-count
(length derivations-and-priorities)))
@@ -744,14 +816,18 @@
category-name
category-value)
- ;; Cancel builds first, as some of the builds we want to submit might be
- ;; for the same outputs as ones we're going to cancel.
- (cancel-builds-not-for-revision
- build-coordinator
- category-name
- category-value
- target-commit
- build-ids-to-keep-set)
+ (retry-on-error
+ (lambda ()
+ ;; Cancel builds first, as some of the builds we want to submit might be
+ ;; for the same outputs as ones we're going to cancel.
+ (cancel-builds-not-for-revision
+ build-coordinator
+ category-name
+ category-value
+ target-commit
+ build-ids-to-keep-set))
+ #:times 3
+ #:delay 2)
(if (or (not build-limit)
(< builds-to-submit-count
@@ -814,7 +890,8 @@
(assoc-ref revision-details "commit-hash")
#f))
(branch-revisions
- (branch-revisions-url 2 "master"))))
+ (branch-revisions-url %data-service-guix-repository-id
+ "master"))))
(recent-processed-revision-commits
(if (> (length processed-revision-commits)
5)
diff --git a/guix-qa-frontpage/manage-patch-branches.scm b/guix-qa-frontpage/manage-patch-branches.scm
index c3ae256..7cb9cee 100644
--- a/guix-qa-frontpage/manage-patch-branches.scm
+++ b/guix-qa-frontpage/manage-patch-branches.scm
@@ -17,21 +17,17 @@
#:use-module (guix sets)
#:use-module (guix memoization)
#:use-module (guix build utils)
- #:use-module ((guix build syscalls)
- #:select (set-thread-name))
- #:use-module (guix-build-coordinator utils)
- #:use-module (guix-build-coordinator utils fibers)
#:use-module ((guix build download) #:select (http-fetch))
#:use-module ((guix build utils) #:select (with-directory-excursion))
+ #:use-module (knots thread-pool)
#:use-module (guix-qa-frontpage mumi)
#:use-module (guix-qa-frontpage database)
#:use-module (guix-qa-frontpage git-repository)
#:use-module (guix-qa-frontpage patchwork)
+ #:use-module (guix-qa-frontpage branch)
#:use-module (guix-qa-frontpage guix-data-service)
#:export (create-branch-for-issue
- patchwork-series->branch
-
start-manage-patch-branches-thread
get-issue-branch-base-and-target-refs))
@@ -129,65 +125,8 @@
(close-pipe pipe)
result))
-(define (parse-patch-name name)
- (let ((args
- (and
- (string-prefix? "[" name)
- (let ((stop (string-index name #\])))
- (substring name 1 stop))))
- (as-bug-number
- (lambda (arg)
- (and (string-prefix? "bug#" arg)
- (string->number (substring arg (string-length "bug#"))))))
- (as-v2
- (lambda (arg)
- (and (string-prefix? "v" arg)
- (string->number (substring arg 1)))))
- (as-patch-number
- (lambda (arg)
- (match (string-split arg #\/)
- (((= string->number index) (= string->number total))
- (and index total (<= index total)
- (cons index total)))
- (else #f)))))
- (let analyze ((bug-number #f)
- (branch "master")
- (version 1)
- (index 1)
- (total 1)
- (arguments
- (if args
- (string-split args #\,)
- '())))
- (match arguments
- ((or ("") ())
- `((bug-number . ,bug-number)
- (branch . ,branch)
- (version . ,version)
- (index . ,index)
- (total . ,total)))
- (((= as-bug-number (? number? new-bug-number))
- arguments ...)
- (analyze new-bug-number branch version index total arguments))
- (((= as-v2 (? number? new-version))
- arguments ...)
- (analyze bug-number branch new-version index total arguments))
- (((= as-patch-number ((? number? new-index) . (? number? new-total)))
- arguments ...)
- (analyze bug-number branch version new-index new-total arguments))
- ((feature-branch arguments ...)
- (analyze bug-number feature-branch version index total arguments))))))
-
-(define (patchwork-series->branch series)
- (match (assoc-ref series "patches")
- (#() "master")
- (#(first-patch rest ...)
- (let ((details
- (parse-patch-name
- (assoc-ref first-patch "name"))))
- (assq-ref details 'branch)))))
-
-(define (create-branch-for-issue database issue-number patchwork-series)
+(define (create-branch-for-issue database latest-processed-master-revision
+ issue-number patchwork-series)
(define branch-name
(simple-format #f "issue-~A" issue-number))
@@ -196,10 +135,9 @@
(define (get-base-commit)
(let ((branch
- (patchwork-series->branch patchwork-series)))
+ (assq-ref patchwork-series 'branch)))
(if (string=? branch "master")
- (get-latest-processed-branch-revision "master")
-
+ latest-processed-master-revision
(with-bare-git-repository
(lambda ()
(invoke "git" "fetch" "--prune" "origin")
@@ -226,14 +164,16 @@
'issue-patches-overall-status
#:args (list issue-number)))
- (define (insert-log results)
+ (define (insert-log base-commit-hash results)
(define log
- (string-join
- (map
- (lambda (patch)
- (assq-ref patch 'output))
- results)
- "\n\n"))
+ (string-append
+ "Using base commit " base-commit-hash "\n\n"
+ (string-join
+ (map
+ (lambda (patch)
+ (assq-ref patch 'output))
+ results)
+ "\n\n")))
(insert-create-branch-for-issue-log database issue-number log))
@@ -253,7 +193,7 @@
(results '()))
(if (null? patch-data)
(begin
- (insert-log results)
+ (insert-log base-commit-hash results)
(if (string=? base-commit-hash
(with-repository (getcwd) repository
@@ -304,7 +244,8 @@
(begin
(simple-format
#t "Failed to apply \"~A.patch\" (~A)\n" id name)
- (insert-log new-results)
+ (insert-log base-commit-hash
+ new-results)
#f)))))))))
(delete-create-branch-for-issue-log database issue-number)
@@ -385,7 +326,12 @@
#:args `(#:count ,(+ series-count series-count-buffer))
#:ttl 120))
(get-latest-processed-branch-revision*
- (memoize get-latest-processed-branch-revision)))
+ (memoize get-latest-processed-branch-revision))
+ (branches
+ (map (lambda (branch)
+ (assoc-ref branch "name"))
+ (list-branches
+ (list-branches-url %data-service-guix-repository-id)))))
;; Several series can use the same base revision, so memoize looking up
;; the changes compared to master
@@ -404,6 +350,7 @@
(simple-format #t "checking for branches to delete (looking at ~A branches)\n"
(length issue-numbers))
+ (simple-format #t "all branches: ~A\n" branches)
(for-each
(lambda (issue-number)
(when (or (if (not (mumi-issue-open? issue-number))
@@ -424,8 +371,9 @@
(assq-ref
(get-issue-branch-base-and-target-refs issue-number)
'base))
- (branch (patchwork-series->branch
- (assq-ref all-patchwork-series issue-number))))
+ (branch
+ (assq-ref (assq-ref all-patchwork-series issue-number)
+ 'branch)))
(with-exception-handler
(lambda (exn)
(if (and (guix-data-service-error? exn)
@@ -435,7 +383,11 @@
"query_parameters" "base_commit"
"message")
(lambda (message)
- (string=? message "unknown commit"))))
+ (string=? message "unknown commit")))
+ ;; Don't treat the base revision
+ ;; as gone if the branch is
+ ;; unknown
+ (member branch branches))
(begin
(simple-format
(current-error-port)
@@ -494,7 +446,9 @@
'latest-patchwork-series-by-issue
latest-patchwork-series-by-issue
#:args `(#:count ,series-count)
- #:ttl 120)))
+ #:ttl 120))
+ (latest-processed-master-revision
+ (get-latest-processed-branch-revision "master")))
(for-each
(match-lambda
((issue-number . patchwork-series)
@@ -537,6 +491,7 @@
(const #t)
(lambda ()
(create-branch-for-issue database
+ latest-processed-master-revision
issue-number
patchwork-series))
#:unwind? #t))))
@@ -564,7 +519,7 @@
(current-error-port)
"exception in manage patch branches thread: ~A\n"
exn)
- (unless (worker-thread-timeout-error? exn)
+ (unless (thread-pool-timeout-error? exn)
(sleep 240)))
(lambda ()
(with-throw-handler #t
diff --git a/guix-qa-frontpage/mumi.scm b/guix-qa-frontpage/mumi.scm
index 6baa199..80f3646 100644
--- a/guix-qa-frontpage/mumi.scm
+++ b/guix-qa-frontpage/mumi.scm
@@ -18,6 +18,7 @@
(define-module (guix-qa-frontpage mumi)
#:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-71)
#:use-module (ice-9 match)
#:use-module (json)
#:use-module (kolam http)
@@ -33,6 +34,75 @@
mumi-bulk-issues))
+(define (at-most max-length lst)
+ "If LST is shorter than MAX-LENGTH, return it and the empty list; otherwise
+return its MAX-LENGTH first elements and its tail."
+ (let loop ((len 0)
+ (lst lst)
+ (result '()))
+ (match lst
+ (()
+ (values (reverse result) '()))
+ ((head . tail)
+ (if (>= len max-length)
+ (values (reverse result) lst)
+ (loop (+ 1 len) tail (cons head result)))))))
+
+(define %max-cached-connections
+ ;; Maximum number of connections kept in cache by
+ ;; 'open-connection-for-uri/cached'.
+ 16)
+
+(define open-socket-for-uri/cached
+ (let ((cache '()))
+ (lambda* (uri #:key fresh? verify-certificate?)
+ "Return a connection for URI, possibly reusing a cached connection.
+When FRESH? is true, delete any cached connections for URI and open a new one.
+Return #f if URI's scheme is 'file' or #f.
+
+When VERIFY-CERTIFICATE? is true, verify HTTPS server certificates."
+ (define host (uri-host uri))
+ (define scheme (uri-scheme uri))
+ (define key (list host scheme (uri-port uri)))
+
+ (and (not (memq scheme '(file #f)))
+ (match (assoc-ref cache key)
+ (#f
+ ;; Open a new connection to URI and evict old entries from
+ ;; CACHE, if any.
+ (let ((socket
+ (open-socket-for-uri
+ uri
+ #:verify-certificate? verify-certificate?))
+ (new-cache evicted
+ (at-most (- %max-cached-connections 1) cache)))
+ (for-each (match-lambda
+ ((_ . port)
+ (false-if-exception (close-port port))))
+ evicted)
+ (set! cache (alist-cons key socket new-cache))
+ socket))
+ (socket
+ (if (or fresh? (port-closed? socket))
+ (begin
+ (false-if-exception (close-port socket))
+ (set! cache (alist-delete key cache))
+ (open-socket-for-uri/cached uri
+ #:verify-certificate?
+ verify-certificate?))
+ (begin
+ ;; Drain input left from the previous use.
+ (drain-input socket)
+ socket))))))))
+
+(define (call-with-cached-connection uri proc)
+ (let ((port (open-socket-for-uri/cached uri)))
+ (with-throw-handler #t
+ (lambda ()
+ (proc port))
+ (lambda _
+ (close-port port)))))
+
(define* (graphql-http-get*
uri document
#:key (verify-certificate? #t)
@@ -43,37 +113,31 @@
(variables '()))
(call-with-values
(lambda ()
- (http-get
- (string-append uri
- "?query="
- (uri-encode (scm->graphql-string document))
- "&"
- "variables="
- (uri-encode (scm->json-string
- ((@@ (kolam http) variables->alist)
- variables))))
- #:streaming? #t
- #:keep-alive? keep-alive?
- #:verify-certificate? verify-certificate?
- #:port port))
+ (let ((response
+ body
+ (http-get
+ (string-append uri
+ "?query="
+ (uri-encode (scm->graphql-string document))
+ "&"
+ "variables="
+ (uri-encode (scm->json-string
+ ((@@ (kolam http) variables->alist)
+ variables))))
+ #:streaming? #t
+ #:keep-alive? keep-alive?
+ #:verify-certificate? verify-certificate?
+ #:port port)))
+ (values response
+ body)))
(@@ (kolam http) graphql-http-response)))
-
(define (mumi-search-issues query)
- (with-exception-handler
- (lambda (exn)
- (simple-format
- (current-error-port)
- "exception when searching issues: ~A\n"
- exn)
- #f)
- (lambda ()
- (let ((response
- (graphql-http-get "https://issues.guix.gnu.org/graphql"
- `(document (query (#(issues #:search ,query) number title date open (blocked_by number)))))))
- (assoc-ref response
- "issues")))
- #:unwind? #t))
+ (let ((response
+ (graphql-http-get "https://issues.guix.gnu.org/graphql"
+ `(document (query (#(issues #:search ,query) number title date open (blocked_by number)))))))
+ (assoc-ref response
+ "issues")))
(define (mumi-issue-open? number)
(let ((response
@@ -89,17 +153,14 @@
(let ((number-to-data
(make-hash-table)))
- (let loop ((chunks (chunk! (list-copy numbers)
- 30))
- (port
- (open-socket-for-uri
- (string->uri url)
- #:verify-certificate? #t)))
- (if (null? chunks)
- (close-port port)
- (let ((response
- (retry-on-error
- (lambda ()
+ (for-each
+ (lambda (chunk)
+ (let ((response
+ (retry-on-error
+ (lambda ()
+ (call-with-cached-connection
+ (string->uri url)
+ (lambda (port)
(graphql-http-get*
url
`(document
@@ -107,30 +168,29 @@
`(query (#(issue #:number ,number)
number title open severity tags
(merged_with number))))
- (car chunks)))
+ chunk))
#:keep-alive? #t
- #:port port))
- #:times 1
- #:delay 0)))
-
- (for-each
- (lambda (res)
- (let ((data (cdr res)))
- (hash-set! number-to-data
- (assoc-ref data "number")
- `((title . ,(assoc-ref data "title"))
- (open? . ,(assoc-ref data "open"))
- (tags . ,(vector->list
- (assoc-ref data "tags")))
- (merged-with . ,(map
- (lambda (data)
- (assoc-ref data "number"))
- (vector->list
- (assoc-ref data "merged_with"))))
- (severity . ,(assoc-ref data "severity"))))))
- response)
-
- (loop (cdr chunks) port))))
+ #:port port))))
+ #:times 1
+ #:delay 0)))
+
+ (for-each
+ (lambda (res)
+ (let ((data (cdr res)))
+ (hash-set! number-to-data
+ (assoc-ref data "number")
+ `((title . ,(assoc-ref data "title"))
+ (open? . ,(assoc-ref data "open"))
+ (tags . ,(vector->list
+ (assoc-ref data "tags")))
+ (merged-with . ,(map
+ (lambda (data)
+ (assoc-ref data "number"))
+ (vector->list
+ (assoc-ref data "merged_with"))))
+ (severity . ,(assoc-ref data "severity"))))))
+ response)))
+ (chunk! (list-copy numbers) 30))
(map (lambda (number)
(hash-ref number-to-data number))
diff --git a/guix-qa-frontpage/patchwork.scm b/guix-qa-frontpage/patchwork.scm
index 8f9d570..e1ee24f 100644
--- a/guix-qa-frontpage/patchwork.scm
+++ b/guix-qa-frontpage/patchwork.scm
@@ -7,19 +7,27 @@
#:use-module (ice-9 match)
#:use-module (rnrs bytevectors)
#:use-module (json)
+ #:use-module (fibers)
+ #:use-module (knots non-blocking)
+ #:use-module (knots timeout)
#:use-module (web uri)
#:use-module (web client)
#:use-module (web request)
#:use-module (web response)
+ #:use-module (knots timeout)
+ #:use-module (knots non-blocking)
#:use-module ((guix-build-coordinator utils)
#:select (call-with-delay-logging))
#:use-module ((guix-build-coordinator utils fibers)
#:select (retry-on-error))
#:use-module (guix-qa-frontpage mumi)
+ #:use-module (guix-qa-frontpage utils)
#:use-module (guix-qa-frontpage debbugs)
#:export (%patchwork-instance
- latest-patchwork-series-by-issue))
+ %patchwork-series-default-count
+ latest-patchwork-series-by-issue
+ latest-patchwork-series-for-issue))
(define %patchwork-instance
(make-parameter "https://patches.guix-patches.cbaines.net"))
@@ -77,12 +85,16 @@
(retry-on-error
(lambda ()
(http-request uri
- #:decode-body? #f))
+ #:port (non-blocking-open-socket-for-uri uri)
+ #:decode-body? #f
+ #:streaming? #t))
#:times 2
#:delay 3)))
(values
- (json-string->scm (utf8->string body))
+ (let ((json (json->scm body)))
+ (close-port body)
+ json)
(and=> (assq-ref (response-headers response) 'link)
(lambda (link-header)
(and=>
@@ -98,15 +110,95 @@
(assq-ref link-details 'uri)
(uri-scheme uri))))))))))
+(define (parse-patch-name name)
+ (let ((args
+ (and
+ (string-prefix? "[" name)
+ (let ((stop (string-index name #\])))
+ (substring name 1 stop))))
+ (as-bug-number
+ (lambda (arg)
+ (and (string-prefix? "bug#" arg)
+ (string->number (substring arg (string-length "bug#"))))))
+ (as-v2
+ (lambda (arg)
+ (and (string-prefix? "v" arg)
+ (string->number (substring arg 1)))))
+ (as-patch-number
+ (lambda (arg)
+ (match (string-split arg #\/)
+ (((= string->number index) (= string->number total))
+ (and index total (<= index total)
+ (cons index total)))
+ (else #f)))))
+ (let analyze ((bug-number #f)
+ (branch "master")
+ (version 1)
+ (index 1)
+ (total 1)
+ (arguments
+ (if args
+ (string-split args #\,)
+ '())))
+ (match arguments
+ ((or ("") ())
+ `((bug-number . ,bug-number)
+ (branch . ,branch)
+ (version . ,version)
+ (index . ,index)
+ (total . ,total)))
+ (((= as-bug-number (? number? new-bug-number))
+ arguments ...)
+ (analyze new-bug-number branch version index total arguments))
+ (((= as-v2 (? number? new-version))
+ arguments ...)
+ (analyze bug-number branch new-version index total arguments))
+ (((= as-patch-number ((? number? new-index) . (? number? new-total)))
+ arguments ...)
+ (analyze bug-number branch version new-index new-total arguments))
+ ((feature-branch arguments ...)
+ (analyze bug-number feature-branch version index total arguments))))))
+
+(define parse-issue-title
+ (let ((regex (make-regexp "\\[([A-Z\\_a-z0-9\\-]+)\\].*")))
+ (lambda (title)
+ (match (regexp-exec regex title)
+ (#f #f)
+ (m
+ (let ((branch (match:substring m 1)))
+ (if (string=? branch "PATCH")
+ #f
+ branch)))))))
+
+(define (patchwork-series->branch series)
+ (match (assoc-ref series "patches")
+ (#() "master")
+ (#(first-patch rest ...)
+ (let ((details
+ (parse-patch-name
+ (assoc-ref first-patch "name"))))
+ (assq-ref details 'branch)))))
+
+(define %patchwork-series-default-count
+ (make-parameter #f))
+
(define* (latest-patchwork-series-by-issue
#:key patchwork
- count)
+ (count (%patchwork-series-default-count)))
(define (string->issue-number str)
(string->number
(match:substring
(string-match "\\[?bug#([0-9]*)(,|:|\\])" str)
1)))
+ (define (strip-title-prefix str)
+ (if (string-prefix? "[" str)
+ (let ((start (string-index str #\])))
+ (if start
+ (string-drop str (+ 1 start))
+ str))
+ str))
+
(define issue-number-to-series-hash-table
(make-hash-table))
@@ -165,7 +257,10 @@
;; Need more series, so keep going
(let* ((series-batch
next-page-uri
- (request-patchwork-series patchwork-uri))
+ (with-port-timeouts
+ (lambda ()
+ (request-patchwork-series patchwork-uri))
+ #:timeout 60))
(batch-hash-table
(make-hash-table)))
@@ -230,14 +325,21 @@
#t))
(assq-ref mumi 'merged-with)))
(cons
- `(,@data
- (mumi . ,mumi))
+ `(,issue-number
+ .
+ (("name" . ,(strip-title-prefix
+ (assq-ref mumi 'title)))
+ ,@(alist-delete "name" (cdr data) string=?)
+ (branch . ,(or (parse-issue-title
+ (assq-ref mumi 'title))
+ (patchwork-series->branch data)))
+ (mumi . ,mumi)))
result)
result)))
result
series-by-issue-number
mumi-data)))))))
-
-
-
+(define* (latest-patchwork-series-for-issue issue-number #:key patchwork)
+ (assq-ref (latest-patchwork-series-by-issue #:patchwork patchwork)
+ issue-number))
diff --git a/guix-qa-frontpage/server.scm b/guix-qa-frontpage/server.scm
index ab0680a..4beaf09 100644
--- a/guix-qa-frontpage/server.scm
+++ b/guix-qa-frontpage/server.scm
@@ -32,18 +32,21 @@
#:use-module (fibers)
#:use-module (fibers scheduler)
#:use-module (fibers conditions)
+ #:use-module (knots)
+ #:use-module (knots web-server)
+ #:use-module (knots parallelism)
#:use-module (guix store)
+ #:use-module (knots web-server)
#:use-module ((guix build syscalls)
#:select (set-thread-name))
+ #:use-module ((guix-data-service utils)
+ #:select (delete-duplicates/sort!))
#:use-module (guix-data-service web util)
#:use-module ((guix-data-service web query-parameters)
#:select (parse-query-string))
#:use-module ((guix-build-coordinator utils)
#:select (with-time-logging
- get-port-metrics-updater
- call-with-delay-logging))
- #:use-module ((guix-build-coordinator utils fibers)
- #:select (run-server/patched call-with-sigint))
+ call-with-delay-logging))
#:use-module (guix-qa-frontpage database)
#:use-module (guix-qa-frontpage derivation-changes)
#:use-module (guix-qa-frontpage reproducible-builds)
@@ -53,6 +56,7 @@
#:use-module (guix-qa-frontpage branch)
#:use-module (guix-qa-frontpage package)
#:use-module (guix-qa-frontpage issue)
+ #:use-module (guix-qa-frontpage utils)
#:use-module (guix-qa-frontpage git-repository)
#:use-module (guix-qa-frontpage manage-builds)
#:use-module (guix-qa-frontpage manage-patch-branches)
@@ -98,17 +102,20 @@
(static-asset-from-store-renderer doc-dir)
(static-asset-from-directory-renderer doc-dir)))
+ (define plain-metrics-registry
+ (make-metrics-registry))
+
(define gc-metrics-updater!
- (get-gc-metrics-updater metrics-registry))
+ (get-gc-metrics-updater plain-metrics-registry))
- (define port-metrics-updater!
- (get-port-metrics-updater metrics-registry))
+ (define process-metrics-updater!
+ (get-process-metrics-updater plain-metrics-registry))
(define guile-time-metrics-updater
(let ((internal-real-time
- (make-gauge-metric metrics-registry "guile_internal_real_time"))
+ (make-gauge-metric plain-metrics-registry "guile_internal_real_time"))
(internal-run-time
- (make-gauge-metric metrics-registry "guile_internal_run_time")))
+ (make-gauge-metric plain-metrics-registry "guile_internal_run_time")))
(lambda ()
(metric-set internal-real-time
(get-internal-real-time))
@@ -143,26 +150,38 @@
(request-uri request))))))
(('GET "metrics")
(gc-metrics-updater!)
- (port-metrics-updater!)
+ (process-metrics-updater!)
(guile-time-metrics-updater)
(list (build-response
#:code 200
#:headers '((content-type . (text/plain))
(vary . (accept))))
- (lambda (port)
- (write-metrics metrics-registry port))))
+ (call-with-output-string
+ (lambda (port)
+ (write-metrics metrics-registry port)
+ (write-metrics plain-metrics-registry port)))))
(('GET "branches")
(let ((branches
(with-sqlite-cache
database
- 'branches
- (lambda ()
- (list-branches
- (list-branches-url 2)))
- #:ttl 60)))
- (render-html
- #:sxml
- (branches-view branches))))
+ 'list-non-master-branches
+ list-non-master-branches
+ #:ttl 300)))
+ (case (most-appropriate-mime-type
+ '(application/json text/html)
+ mime-types)
+ ((application/json)
+ (render-json
+ `((branches . ,(list->vector
+ (map (match-lambda
+ ((name . details)
+ `((name . ,name)
+ ,@details)))
+ branches))))))
+ (else
+ (render-html
+ #:sxml
+ (branches-view branches))))))
(('GET "branch" "master")
(let ((substitute-availability
systems-with-low-substitute-availability
@@ -179,7 +198,7 @@
package-reproducibility))))
(('GET "branch" branch)
(let ((revisions
- derivation-changes
+ derivation-changes-counts
substitute-availability
package-reproducibility
up-to-date-with-master
@@ -204,25 +223,33 @@
#:sxml
(branch-view branch
revisions
- derivation-changes
+ derivation-changes-counts
substitute-availability
package-reproducibility
up-to-date-with-master
master-branch-systems-with-low-substitute-availability))))
(('GET "branch" branch "package-changes")
- (let ((revisions
- derivation-changes
- substitute-availability
- package-reproducibility
- up-to-date-with-master
- (with-sqlite-cache
- database
- 'branch-data
- branch-data
- #:args
- (list branch)
- #:version 3
- #:ttl 6000)))
+ (let* ((revisions
+ derivation-changes-counts
+ substitute-availability
+ package-reproducibility
+ up-to-date-with-master
+ (with-sqlite-cache
+ database
+ 'branch-data
+ branch-data
+ #:args
+ (list branch)
+ #:version 3
+ #:ttl 6000))
+ (derivation-changes
+ (with-sqlite-cache
+ database
+ 'branch-derivation-changes-data
+ branch-derivation-changes-data/all-systems
+ #:args
+ (list revisions)
+ #:ttl 6000)))
(render-html
#:sxml
(branch-package-changes-view branch
@@ -257,16 +284,13 @@
symbol-key
#f))))
query-params))
- (latest-series-branches
- (map
- (match-lambda
- ((_ . series)
- (patchwork-series->branch series)))
- latest-series))
(branch-options
- (sort (delete-duplicates
- latest-series-branches)
- string<?))
+ (reverse
+ (delete-duplicates/sort!
+ (map (lambda (series)
+ (assq-ref series 'branch))
+ latest-series)
+ string<?)))
(filtered-branches
(filter-map
(match-lambda
@@ -277,7 +301,7 @@
query-params))
(latest-series-with-overall-statuses
(filter-map
- (lambda (series branch)
+ (lambda (series)
(let ((overall-status
(with-sqlite-cache
database
@@ -285,7 +309,9 @@
(const 'unknown)
#:store-computed-value? #f
#:args (list (first series))
- #:ttl 3600)))
+ #:ttl 3600))
+ (branch
+ (assq-ref series 'branch)))
(if (and (or (null? filtered-statuses)
(member overall-status
filtered-statuses))
@@ -295,8 +321,7 @@
`((branch . ,branch)
(overall-status . ,overall-status)))
#f)))
- latest-series
- latest-series-branches))
+ latest-series))
(sorted-latest-series
(sort
latest-series-with-overall-statuses
@@ -590,13 +615,12 @@
</svg>"))
port)))))
(('GET "issue" number)
- (let ((series (assq-ref (with-sqlite-cache
- database
- 'latest-patchwork-series-by-issue
- latest-patchwork-series-by-issue
- #:args `(#:count ,patch-issues-to-show)
- #:ttl 1800)
- (string->number number))))
+ (let ((series (with-sqlite-cache
+ database
+ 'latest-patchwork-series-for-issue
+ latest-patchwork-series-for-issue
+ #:args (list (string->number number))
+ #:ttl 1800)))
(if series
(let* ((base-and-target-refs
derivation-changes
@@ -616,8 +640,6 @@
(select-create-branch-for-issue-log
database
number))
- (branch
- (patchwork-series->branch series))
(master-branch-substitute-availability
systems-with-low-substitute-availability
master-branch-package-reproducibility
@@ -630,7 +652,7 @@
(render-html
#:sxml (issue-view number
series
- branch
+ (assq-ref series 'branch)
(assq-ref (assq-ref series 'mumi)
'tags)
base-and-target-refs
@@ -769,6 +791,13 @@ has no patches or has been closed.")
(render-html
#:sxml (package-view package-data))))
+ (('GET "robots") ; robots.txt
+ (render-text
+ "User-agent: *
+Disallow: /patches
+Disallow: /issue
+"))
+
(('GET "README")
(let ((filename (string-append doc-dir "/README.html")))
(if (file-exists? filename)
@@ -801,27 +830,23 @@ has no patches or has been closed.")
(request-method request)
(uri-path (request-uri request))))
- (call-with-error-handling
- (lambda ()
- (let-values (((request-components mime-types)
- (request->path-components-and-mime-type request)))
- (call-with-delay-logging
- controller
- #:threshold 30
- #:args (list request
- (cons (request-method request)
- request-components)
- mime-types
- body))))
- #:on-error 'backtrace
- #:post-error (lambda args
- (render-html #:sxml (error-page args)
- #:code 500))))
+ (let ((request-components
+ mime-types
+ (request->path-components-and-mime-type request)))
+ (call-with-delay-logging
+ controller
+ #:threshold 30
+ #:args (list request
+ (cons (request-method request)
+ request-components)
+ mime-types
+ body))))
(define* (start-guix-qa-frontpage port host assets-directory
database metrics-registry
#:key (controller-args '())
submit-builds?
+ manage-patch-branches?
patch-issues-to-show
generate-reproducible.json)
(define controller
@@ -831,6 +856,11 @@ has no patches or has been closed.")
(when generate-reproducible.json
(start-generate-reproducible.json-thread))
+ (when manage-patch-branches?
+ (start-manage-patch-branches-thread database
+ metrics-registry
+ #:series-count patch-issues-to-show))
+
(let ((finished? (make-condition)))
(call-with-new-thread
(lambda ()
@@ -841,14 +871,37 @@ has no patches or has been closed.")
(run-fibers
(lambda ()
+ (start-refresh-patch-branches-data-fiber
+ database
+ metrics-registry
+ #:number-of-series-to-refresh patch-issues-to-show)
+
+ (start-refresh-non-patch-branches-data-fiber database
+ metrics-registry)
+
(when submit-builds?
- (start-submit-patch-builds-fiber database
- "http://127.0.0.1:8746"
- "https://data.qa.guix.gnu.org"
- metrics-registry
- #:series-count
- patch-issues-to-show))
+ (parameterize
+ ((%fiberized-submit-build
+ (fiberize (lambda args
+ (call-with-duration-metric
+ metrics-registry
+ "submit_build_duration_seconds"
+ (lambda ()
+ (apply submit-build args))))
+ #:parallelism 8)))
+
+ (start-submit-patch-builds-fiber database
+ "http://127.0.0.1:8746"
+ "https://data.qa.guix.gnu.org"
+ metrics-registry
+ #:series-count
+ patch-issues-to-show)
+ (start-submit-branch-builds-fiber database
+ "http://127.0.0.1:8746"
+ "https://data.qa.guix.gnu.org"
+ metrics-registry)))
(wait finished?))
+ #:hz 0
#:parallelism 1)))
(call-with-sigint
@@ -871,12 +924,20 @@ has no patches or has been closed.")
(iota (length schedulers))
schedulers))
- (run-server/patched
- (lambda (request body)
- (apply values (handler request body controller)))
+ (run-knots-web-server
+ (lambda (request)
+ (apply values (handler request
+ (read-request-body request)
+ controller)))
+ #:exception-handler
+ (lambda (exn)
+ (apply values
+ (render-html #:sxml (error-page exn)
+ #:code 500)))
#:host host
#:port port)
(wait finished?))
- #:parallelism 2))
+ #:hz 0
+ #:parallelism 1))
finished?)))
diff --git a/guix-qa-frontpage/utils.scm b/guix-qa-frontpage/utils.scm
index d96db57..63b741c 100644
--- a/guix-qa-frontpage/utils.scm
+++ b/guix-qa-frontpage/utils.scm
@@ -18,172 +18,55 @@
(define-module (guix-qa-frontpage utils)
#:use-module (srfi srfi-1)
- #:use-module (srfi srfi-9)
- #:use-module (ice-9 q)
- #:use-module (ice-9 iconv)
+ #:use-module (srfi srfi-71)
#:use-module (ice-9 match)
- #:use-module (ice-9 format)
#:use-module (ice-9 threads)
- #:use-module (ice-9 atomic)
- #:use-module (ice-9 textual-ports)
- #:use-module (ice-9 rdelim)
- #:use-module (ice-9 binary-ports)
- #:use-module (ice-9 exceptions)
- #:use-module (rnrs bytevectors)
- #:use-module (ice-9 suspendable-ports)
- #:use-module ((ice-9 ports internal) #:select (port-poll
- port-read-wait-fd
- port-write-wait-fd))
- #:use-module (web uri)
- #:use-module (web http)
- #:use-module (web client)
- #:use-module (web request)
- #:use-module (web response)
#:use-module (fibers)
- #:use-module (fibers timers)
#:use-module (fibers channels)
- #:use-module (fibers scheduler)
- #:use-module (fibers conditions)
- #:use-module (fibers operations)
- #:export (port-read-timeout-error?
- port-write-timeout-error?
- with-fibers-port-timeouts))
-
-(define (readable? port)
- "Test if PORT is writable."
- (match (select (vector port) #() #() 0)
- ((#() #() #()) #f)
- ((#(_) #() #()) #t)))
-
-(define (writable? port)
- "Test if PORT is writable."
- (match (select #() (vector port) #() 0)
- ((#() #() #()) #f)
- ((#() #(_) #()) #t)))
-
-(define (make-wait-operation ready? schedule-when-ready port port-ready-fd this-procedure)
- (make-base-operation #f
- (lambda _
- (and (ready? (port-ready-fd port)) values))
- (lambda (flag sched resume)
- (define (commit)
- (match (atomic-box-compare-and-swap! flag 'W 'S)
- ('W (resume values))
- ('C (commit))
- ('S #f)))
- (schedule-when-ready
- sched (port-ready-fd port) commit))))
-
-(define (wait-until-port-readable-operation port)
- "Make an operation that will succeed when PORT is readable."
- (unless (input-port? port)
- (error "refusing to wait forever for input on non-input port"))
- (make-wait-operation readable? schedule-task-when-fd-readable port
- port-read-wait-fd
- wait-until-port-readable-operation))
-
-(define (wait-until-port-writable-operation port)
- "Make an operation that will succeed when PORT is writable."
- (unless (output-port? port)
- (error "refusing to wait forever for output on non-output port"))
- (make-wait-operation writable? schedule-task-when-fd-writable port
- port-write-wait-fd
- wait-until-port-writable-operation))
-
-
-
-(define &port-timeout
- (make-exception-type '&port-timeout
- &external-error
- '(port)))
-
-(define make-port-timeout-error
- (record-constructor &port-timeout))
-
-(define port-timeout-error?
- (record-predicate &port-timeout))
-
-(define &port-read-timeout
- (make-exception-type '&port-read-timeout
- &port-timeout
- '()))
-
-(define make-port-read-timeout-error
- (record-constructor &port-read-timeout))
-
-(define port-read-timeout-error?
- (record-predicate &port-read-timeout))
-
-(define &port-write-timeout
- (make-exception-type '&port-write-timeout
- &port-timeout
- '()))
-
-(define make-port-write-timeout-error
- (record-constructor &port-write-timeout))
-
-(define port-write-timeout-error?
- (record-predicate &port-write-timeout))
-
-(define* (with-fibers-port-timeouts thunk
- #:key timeout
- (read-timeout timeout)
- (write-timeout timeout))
- (define (no-fibers-wait port mode timeout)
- (define poll-timeout-ms 200)
-
- ;; When the GC runs, it restarts the poll syscall, but the timeout
- ;; remains unchanged! When the timeout is longer than the time
- ;; between the syscall restarting, I think this renders the
- ;; timeout useless. Therefore, this code uses a short timeout, and
- ;; repeatedly calls poll while watching the clock to see if it has
- ;; timed out overall.
- (let ((timeout-internal
- (+ (get-internal-real-time)
- (* internal-time-units-per-second
- timeout))))
- (let loop ((poll-value
- (port-poll port mode poll-timeout-ms)))
- (if (= poll-value 0)
- (if (> (get-internal-real-time)
- timeout-internal)
- (raise-exception
- (if (string=? mode "r")
- (make-port-read-timeout-error port)
- (make-port-write-timeout-error port)))
- (loop (port-poll port mode poll-timeout-ms)))
- poll-value))))
-
- (unless read-timeout
- (if timeout
- (error "unset read-timeout")
- (error "unset timeout")))
- (unless write-timeout
- (error "unset write-timeout"))
-
- (parameterize
- ((current-read-waiter
- (lambda (port)
- (if (current-scheduler)
- (perform-operation
- (choice-operation
- (wait-until-port-readable-operation port)
- (wrap-operation
- (sleep-operation read-timeout)
- (lambda ()
- (raise-exception
- (make-port-read-timeout-error thunk port))))))
- (no-fibers-wait port "r" read-timeout))))
- (current-write-waiter
- (lambda (port)
- (if (current-scheduler)
- (perform-operation
- (choice-operation
- (wait-until-port-writable-operation port)
- (wrap-operation
- (sleep-operation write-timeout)
- (lambda ()
- (raise-exception
- (make-port-write-timeout-error thunk port))))))
- (no-fibers-wait port "w" write-timeout)))))
- (thunk)))
+ #:use-module (knots)
+ #:use-module (zlib)
+ #:export (non-blocking
+ call-with-zlib-input-port*))
+
+(define (non-blocking thunk)
+ (let ((channel (make-channel)))
+ (call-with-default-io-waiters
+ (lambda ()
+ (call-with-new-thread
+ (lambda ()
+ (with-exception-handler
+ (lambda (exn)
+ (put-message channel `(exception ,exn)))
+ (lambda ()
+ (with-throw-handler #t
+ (lambda ()
+ (call-with-values thunk
+ (lambda values
+ (put-message channel `(values ,@values)))))
+ (lambda args
+ (display (backtrace) (current-error-port))
+ (newline (current-error-port)))))
+ #:unwind? #t)))))
+ (match (get-message channel)
+ (('values . results)
+ (apply values results))
+ (('exception . exn)
+ (raise-exception exn)))))
+
+(define* (call-with-zlib-input-port* port proc
+ #:key
+ (format 'zlib)
+ (buffer-size %default-buffer-size))
+ "Call PROC with a port that wraps PORT and decompresses data read from it.
+PORT is closed upon completion. The zlib internal buffer size is set to
+BUFFER-SIZE bytes."
+ (let ((zlib (make-zlib-input-port port
+ #:format format
+ #:buffer-size buffer-size
+ #:close? #t)))
+ (call-with-values
+ (lambda ()
+ (proc zlib))
+ (lambda vals
+ (close-port zlib)
+ (apply values vals)))))
diff --git a/guix-qa-frontpage/view/branch.scm b/guix-qa-frontpage/view/branch.scm
index 5c7c94f..d7c93f7 100644
--- a/guix-qa-frontpage/view/branch.scm
+++ b/guix-qa-frontpage/view/branch.scm
@@ -5,6 +5,7 @@
#:use-module (ice-9 format)
#:use-module ((guix-data-service model utils) #:select (group-to-alist))
#:use-module (guix-qa-frontpage manage-builds)
+ #:use-module (guix-qa-frontpage guix-data-service)
#:use-module (guix-qa-frontpage derivation-changes)
#:use-module (guix-qa-frontpage view util)
#:use-module (guix-qa-frontpage view shared)
@@ -13,16 +14,11 @@
master-branch-view))
-(define (branch-view branch revisions derivation-changes
+(define (branch-view branch revisions derivation-changes-counts
substitute-availability
package-reproducibility
up-to-date-with-master
master-branch-systems-with-low-substitute-availability)
- (define derivation-changes-counts
- (if (assq-ref derivation-changes 'exception)
- derivation-changes
- (assq-ref derivation-changes 'counts)))
-
(layout
#:title (simple-format #f "Branch ~A" branch)
#:head
@@ -58,7 +54,9 @@ td.bad {
"View Git branch"))
(li
(a (@ (href ,(simple-format
- #f "https://data.qa.guix.gnu.org/repository/2/branch/~A"
+ #f "~A/repository/~A/branch/~A"
+ %data-service-url-base
+ %data-service-guix-repository-id
branch)))
"View branch with Guix Data Service"))))
diff --git a/guix-qa-frontpage/view/branches.scm b/guix-qa-frontpage/view/branches.scm
index 90d1da7..9573d2b 100644
--- a/guix-qa-frontpage/view/branches.scm
+++ b/guix-qa-frontpage/view/branches.scm
@@ -10,10 +10,22 @@
#:body
`((main
(table
+ (thead
+ (tr (th "Branch")
+ (th "Request to merge")))
(tbody
- ,@(map (lambda (branch-details)
- (let ((name (assoc-ref branch-details "name")))
- `(tr
- (td (a (@ (href ,(simple-format #f "/branch/~A" name)))
- ,name)))))
+ ,@(map (match-lambda
+ ((name . details)
+ (let ((issue-number
+ (assoc-ref details "issue_number")))
+ `(tr
+ (td (a (@ (href ,(simple-format #f "/branch/~A" name))
+ (style "font-family: monospace;"))
+ ,name))
+ (td ,@(if issue-number
+ `((a (@ (href ,(string-append
+ "https://issues.guix.gnu.org/"
+ (number->string issue-number))))
+ "#" ,issue-number))
+ '()))))))
branches)))))))
diff --git a/guix-qa-frontpage/view/home.scm b/guix-qa-frontpage/view/home.scm
index a25e486..3a1c1d9 100644
--- a/guix-qa-frontpage/view/home.scm
+++ b/guix-qa-frontpage/view/home.scm
@@ -23,6 +23,13 @@ dd {
dt {
margin-left: 2em;
}
+
+td.bad {
+ padding: 0.05rem 0.65rem;
+ font-weight: bold;
+
+ border: 0.3rem dashed red;
+}
"))
#:body
`((main
@@ -75,22 +82,30 @@ dt {
(tr (th "Branch")
(th "Request to merge")))
(tbody
- ,@(append-map
- (match-lambda
- ((branch . details)
- (let ((issue-number
- (assoc-ref details "issue_number")))
- `((tr
- (td (a (@ (href ,(string-append "/branch/" branch))
- (style "font-family: monospace;"))
- ,branch))
- (td ,@(if issue-number
- `((a (@ (href ,(string-append
- "https://issues.guix.gnu.org/"
- (number->string issue-number))))
- "#" ,issue-number))
- '())))))))
- branches)))))
+ ,@(if (assq-ref branches 'exception)
+ `((tr
+ (td (@ (colspan 2) (class "bad")
+ (style "white-space: normal;"))
+ "Exception fetching branches:"
+ (br)
+ ,(assq-ref branches 'exception))))
+
+ (append-map
+ (match-lambda
+ ((branch . details)
+ (let ((issue-number
+ (assoc-ref details "issue_number")))
+ `((tr
+ (td (a (@ (href ,(string-append "/branch/" branch))
+ (style "font-family: monospace;"))
+ ,branch))
+ (td ,@(if issue-number
+ `((a (@ (href ,(string-append
+ "https://issues.guix.gnu.org/"
+ (number->string issue-number))))
+ "#" ,issue-number))
+ '())))))))
+ branches))))))
(h2 "Topics")
(div
(@ (class "row"))
diff --git a/guix-qa-frontpage/view/issue.scm b/guix-qa-frontpage/view/issue.scm
index 4e851f8..567ba24 100644
--- a/guix-qa-frontpage/view/issue.scm
+++ b/guix-qa-frontpage/view/issue.scm
@@ -78,7 +78,7 @@
(simple-format
#f
"~A/log/?h=~A&qt=range&q=~A..~A"
- "https://git.guix-patches.cbaines.net/guix-patches"
+ "https://git.qa.guix.gnu.org/guix-patches"
branch-name base-tag branch-name))))
"View Git branch")))
'())
@@ -258,7 +258,10 @@
patches to record a review, which will highlight that these patches should be
ready to merge.")
- (p "Here's a list of common things to check, tick them off as you review
+ (p "There's some "
+ (a (@ (href "https://guix.gnu.org/manual/devel/en/html_node/Reviewing-the-Work-of-Others.html"))
+ "guidance in the manual about reviewing patches")
+ ". Here's a list of common things to check, tick them off as you review
the patches:"))
,@(map
@@ -475,5 +478,5 @@ Guix QA review form submission:"
(uri-encode email-text))))
(b "Open mail client to send review email"))
(p "If the above link doesn't work for you, the contents of the suggested email is given below, and can be sent "
- (strong "to control@debbugs.gnu.org and 66195@debbugs.gnu.org"))
+ (strong "to control@debbugs.gnu.org and " ,issue-number "@debbugs.gnu.org"))
(pre ,email-text)))))
diff --git a/guix-qa-frontpage/view/patches.scm b/guix-qa-frontpage/view/patches.scm
index fc5c575..dc0af45 100644
--- a/guix-qa-frontpage/view/patches.scm
+++ b/guix-qa-frontpage/view/patches.scm
@@ -127,47 +127,11 @@ will appear first.")
,id))
(td
(@ (style "vertical-align: middle;"))
- ,@(cond
- ((eq? status 'reviewed-looks-good)
- `((span (@ (aria-label "status: darkgreen")
- (class "darkgreen-dot"))
- (*ENTITY* "#10004"))))
- ((eq? status 'important-checks-passing)
- `((span (@ (aria-label "status: green")
- (class "green-dot"))
- (*ENTITY* "#10004"))))
- ((eq? status 'important-checks-failing)
- `((span (@ (aria-label "status: red")
- (class "red-dot"))
- (*ENTITY* "#10005"))))
- ((eq? status 'failed-to-apply-patches)
- `((span (@ (aria-label "status: darkred")
- (class "darkred-dot"))
- (*ENTITY* "#10005"))))
- ((eq? status 'large-number-of-builds)
- `((span (@ (aria-label "status: purple")
- (class "purple-dot"))
- (*ENTITY* "#10005"))))
- ((eq? status 'waiting-for-build-results)
- `((span (@ (aria-label "status: lightblue")
- (class "lightblue-dot"))
- (*ENTITY* "#127959"))))
- ((eq? status 'patches-missing)
- `((span (@ (aria-label "status: pink")
- (class "pink-dot"))
- "?")))
- ((eq? status 'guix-data-service-failed)
- `((span (@ (aria-label "status: yellow")
- (class "yellow-dot"))
- (*ENTITY* "#10005"))))
- ((eq? status 'needs-looking-at)
- `((span (@ (aria-label "status: orange")
- (class "orange-dot"))
- (*ENTITY* "#9888"))))
- (else
- `((span (@ (aria-label "status: grey")
- (class "grey-dot"))
- "?")))))
+ ,(status->issue-status-span status))
(td (@ (style "text-align: left;"))
+ ,@(let ((branch (assq-ref details 'branch)))
+ (if (string=? branch "master")
+ '()
+ `((code ,branch))))
,(assoc-ref details "name"))))))
latest-series)))))))
diff --git a/guix-qa-frontpage/view/shared.scm b/guix-qa-frontpage/view/shared.scm
index 708ac63..804923b 100644
--- a/guix-qa-frontpage/view/shared.scm
+++ b/guix-qa-frontpage/view/shared.scm
@@ -745,55 +745,64 @@
(td (@ (colspan 10)
(class "bad"))
"Comparison unavailable"
- ,@(or (and=>
- (assq-ref derivation-changes-counts
- 'invalid_query_parameters)
- (lambda (params)
- (append-map
- (match-lambda
- ((param . details)
- (let ((error
- (assq-ref details 'error)))
- (cond
- ((member param '("base_commit"
- "target_commit"))
- `((br)
- (a
- (@ (href
- ,(string-append
- "https://data.qa.guix.gnu.org"
- "/revision/"
- (assq-ref
- revisions
- (if (string=? param "base_commit")
- 'base
- 'target)))))
- ,(cond
- ((eq? error 'unknown-commit)
- (string-append
- (if (string=? param "base_commit")
- "Base revision "
- "Target revision ")
- "unknown to the data service."))
- ((member error
- '(yet-to-process-revision
- failed-to-process-revision))
- (simple-format
- #f "~A to process ~A"
- (if (eq? error 'yet-to-process-revision)
- "Yet"
- "Failed")
- (if (string=? param "base_commit")
- "base revision (from master branch)"
- "target revision")))
- (else
- (string-append
- "Error with "
- (if (string=? param "base_commit")
- "base revision."
- "target revision.")))))))))))
- params)))
- '()))))))))
+ ,@(cond
+ ((eq? (assq-ref derivation-changes-counts 'exception)
+ 'guix-data-service-invalid-parameters)
+ (append-map
+ (match-lambda
+ ((param . details)
+ (let ((error
+ (assq-ref details 'error)))
+ (cond
+ ((member param '("base_commit"
+ "target_commit"))
+ `((br)
+ (a
+ (@ (href
+ ,(string-append
+ "https://data.qa.guix.gnu.org"
+ "/revision/"
+ (assq-ref
+ revisions
+ (if (string=? param "base_commit")
+ 'base
+ 'target)))))
+ ,(cond
+ ((eq? error 'unknown-commit)
+ (string-append
+ (if (string=? param "base_commit")
+ "Base revision "
+ "Target revision ")
+ "unknown to the data service."))
+ ((member error
+ '(yet-to-process-revision
+ failed-to-process-revision))
+ (simple-format
+ #f "~A to process ~A"
+ (if (eq? error 'yet-to-process-revision)
+ "Yet"
+ "Failed")
+ (if (string=? param "base_commit")
+ "base revision (from master branch)"
+ "target revision")))
+ (else
+ (string-append
+ "Error with "
+ (if (string=? param "base_commit")
+ "base revision."
+ "target revision.")))))))))))
+ (assq-ref derivation-changes-counts
+ 'invalid_query_parameters)))
+ ((eq? (assq-ref derivation-changes-counts 'exception)
+ 'guix-data-service-exception)
+ (let ((url
+ (assq-ref derivation-changes-counts 'url)))
+ `((br)
+ "Exception fetching data from "
+ (a (@ (href ,url))
+ ,url))))
+ (else
+ '())))))))))
(define (package-cross-changes-summary-table revisions
cross-derivation-changes-counts
diff --git a/guix-qa-frontpage/view/util.scm b/guix-qa-frontpage/view/util.scm
index 60ec66a..497e718 100644
--- a/guix-qa-frontpage/view/util.scm
+++ b/guix-qa-frontpage/view/util.scm
@@ -45,6 +45,8 @@
table/branches-with-most-recent-commits
render-html
+ render-json
+ render-text
general-not-found
error-page
@@ -417,6 +419,12 @@ main > header {
(define render-html
guix-data-service:render-html)
+(define render-json
+ guix-data-service:render-json)
+
+(define render-text
+ guix-data-service:render-text)
+
(define (general-not-found header-text body)
(layout
#:body
@@ -424,17 +432,14 @@ main > header {
(h1 ,header-text)
(p ,body)))))
-(define* (error-page #:optional error)
+(define* (error-page #:optional exn)
(layout
#:body
`((main
(h1 "An error occurred")
(p "Sorry about that!")
- ,@(if error
- (match error
- ((key . args)
- `((b ,key)
- (pre ,args))))
+ ,@(if exn
+ `((pre ,exn))
'())))))
(define file-mime-types