ci: Properly construct URLs.
Implement a new function "api-url", which constructs URLs using relative URI and "resolve-uri-reference" (which implements the algorithm specified in RFC 3986 section 5.2.2) for building the URL, instead of just appending strings. This avoids issued if the server-url ends with a slash. Since "api-url" uses URI-objects, it makes sense to also construct the query-part of the URL here. For this "api-url" accepts optional key-value-pairs. New function "json-api-fetch" is a wrapper using "api-url". * guix/ci.scm (api-url): New function. (build): Use it. (json-api-fetch): New function. (queued-builds, latest-builds, evaluation, latest-evaluations, evaluation-jobs: Use it.
This commit is contained in:
		
							parent
							
								
									ccdf7b8006
								
							
						
					
					
						commit
						3ee0f170c8
					
				
					 1 changed files with 49 additions and 33 deletions
				
			
		
							
								
								
									
										78
									
								
								guix/ci.scm
									
										
									
									
									
								
							
							
						
						
									
										78
									
								
								guix/ci.scm
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -20,9 +20,12 @@
 | 
			
		|||
(define-module (guix ci)
 | 
			
		||||
  #:use-module (guix http-client)
 | 
			
		||||
  #:use-module (guix utils)
 | 
			
		||||
  #:use-module ((guix build download)
 | 
			
		||||
                #:select (resolve-uri-reference))
 | 
			
		||||
  #:use-module (json)
 | 
			
		||||
  #:use-module (srfi srfi-1)
 | 
			
		||||
  #:use-module (ice-9 match)
 | 
			
		||||
  #:use-module (web uri)
 | 
			
		||||
  #:use-module (guix i18n)
 | 
			
		||||
  #:use-module (guix diagnostics)
 | 
			
		||||
  #:autoload   (guix channels) (channel)
 | 
			
		||||
| 
						 | 
				
			
			@ -146,16 +149,44 @@
 | 
			
		|||
  ;; Max number of builds requested in queries.
 | 
			
		||||
  1000)
 | 
			
		||||
 | 
			
		||||
(define* (api-url base-url path #:rest query)
 | 
			
		||||
  "Build a proper API url, taking into account BASE-URL's trailing slashes.
 | 
			
		||||
QUERY takes any number of '(\"name\" value) 2-element lists, with VALUE being
 | 
			
		||||
either a string or a number (which will be converted to a string).  If VALUE
 | 
			
		||||
is #f, the respective element will not be added to the query parameters.
 | 
			
		||||
Other types of VALUE will raise an error since this low-level function is
 | 
			
		||||
api-agnostic."
 | 
			
		||||
 | 
			
		||||
  (define (build-query-string query)
 | 
			
		||||
    (let lp ((query (or (reverse query) '())) (acc '()))
 | 
			
		||||
      (match query
 | 
			
		||||
        (() (string-concatenate acc))
 | 
			
		||||
        (((_ #f) . rest) (lp rest acc))
 | 
			
		||||
        (((name val) . rest)
 | 
			
		||||
         (lp rest (cons*
 | 
			
		||||
                   name "="
 | 
			
		||||
                   (if (string? val) (uri-encode val) (number->string val))
 | 
			
		||||
                   (if (null? acc) "" "&")
 | 
			
		||||
                   acc))))))
 | 
			
		||||
 | 
			
		||||
  (let* ((query-string (build-query-string query))
 | 
			
		||||
         (base (string->uri base-url))
 | 
			
		||||
         (ref (build-relative-ref #:path path #:query query-string)))
 | 
			
		||||
    (resolve-uri-reference ref base)))
 | 
			
		||||
 | 
			
		||||
(define (json-fetch url)
 | 
			
		||||
  (let* ((port (http-fetch url))
 | 
			
		||||
         (json (json->scm port)))
 | 
			
		||||
    (close-port port)
 | 
			
		||||
    json))
 | 
			
		||||
 | 
			
		||||
(define* (json-api-fetch base-url path #:rest query)
 | 
			
		||||
  (json-fetch (apply api-url base-url path query)))
 | 
			
		||||
 | 
			
		||||
(define* (queued-builds url #:optional (limit %query-limit))
 | 
			
		||||
  "Return the list of queued derivations on URL."
 | 
			
		||||
  (let ((queue (json-fetch (string-append url "/api/queue?nr="
 | 
			
		||||
                                          (number->string limit)))))
 | 
			
		||||
  (let ((queue
 | 
			
		||||
         (json-api-fetch url "/api/queue" `("nr" ,limit))))
 | 
			
		||||
    (map json->build (vector->list queue))))
 | 
			
		||||
 | 
			
		||||
(define* (latest-builds url #:optional (limit %query-limit)
 | 
			
		||||
| 
						 | 
				
			
			@ -163,28 +194,21 @@
 | 
			
		|||
  "Return the latest builds performed by the CI server at URL.  If EVALUATION
 | 
			
		||||
is an integer, restrict to builds of EVALUATION.  If SYSTEM is true (a system
 | 
			
		||||
string such as \"x86_64-linux\"), restrict to builds for SYSTEM."
 | 
			
		||||
  (define* (option name value #:optional (->string identity))
 | 
			
		||||
    (if value
 | 
			
		||||
        (string-append "&" name "=" (->string value))
 | 
			
		||||
        ""))
 | 
			
		||||
 | 
			
		||||
  (let ((latest (json-fetch (string-append url "/api/latestbuilds?nr="
 | 
			
		||||
                                           (number->string limit)
 | 
			
		||||
                                           (option "evaluation" evaluation
 | 
			
		||||
                                                   number->string)
 | 
			
		||||
                                           (option "system" system)
 | 
			
		||||
                                           (option "job" job)
 | 
			
		||||
                                           (option "status" status
 | 
			
		||||
                                                   number->string)))))
 | 
			
		||||
  (let ((latest (json-api-fetch
 | 
			
		||||
                 url "/api/latestbuilds"
 | 
			
		||||
                 `("nr" ,limit)
 | 
			
		||||
                 `("evaluation" ,evaluation)
 | 
			
		||||
                 `("system" ,system)
 | 
			
		||||
                 `("job" ,job)
 | 
			
		||||
                 `("status" ,status))))
 | 
			
		||||
    ;; Note: Hydra does not provide a "derivation" field for entries in
 | 
			
		||||
    ;; 'latestbuilds', but Cuirass does.
 | 
			
		||||
    (map json->build (vector->list latest))))
 | 
			
		||||
 | 
			
		||||
(define (evaluation url evaluation)
 | 
			
		||||
  "Return the given EVALUATION performed by the CI server at URL."
 | 
			
		||||
  (let ((evaluation (json-fetch
 | 
			
		||||
                     (string-append url "/api/evaluation?id="
 | 
			
		||||
                                    (number->string evaluation)))))
 | 
			
		||||
  (let ((evaluation
 | 
			
		||||
         (json-api-fetch url "/api/evaluation" `("id" ,evaluation))))
 | 
			
		||||
    (json->evaluation evaluation)))
 | 
			
		||||
 | 
			
		||||
(define* (latest-evaluations url
 | 
			
		||||
| 
						 | 
				
			
			@ -192,16 +216,10 @@ string such as \"x86_64-linux\"), restrict to builds for SYSTEM."
 | 
			
		|||
                             #:key spec)
 | 
			
		||||
  "Return the latest evaluations performed by the CI server at URL.  If SPEC
 | 
			
		||||
is passed, only consider the evaluations for the given SPEC specification."
 | 
			
		||||
  (let ((spec (if spec
 | 
			
		||||
                  (format #f "&spec=~a" spec)
 | 
			
		||||
                  "")))
 | 
			
		||||
  (map json->evaluation
 | 
			
		||||
       (vector->list
 | 
			
		||||
          (json->scm
 | 
			
		||||
           (http-fetch
 | 
			
		||||
            (string-append url "/api/evaluations?nr="
 | 
			
		||||
                           (number->string limit)
 | 
			
		||||
                           spec)))))))
 | 
			
		||||
        (json-api-fetch
 | 
			
		||||
         url "/api/evaluations" `("nr" ,limit) `("spec" ,spec)))))
 | 
			
		||||
 | 
			
		||||
(define* (evaluations-for-commit url commit #:optional (limit %query-limit))
 | 
			
		||||
  "Return the evaluations among the latest LIMIT evaluations that have COMMIT
 | 
			
		||||
| 
						 | 
				
			
			@ -216,16 +234,14 @@ as one of their inputs."
 | 
			
		|||
  "Return the list of jobs of evaluation EVALUATION-ID."
 | 
			
		||||
  (map json->job
 | 
			
		||||
       (vector->list
 | 
			
		||||
        (json->scm (http-fetch
 | 
			
		||||
                    (string-append url "/api/jobs?evaluation="
 | 
			
		||||
                                   (number->string evaluation-id)))))))
 | 
			
		||||
        (json-api-fetch url "/api/jobs" `("evaluation" ,evaluation-id)))))
 | 
			
		||||
 | 
			
		||||
(define (build url id)
 | 
			
		||||
  "Look up build ID at URL and return it.  Raise &http-get-error if it is not
 | 
			
		||||
found (404)."
 | 
			
		||||
  (json->build
 | 
			
		||||
   (http-fetch (string-append url "/build/"       ;note: no "/api" here
 | 
			
		||||
                              (number->string id)))))
 | 
			
		||||
   (http-fetch (api-url url (string-append "/build/"    ;note: no "/api" here
 | 
			
		||||
                                           (number->string id))))))
 | 
			
		||||
 | 
			
		||||
(define (job-build url job)
 | 
			
		||||
  "Return the build associated with JOB."
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Reference in a new issue