aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--guix-dev.scm1
-rw-r--r--guix-qa-frontpage/branch.scm106
-rw-r--r--guix-qa-frontpage/database.scm107
-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.scm139
-rw-r--r--guix-qa-frontpage/manage-builds.scm412
-rw-r--r--guix-qa-frontpage/manage-patch-branches.scm54
-rw-r--r--guix-qa-frontpage/mumi.scm165
-rw-r--r--guix-qa-frontpage/patchwork.scm38
-rw-r--r--guix-qa-frontpage/server.scm171
-rw-r--r--guix-qa-frontpage/utils.scm164
-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/issue.scm9
-rw-r--r--guix-qa-frontpage/view/shared.scm107
-rw-r--r--guix-qa-frontpage/view/util.scm17
-rw-r--r--qa-information-flow.plantuml13
-rw-r--r--scripts/guix-qa-frontpage.in9
21 files changed, 1136 insertions, 838 deletions
diff --git a/guix-dev.scm b/guix-dev.scm
index e820570..595be5a 100644
--- a/guix-dev.scm
+++ b/guix-dev.scm
@@ -52,6 +52,7 @@
guix-data-service
guile-json-4
guile-fibers-1.1
+ guile-knots
guile-kolam
guile-git
guile-debbugs
diff --git a/guix-qa-frontpage/branch.scm b/guix-qa-frontpage/branch.scm
index 719b350..6276476 100644
--- a/guix-qa-frontpage/branch.scm
+++ b/guix-qa-frontpage/branch.scm
@@ -23,12 +23,16 @@
#: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)
@@ -38,6 +42,8 @@
#: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
@@ -63,11 +69,12 @@
`(("issue_number" . ,issue-number)
("issue_date" . ,(assoc-ref issue "date"))
("blocked_by"
- . ,(map (lambda (issue)
- (assoc-ref issue "number"))
- (or (and=> (assoc-ref issue "blocked_by")
- vector->list)
- '()))))))))
+ . ,(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
@@ -98,10 +105,11 @@
"master")
(string-prefix? "version-"
(assoc-ref branch "name"))
+ (string-prefix? "wip-"
+ (assoc-ref branch "name"))
(string=? (assoc-ref branch "commit")
"")))
- (list-branches
- (list-branches-url 2))))))
+ (get-git-remote-branches "origin")))))
(let* ((initial-ordered-branches
(stable-sort
branches
@@ -145,9 +153,11 @@
(assq-ref initial-ordering-index-by-branch
(car b)))
(a-blocked-by
- (or (assoc-ref (cdr a) "blocked_by") '()))
+ (vector->list
+ (or (assoc-ref (cdr a) "blocked_by") #())))
(b-blocked-by
- (or (assoc-ref (cdr b) "blocked_by") '())))
+ (vector->list
+ (or (assoc-ref (cdr b) "blocked_by") #()))))
(<
(if (null? a-blocked-by)
a-initial-ordering-index
@@ -182,6 +192,26 @@
(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
(get-commit
@@ -217,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
@@ -250,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?))
@@ -260,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
@@ -303,10 +337,12 @@
(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"))))
@@ -379,7 +415,7 @@
(string-prefix? "version-"
(assoc-ref branch "name"))))
(list-branches
- (list-branches-url 2))))
+ (list-branches-url %data-service-guix-repository-id))))
#:ttl 0)))
(for-each
diff --git a/guix-qa-frontpage/database.scm b/guix-qa-frontpage/database.scm
index c44d83a..06ce3bd 100644
--- a/guix-qa-frontpage/database.scm
+++ b/guix-qa-frontpage/database.scm
@@ -28,16 +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
- make-queueing-channel))
+ #:select (retry-on-error))
#:use-module (guix-qa-frontpage guix-data-service)
#:export (setup-database
@@ -58,14 +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
- set-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))
@@ -145,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
@@ -182,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
@@ -220,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)
@@ -246,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
@@ -258,10 +261,11 @@ PRAGMA optimize;")))
(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-channel!
+ (set-database-writer-thread-set-channel!
database
- (make-queueing-channel
- (database-writer-thread-channel database)))
+ (spawn-queueing-fiber
+ (thread-pool-channel
+ (database-writer-thread-set database))))
(spawn-fiber
(lambda ()
@@ -322,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)))
@@ -346,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))))
@@ -430,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
@@ -464,12 +472,17 @@ 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)
@@ -557,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
@@ -613,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 94267a5..beed41f 100644
--- a/guix-qa-frontpage/issue.scm
+++ b/guix-qa-frontpage/issue.scm
@@ -23,10 +23,12 @@
#: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)
@@ -179,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
@@ -290,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
@@ -310,7 +308,20 @@
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)
@@ -328,6 +339,17 @@
(take latest-series number-of-series-to-refresh)
latest-series)))
+ (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)
+
(non-blocking
(lambda ()
(update-repository!)))
@@ -351,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
@@ -388,7 +403,7 @@
#:args (list issue-number)
#:ttl 0)))
#:unwind? #t)))
- 5
+ 50
series-to-refresh)))
(spawn-fiber
diff --git a/guix-qa-frontpage/manage-builds.scm b/guix-qa-frontpage/manage-builds.scm
index d07a773..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)
@@ -45,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"))
@@ -89,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
@@ -210,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
@@ -240,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
@@ -263,106 +281,105 @@
(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)))
- (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)
- (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)))
- (submit-builds-for-category build-coordinator
- guix-data-service
- 'branch
- branch
- derivations-and-priorities
- (set)
- branch-commit)))))
- (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)
@@ -426,7 +443,9 @@
((name . details)
(->bool (assoc-ref details "issue_number"))))
all-branches)
- 2))
+ ;; TODO The builds for the first branch should be mostly
+ ;; complete before submitting builds for any others
+ 1))
(branch-names
(map car branches)))
@@ -464,7 +483,7 @@
(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
@@ -482,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
@@ -497,7 +517,9 @@
#t
#t
#t
- tags))
+ tags
+ #:skip-updating-derived-priorities?
+ skip-updating-derived-priorities?))
#:timeout 240)))
(let ((no-build-submitted-response
(assoc-ref response "no-build-submitted")))
@@ -534,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
@@ -596,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
@@ -626,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
@@ -655,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
@@ -729,7 +783,8 @@
build-ids-to-keep-set
target-commit
#:key build-limit
- (build-count-priority-penalty (const 0)))
+ (build-count-priority-penalty (const 0))
+ skip-updating-derived-priorities?)
(define (submit-builds build-details
build-ids-to-keep-set)
(define submit-build/fiberized
@@ -748,7 +803,9 @@
((key . ,category-name)
(value . ,category-value))
((key . revision)
- (value . ,target-commit)))))))
+ (value . ,target-commit)))
+ #:skip-updating-derived-priorities?
+ skip-updating-derived-priorities?))))
(fibers-for-each submit-single build-details))
@@ -833,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 16bfbd9..7cb9cee 100644
--- a/guix-qa-frontpage/manage-patch-branches.scm
+++ b/guix-qa-frontpage/manage-patch-branches.scm
@@ -17,16 +17,14 @@
#: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
@@ -127,7 +125,8 @@
(close-pipe pipe)
result))
-(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))
@@ -138,8 +137,7 @@
(let ((branch
(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")
@@ -166,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))
@@ -193,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
@@ -244,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)
@@ -325,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
@@ -344,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))
@@ -376,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)
@@ -435,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)
@@ -478,6 +491,7 @@
(const #t)
(lambda ()
(create-branch-for-issue database
+ latest-processed-master-revision
issue-number
patchwork-series))
#:unwind? #t))))
@@ -505,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 94c1842..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,22 +113,25 @@
(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)
(let ((response
(graphql-http-get "https://issues.guix.gnu.org/graphql"
@@ -80,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
@@ -98,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 049012f..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=>
@@ -167,9 +179,12 @@
(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
@@ -179,7 +194,9 @@
(define (strip-title-prefix str)
(if (string-prefix? "[" str)
(let ((start (string-index str #\])))
- (string-drop str (+ 1 start)))
+ (if start
+ (string-drop str (+ 1 start))
+ str))
str))
(define issue-number-to-series-hash-table
@@ -240,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)))
@@ -320,6 +340,6 @@
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 ccfa985..4beaf09 100644
--- a/guix-qa-frontpage/server.scm
+++ b/guix-qa-frontpage/server.scm
@@ -32,7 +32,11 @@
#: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)
@@ -42,9 +46,7 @@
#:select (parse-query-string))
#:use-module ((guix-build-coordinator utils)
#:select (with-time-logging
- 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)
@@ -162,14 +164,24 @@
(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
@@ -186,7 +198,7 @@
package-reproducibility))))
(('GET "branch" branch)
(let ((revisions
- derivation-changes
+ derivation-changes-counts
substitute-availability
package-reproducibility
up-to-date-with-master
@@ -211,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
@@ -595,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
@@ -772,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)
@@ -804,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
@@ -834,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 ()
@@ -844,9 +871,6 @@ has no patches or has been closed.")
(run-fibers
(lambda ()
- (%fiberized-submit-build
- (fiberize submit-build #:parallelism 8))
-
(start-refresh-patch-branches-data-fiber
database
metrics-registry
@@ -856,16 +880,26 @@ has no patches or has been closed.")
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)
- (start-submit-branch-builds-fiber database
- "http://127.0.0.1:8746"
- "https://data.qa.guix.gnu.org"
- metrics-registry))
+ (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)))
@@ -890,9 +924,16 @@ 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)
diff --git a/guix-qa-frontpage/utils.scm b/guix-qa-frontpage/utils.scm
index f0b47a9..63b741c 100644
--- a/guix-qa-frontpage/utils.scm
+++ b/guix-qa-frontpage/utils.scm
@@ -23,138 +23,50 @@
#:use-module (ice-9 threads)
#:use-module (fibers)
#:use-module (fibers channels)
- #:use-module ((guix-build-coordinator utils) #:select (with-port-timeouts))
- #:use-module (guix-build-coordinator utils fibers)
- #:export (fiberize
- fibers-map
- fibers-batch-for-each
- fibers-for-each
- non-blocking)
- #:re-export (with-fibers-port-timeouts))
-
-(define* (fiberize proc #:key (parallelism 1))
- (let ((channel (make-channel)))
- (for-each
- (lambda _
- (spawn-fiber
- (lambda ()
- (while #t
- (let ((reply-channel args (car+cdr
- (get-message channel))))
- (put-message
- reply-channel
- (with-exception-handler
- (lambda (exn)
- (cons 'exception exn))
- (lambda ()
- (with-throw-handler #t
- (lambda ()
- (call-with-values
- (lambda ()
- (apply proc args))
- (lambda vals
- (cons 'result vals))))
- (lambda _
- (backtrace))))
- #:unwind? #t)))))
- #:parallel? #t))
- (iota parallelism))
-
- (lambda args
- (let ((reply-channel (make-channel)))
- (put-message channel (cons reply-channel args))
- (match (get-message reply-channel)
- (('result . vals) (apply values vals))
- (('exception . exn) (raise-exception exn)))))))
-
-(define (fibers-map proc . lists)
- (let ((channels
- (apply
- map
- (lambda args
- (let ((channel (make-channel)))
- (spawn-fiber
- (lambda ()
- (put-message
- channel
- (with-exception-handler
- (lambda (exn)
- (cons 'exception exn))
- (lambda ()
- (with-throw-handler #t
- (lambda ()
- (call-with-values
- (lambda ()
- (apply proc args))
- (lambda val
- (cons 'result val))))
- (lambda _
- (backtrace))))
- #:unwind? #t))))
- channel))
- lists)))
- (map
- (match-lambda
- (('result . val) val)
- (('exception . exn) (raise-exception exn)))
- (map get-message channels))))
-
-(define (fibers-batch-for-each proc batch-size . lists)
- ;; Like split-at, but don't care about the order of the resulting lists, and
- ;; don't error if the list is shorter than i elements
- (define (split-at* lst i)
- (let lp ((l lst) (n i) (acc '()))
- (if (or (<= n 0) (null? l))
- (values (reverse! acc) l)
- (lp (cdr l) (- n 1) (cons (car l) acc)))))
-
- ;; As this can be called with lists with tens of thousands of items in them,
- ;; batch the
- (define (get-batch lists)
- (let ((split-lists
- (map (lambda (lst)
- (let ((batch rest (split-at* lst batch-size)))
- (cons batch rest)))
- lists)))
- (values (map car split-lists)
- (map cdr split-lists))))
-
- (let loop ((lists lists))
- (call-with-values
- (lambda ()
- (get-batch lists))
- (lambda (batch rest)
- (apply fibers-map proc batch)
- (unless (null? (car rest))
- (loop rest)))))
- *unspecified*)
-
-(define (fibers-for-each proc . lists)
- (apply fibers-batch-for-each proc 20 lists))
+ #:use-module (knots)
+ #:use-module (zlib)
+ #:export (non-blocking
+ call-with-zlib-input-port*))
(define (non-blocking thunk)
(let ((channel (make-channel)))
- (call-with-new-thread
+ (call-with-default-io-waiters
(lambda ()
- (with-exception-handler
- (lambda (exn)
- (put-message channel `(exception ,exn)))
- (lambda ()
- (with-throw-handler #t
- (lambda ()
- (call-with-values
- (lambda ()
- ;; This is mostly to set non fibers IO waiters
- (with-port-timeouts thunk
- #:timeout (* 300 1000)))
- (lambda values
- (put-message channel `(values ,@values)))))
- (lambda args
- (display (backtrace) (current-error-port))
- (newline (current-error-port)))))
- #:unwind? #t)))
+ (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/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/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
diff --git a/qa-information-flow.plantuml b/qa-information-flow.plantuml
index 5257b80..cd9f196 100644
--- a/qa-information-flow.plantuml
+++ b/qa-information-flow.plantuml
@@ -1,14 +1,13 @@
@startuml
-cloud "Running on beid" {
+cloud "Running on mago" {
component Patchwork [
Patchwork
patches.guix-patches.cbaines.net
]
- component patchesgit [
- Patches Git repository
- git.guix-patches.cbaines.net
- ]
+}
+
+cloud "Running on hydra-guix-130" {
component dataservice [
Guix Data Service
data.qa.guix.gnu.org
@@ -16,6 +15,10 @@ cloud "Running on beid" {
}
cloud "Running on bayfront" {
+ component patchesgit [
+ Patches Git repository
+ git.qa.guix.gnu.org
+ ]
component qafrontpage [
QA Frontpage
qa.guix.gnu.org
diff --git a/scripts/guix-qa-frontpage.in b/scripts/guix-qa-frontpage.in
index eee3b4c..c666901 100644
--- a/scripts/guix-qa-frontpage.in
+++ b/scripts/guix-qa-frontpage.in
@@ -267,7 +267,8 @@
(with-fluids ((%file-port-name-canonicalization 'none))
(parameterize
- ((%git-repository-location (string-append (getcwd) "/guix.git")))
+ ((%git-repository-location (string-append (getcwd) "/guix.git"))
+ (%patchwork-series-default-count patch-issues-to-show))
(let* ((metrics-registry (make-metrics-registry
#:namespace
"guixqafrontpage"))
@@ -275,11 +276,6 @@
(setup-database (assq-ref opts 'database)
metrics-registry)))
- (when (assq-ref opts 'manage-patch-branches)
- (start-manage-patch-branches-thread database
- metrics-registry
- #:series-count patch-issues-to-show))
-
(start-guix-qa-frontpage
(assq-ref opts 'port)
(assq-ref opts 'host)
@@ -289,5 +285,6 @@
#:controller-args `(#:doc-dir ,doc-dir
#:patch-issues-to-show ,patch-issues-to-show)
#:submit-builds? (assq-ref opts 'submit-builds)
+ #:manage-patch-branches? (assq-ref opts 'manage-patch-branches)
#:patch-issues-to-show patch-issues-to-show
#:generate-reproducible.json #t)))))))