diff options
| author | Sarah Morgensen <iskarian@mgsn.dev> | 2021-07-16 21:01:28 -0700 |
|---|---|---|
| committer | Maxim Cournoyer <maxim.cournoyer@gmail.com> | 2021-07-18 01:57:17 -0400 |
| commit | 793ba333c6039545684e8af7e384704e76bc0f2f (patch) | |
| tree | 2705d7a6da51a129ff441820199a485b1cce852b | |
| parent | 3217a04b0352c2dd13323257b369604eeabfccc3 (diff) | |
import: go: Upgrade go.mod parser.
Upgrade the go.mod parser to handle the full go.mod spec, and to gracefully
handle unexpected/malformed syntax. Restructure parser usage, making the
parse tree available for other uses.
guix/import/go.scm (parse-go.mod): Parse using (ice-9 peg) instead of
regex matching for more robustness. Return a list of directives.
(go.mod-directives): New procedure.
(go.mod-requirements): Likewise.
(go-module->guix-package): Use it.
(%go.mod-replace-directive-rx): Remove unused variable.
tests/go.scm (testing-parse-mod): Adjust accordingly.
(go.mod-requirements)
(fixture-go-mod-unparseable)
(fixture-go-mod-retract)
(fixture-go-mod-strings): New variables.
("parse-go.mod: simple")
("parse-go.mod: comments and unparseable lines")
("parse-go.mod: retract")
("parse-go.mod: raw strings and quoted strings")
("parse-go.mod: complete"): New tests.
Signed-off-by: Maxim Cournoyer <maxim.cournoyer@gmail.com>
| -rw-r--r-- | guix/import/go.scm | 249 | ||||
| -rw-r--r-- | tests/go.scm | 133 |
2 files changed, 262 insertions, 120 deletions
diff --git a/guix/import/go.scm b/guix/import/go.scm index 24d12acd973..a6e2af215f0 100644 --- a/guix/import/go.scm +++ b/guix/import/go.scm | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | ;;; Copyright © 2021 Maxim Cournoyer <maxim.cournoyer@gmail.com> | 5 | ;;; Copyright © 2021 Maxim Cournoyer <maxim.cournoyer@gmail.com> |
| 6 | ;;; Copyright © 2021 Ludovic Courtès <ludo@gnu.org> | 6 | ;;; Copyright © 2021 Ludovic Courtès <ludo@gnu.org> |
| 7 | ;;; Copyright © 2021 Xinglu Chen <public@yoctocell.xyz> | 7 | ;;; Copyright © 2021 Xinglu Chen <public@yoctocell.xyz> |
| 8 | ;; Copyright © 2021 Sarah Morgensen <iskarian@mgsn.dev> | 8 | ;;; Copyright © 2021 Sarah Morgensen <iskarian@mgsn.dev> |
| 9 | ;;; | 9 | ;;; |
| 10 | ;;; This file is part of GNU Guix. | 10 | ;;; This file is part of GNU Guix. |
| 11 | ;;; | 11 | ;;; |
| @@ -41,6 +41,7 @@ | |||
| 41 | #:autoload (guix base32) (bytevector->nix-base32-string) | 41 | #:autoload (guix base32) (bytevector->nix-base32-string) |
| 42 | #:autoload (guix build utils) (mkdir-p) | 42 | #:autoload (guix build utils) (mkdir-p) |
| 43 | #:use-module (ice-9 match) | 43 | #:use-module (ice-9 match) |
| 44 | #:use-module (ice-9 peg) | ||
| 44 | #:use-module (ice-9 rdelim) | 45 | #:use-module (ice-9 rdelim) |
| 45 | #:use-module (ice-9 receive) | 46 | #:use-module (ice-9 receive) |
| 46 | #:use-module (ice-9 regex) | 47 | #:use-module (ice-9 regex) |
| @@ -244,129 +245,139 @@ and VERSION and return an input port." | |||
| 244 | (go-path-escape version)))) | 245 | (go-path-escape version)))) |
| 245 | (http-fetch* url))) | 246 | (http-fetch* url))) |
| 246 | 247 | ||
| 247 | (define %go.mod-require-directive-rx | ||
| 248 | ;; A line in a require directive is composed of a module path and | ||
| 249 | ;; a version separated by whitespace and an optionnal '//' comment at | ||
| 250 | ;; the end. | ||
| 251 | (make-regexp | ||
| 252 | (string-append | ||
| 253 | "^[[:blank:]]*([^[:blank:]]+)[[:blank:]]+" ;the module path | ||
| 254 | "([^[:blank:]]+)" ;the version | ||
| 255 | "([[:blank:]]+//.*)?"))) ;an optional comment | ||
| 256 | |||
| 257 | (define %go.mod-replace-directive-rx | ||
| 258 | ;; ReplaceSpec = ModulePath [ Version ] "=>" FilePath newline | ||
| 259 | ;; | ModulePath [ Version ] "=>" ModulePath Version newline . | ||
| 260 | (make-regexp | ||
| 261 | (string-append | ||
| 262 | "([^[:blank:]]+)" ;the module path | ||
| 263 | "([[:blank:]]+([^[:blank:]]+))?" ;optional version | ||
| 264 | "[[:blank:]]+=>[[:blank:]]+" | ||
| 265 | "([^[:blank:]]+)" ;the file or module path | ||
| 266 | "([[:blank:]]+([^[:blank:]]+))?"))) ;the version (if a module path) | ||
| 267 | 248 | ||
| 268 | (define (parse-go.mod content) | 249 | (define (parse-go.mod content) |
| 269 | "Parse the go.mod file CONTENT, returning a list of requirements." | 250 | "Parse the go.mod file CONTENT, returning a list of directives, comments, |
| 270 | ;; We parse only a subset of https://golang.org/ref/mod#go-mod-file-grammar | 251 | and unknown lines. Each sublist begins with a symbol (go, module, require, |
| 271 | ;; which we think necessary for our use case. | 252 | replace, exclude, retract, comment, or unknown) and is followed by one or more |
| 272 | (define (toplevel requirements replaced) | 253 | sublists. Each sublist begins with a symbol (module-path, version, file-path, |
| 273 | "This is the main parser. The results are accumulated in THE REQUIREMENTS | 254 | comment, or unknown) and is followed by the indicated data." |
| 274 | and REPLACED lists." | 255 | ;; https://golang.org/ref/mod#go-mod-file-grammar |
| 275 | (let ((line (read-line))) | 256 | (define-peg-pattern NL none "\n") |
| 276 | (cond | 257 | (define-peg-pattern WS none (or " " "\t" "\r")) |
| 277 | ((eof-object? line) | 258 | (define-peg-pattern => none (and (* WS) "=>")) |
| 278 | ;; parsing ended, give back the result | 259 | (define-peg-pattern punctuation none (or "," "=>" "[" "]" "(" ")")) |
| 279 | (values requirements replaced)) | 260 | (define-peg-pattern comment all |
| 280 | ((string=? line "require (") | 261 | (and (ignore "//") (* WS) (* (and (not-followed-by NL) peg-any)))) |
| 281 | ;; a require block begins, delegate parsing to IN-REQUIRE | 262 | (define-peg-pattern EOL body (and (* WS) (? comment) NL)) |
| 282 | (in-require requirements replaced)) | 263 | (define-peg-pattern block-start none (and (* WS) "(" EOL)) |
| 283 | ((string=? line "replace (") | 264 | (define-peg-pattern block-end none (and (* WS) ")" EOL)) |
| 284 | ;; a replace block begins, delegate parsing to IN-REPLACE | 265 | (define-peg-pattern any-line body |
| 285 | (in-replace requirements replaced)) | 266 | (and (* WS) (* (and (not-followed-by NL) peg-any)) EOL)) |
| 286 | ((string-prefix? "require " line) | 267 | |
| 287 | ;; a require directive by itself | 268 | ;; Strings and identifiers |
| 288 | (let* ((stripped-line (string-drop line 8))) | 269 | (define-peg-pattern identifier body |
| 289 | (call-with-values | 270 | (+ (and (not-followed-by (or NL WS punctuation)) peg-any))) |
| 290 | (lambda () | 271 | (define-peg-pattern string-raw body |
| 291 | (require-directive requirements replaced stripped-line)) | 272 | (and (ignore "`") (+ (and (not-followed-by "`") peg-any)) (ignore "`"))) |
| 292 | toplevel))) | 273 | (define-peg-pattern string-quoted body |
| 293 | ((string-prefix? "replace " line) | 274 | (and (ignore "\"") |
| 294 | ;; a replace directive by itself | 275 | (+ (or (and (ignore "\\") peg-any) |
| 295 | (let* ((stripped-line (string-drop line 8))) | 276 | (and (not-followed-by "\"") peg-any))) |
| 296 | (call-with-values | 277 | (ignore "\""))) |
| 297 | (lambda () | 278 | (define-peg-pattern string-or-ident body |
| 298 | (replace-directive requirements replaced stripped-line)) | 279 | (and (* WS) (or string-raw string-quoted identifier))) |
| 299 | toplevel))) | 280 | |
| 300 | (#t | 281 | (define-peg-pattern version all string-or-ident) |
| 301 | ;; unrecognised line, ignore silently | 282 | (define-peg-pattern module-path all string-or-ident) |
| 302 | (toplevel requirements replaced))))) | 283 | (define-peg-pattern file-path all string-or-ident) |
| 303 | 284 | ||
| 304 | (define (in-require requirements replaced) | 285 | ;; Non-directive lines |
| 305 | (let ((line (read-line))) | 286 | (define-peg-pattern unknown all any-line) |
| 306 | (cond | 287 | (define-peg-pattern block-line body |
| 307 | ((eof-object? line) | 288 | (or EOL (and (not-followed-by block-end) unknown))) |
| 308 | ;; this should never happen here but we ignore silently | 289 | |
| 309 | (values requirements replaced)) | 290 | ;; GoDirective = "go" GoVersion newline . |
| 310 | ((string=? line ")") | 291 | (define-peg-pattern go all (and (ignore "go") version EOL)) |
| 311 | ;; end of block, coming back to toplevel | 292 | |
| 312 | (toplevel requirements replaced)) | 293 | ;; ModuleDirective = "module" ( ModulePath | "(" newline ModulePath newline ")" ) newline . |
| 313 | (#t | 294 | (define-peg-pattern module all |
| 314 | (call-with-values (lambda () | 295 | (and (ignore "module") (or (and block-start module-path EOL block-end) |
| 315 | (require-directive requirements replaced line)) | 296 | (and module-path EOL)))) |
| 316 | in-require))))) | 297 | |
| 317 | 298 | ;; The following directives may all be used solo or in a block | |
| 318 | (define (in-replace requirements replaced) | 299 | ;; RequireSpec = ModulePath Version newline . |
| 319 | (let ((line (read-line))) | 300 | (define-peg-pattern require all (and module-path version EOL)) |
| 320 | (cond | 301 | (define-peg-pattern require-top body |
| 321 | ((eof-object? line) | 302 | (and (ignore "require") |
| 322 | ;; this should never happen here but we ignore silently | 303 | (or (and block-start (* (or require block-line)) block-end) require))) |
| 323 | (values requirements replaced)) | 304 | |
| 324 | ((string=? line ")") | 305 | ;; ExcludeSpec = ModulePath Version newline . |
| 325 | ;; end of block, coming back to toplevel | 306 | (define-peg-pattern exclude all (and module-path version EOL)) |
| 326 | (toplevel requirements replaced)) | 307 | (define-peg-pattern exclude-top body |
| 327 | (#t | 308 | (and (ignore "exclude") |
| 328 | (call-with-values (lambda () | 309 | (or (and block-start (* (or exclude block-line)) block-end) exclude))) |
| 329 | (replace-directive requirements replaced line)) | 310 | |
| 330 | in-replace))))) | 311 | ;; ReplaceSpec = ModulePath [ Version ] "=>" FilePath newline |
| 331 | 312 | ;; | ModulePath [ Version ] "=>" ModulePath Version newline . | |
| 332 | (define (replace-directive requirements replaced line) | 313 | (define-peg-pattern original all (or (and module-path version) module-path)) |
| 333 | "Extract replaced modules and new requirements from the replace directive | 314 | (define-peg-pattern with all (or (and module-path version) file-path)) |
| 334 | in LINE and add them to the REQUIREMENTS and REPLACED lists." | 315 | (define-peg-pattern replace all (and original => with EOL)) |
| 335 | (let* ((rx-match (regexp-exec %go.mod-replace-directive-rx line)) | 316 | (define-peg-pattern replace-top body |
| 336 | (module-path (match:substring rx-match 1)) | 317 | (and (ignore "replace") |
| 337 | (version (match:substring rx-match 3)) | 318 | (or (and block-start (* (or replace block-line)) block-end) replace))) |
| 338 | (new-module-path (match:substring rx-match 4)) | 319 | |
| 339 | (new-version (match:substring rx-match 6)) | 320 | ;; RetractSpec = ( Version | "[" Version "," Version "]" ) newline . |
| 340 | (new-replaced (cons (list module-path version) replaced)) | 321 | (define-peg-pattern range all |
| 341 | (new-requirements | 322 | (and (* WS) (ignore "[") version |
| 342 | (if (string-match "^\\.?\\./" new-module-path) | 323 | (* WS) (ignore ",") version (* WS) (ignore "]"))) |
| 343 | requirements | 324 | (define-peg-pattern retract all (and (or range version) EOL)) |
| 344 | (cons (list new-module-path new-version) requirements)))) | 325 | (define-peg-pattern retract-top body |
| 345 | (values new-requirements new-replaced))) | 326 | (and (ignore "retract") |
| 346 | 327 | (or (and block-start (* (or retract block-line)) block-end) retract))) | |
| 347 | (define (require-directive requirements replaced line) | 328 | |
| 348 | "Extract requirement from LINE and augment the REQUIREMENTS and REPLACED | 329 | (define-peg-pattern go-mod body |
| 349 | lists." | 330 | (* (and (* WS) (or go module require-top exclude-top replace-top |
| 350 | (let* ((rx-match (regexp-exec %go.mod-require-directive-rx line)) | 331 | retract-top EOL unknown)))) |
| 351 | (module-path (match:substring rx-match 1)) | 332 | |
| 352 | ;; Double-quoted strings were seen in the wild without escape | 333 | (let ((tree (peg:tree (match-pattern go-mod content))) |
| 353 | ;; sequences; trim the quotes to be on the safe side. | 334 | (keywords '(go module require replace exclude retract comment unknown))) |
| 354 | (module-path (string-trim-both module-path #\")) | 335 | (keyword-flatten keywords tree))) |
| 355 | (version (match:substring rx-match 2))) | ||
| 356 | (values (cons (list module-path version) requirements) replaced))) | ||
| 357 | |||
| 358 | (with-input-from-string content | ||
| 359 | (lambda () | ||
| 360 | (receive (requirements replaced) | ||
| 361 | (toplevel '() '()) | ||
| 362 | ;; At last remove the replaced modules from the requirements list. | ||
| 363 | (remove (lambda (r) | ||
| 364 | (assoc (car r) replaced)) | ||
| 365 | requirements))))) | ||
| 366 | 336 | ||
| 367 | ;; Prevent inlining of this procedure, which is accessed by unit tests. | 337 | ;; Prevent inlining of this procedure, which is accessed by unit tests. |
| 368 | (set! parse-go.mod parse-go.mod) | 338 | (set! parse-go.mod parse-go.mod) |
| 369 | 339 | ||
| 340 | (define (go.mod-directives go.mod directive) | ||
| 341 | "Return the list of top-level directive bodies in GO.MOD matching the symbol | ||
| 342 | DIRECTIVE." | ||
| 343 | (filter-map (match-lambda | ||
| 344 | (((? (cut eq? <> directive) head) . rest) rest) | ||
| 345 | (_ #f)) | ||
| 346 | go.mod)) | ||
| 347 | |||
| 348 | (define (go.mod-requirements go.mod) | ||
| 349 | "Compute and return the list of requirements specified by GO.MOD." | ||
| 350 | (define (replace directive requirements) | ||
| 351 | (define (maybe-replace module-path new-requirement) | ||
| 352 | ;; Do not allow version updates for indirect dependencies (see: | ||
| 353 | ;; https://golang.org/ref/mod#go-mod-file-replace). | ||
| 354 | (if (and (equal? module-path (first new-requirement)) | ||
| 355 | (not (assoc-ref requirements module-path))) | ||
| 356 | requirements | ||
| 357 | (cons new-requirement (alist-delete module-path requirements)))) | ||
| 358 | |||
| 359 | (match directive | ||
| 360 | ((('original ('module-path module-path) . _) with . _) | ||
| 361 | (match with | ||
| 362 | (('with ('file-path _) . _) | ||
| 363 | (alist-delete module-path requirements)) | ||
| 364 | (('with ('module-path new-module-path) ('version new-version) . _) | ||
| 365 | (maybe-replace module-path | ||
| 366 | (list new-module-path new-version))))))) | ||
| 367 | |||
| 368 | (define (require directive requirements) | ||
| 369 | (match directive | ||
| 370 | ((('module-path module-path) ('version version) . _) | ||
| 371 | (cons (list module-path version) requirements)))) | ||
| 372 | |||
| 373 | (let* ((requires (go.mod-directives go.mod 'require)) | ||
| 374 | (replaces (go.mod-directives go.mod 'replace)) | ||
| 375 | (requirements (fold require '() requires))) | ||
| 376 | (fold replace requirements replaces))) | ||
| 377 | |||
| 378 | ;; Prevent inlining of this procedure, which is accessed by unit tests. | ||
| 379 | (set! go.mod-requirements go.mod-requirements) | ||
| 380 | |||
| 370 | (define-record-type <vcs> | 381 | (define-record-type <vcs> |
| 371 | (%make-vcs url-prefix root-regex type) | 382 | (%make-vcs url-prefix root-regex type) |
| 372 | vcs? | 383 | vcs? |
| @@ -592,7 +603,7 @@ When VERSION is unspecified, the latest version available is used." | |||
| 592 | hint: use one of the following available versions ~a\n" | 603 | hint: use one of the following available versions ~a\n" |
| 593 | version* available-versions)))) | 604 | version* available-versions)))) |
| 594 | (content (fetch-go.mod goproxy module-path version*)) | 605 | (content (fetch-go.mod goproxy module-path version*)) |
| 595 | (dependencies+versions (parse-go.mod content)) | 606 | (dependencies+versions (go.mod-requirements (parse-go.mod content))) |
| 596 | (dependencies (if pin-versions? | 607 | (dependencies (if pin-versions? |
| 597 | dependencies+versions | 608 | dependencies+versions |
| 598 | (map car dependencies+versions))) | 609 | (map car dependencies+versions))) |
diff --git a/tests/go.scm b/tests/go.scm index 2dfdc97eb5c..6749f4585ff 100644 --- a/tests/go.scm +++ b/tests/go.scm | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | ;;; GNU Guix --- Functional package management for GNU | 1 | ;;; GNU Guix --- Functional package management for GNU |
| 2 | ;;; Copyright © 2021 François Joulaud <francois.joulaud@radiofrance.com> | 2 | ;;; Copyright © 2021 François Joulaud <francois.joulaud@radiofrance.com> |
| 3 | ;;; Copyright © 2021 Sarah Morgensen <iskarian@mgsn.dev> | ||
| 3 | ;;; | 4 | ;;; |
| 4 | ;;; This file is part of GNU Guix. | 5 | ;;; This file is part of GNU Guix. |
| 5 | ;;; | 6 | ;;; |
| @@ -31,6 +32,9 @@ | |||
| 31 | #:use-module (srfi srfi-64) | 32 | #:use-module (srfi srfi-64) |
| 32 | #:use-module (web response)) | 33 | #:use-module (web response)) |
| 33 | 34 | ||
| 35 | (define go.mod-requirements | ||
| 36 | (@@ (guix import go) go.mod-requirements)) | ||
| 37 | |||
| 34 | (define parse-go.mod | 38 | (define parse-go.mod |
| 35 | (@@ (guix import go) parse-go.mod)) | 39 | (@@ (guix import go) parse-go.mod)) |
| 36 | 40 | ||
| @@ -95,6 +99,41 @@ replace ( | |||
| 95 | 99 | ||
| 96 | ") | 100 | ") |
| 97 | 101 | ||
| 102 | (define fixture-go-mod-unparseable | ||
| 103 | "module my/thing | ||
| 104 | go 1.12 // avoid feature X | ||
| 105 | require other/thing v1.0.2 | ||
| 106 | // Security issue: CVE-XXXXX | ||
| 107 | exclude old/thing v1.2.3 | ||
| 108 | new-directive another/thing yet-another/thing | ||
| 109 | replace ( | ||
| 110 | bad/thing v1.4.5 => good/thing v1.4.5 | ||
| 111 | // Unparseable | ||
| 112 | bad/thing [v1.4.5, v1.9.7] => good/thing v2.0.0 | ||
| 113 | ) | ||
| 114 | ") | ||
| 115 | |||
| 116 | (define fixture-go-mod-retract | ||
| 117 | "retract v0.9.1 | ||
| 118 | |||
| 119 | retract ( | ||
| 120 | v1.9.2 | ||
| 121 | [v1.0.0, v1.7.9] | ||
| 122 | ) | ||
| 123 | ") | ||
| 124 | |||
| 125 | (define fixture-go-mod-strings | ||
| 126 | "require `example.com/\"some-repo\"` v1.9.3 | ||
| 127 | require ( | ||
| 128 | `example.com/\"another.repo\"` v1.0.0 | ||
| 129 | \"example.com/special!repo\" v9.3.1 | ||
| 130 | ) | ||
| 131 | replace \"example.com/\\\"some-repo\\\"\" => `launchpad.net/some-repo` v1.9.3 | ||
| 132 | replace ( | ||
| 133 | \"example.com/\\\"another.repo\\\"\" => launchpad.net/another-repo v1.0.0 | ||
| 134 | ) | ||
| 135 | ") | ||
| 136 | |||
| 98 | (define fixtures-go-check-test | 137 | (define fixtures-go-check-test |
| 99 | (let ((version | 138 | (let ((version |
| 100 | "{\"Version\":\"v0.0.0-20201130134442-10cb98267c6c\",\"Time\":\"2020-11-30T13:44:42Z\"}") | 139 | "{\"Version\":\"v0.0.0-20201130134442-10cb98267c6c\",\"Time\":\"2020-11-30T13:44:42Z\"}") |
| @@ -178,7 +217,7 @@ require github.com/kr/pretty v0.2.1 | |||
| 178 | (string<? (car p1) (car p2))) | 217 | (string<? (car p1) (car p2))) |
| 179 | (test-equal name | 218 | (test-equal name |
| 180 | (sort expected inf?) | 219 | (sort expected inf?) |
| 181 | (sort ((@@ (guix import go) parse-go.mod) input) inf?))) | 220 | (sort (go.mod-requirements (parse-go.mod input)) inf?))) |
| 182 | 221 | ||
| 183 | (testing-parse-mod "parse-go.mod-simple" | 222 | (testing-parse-mod "parse-go.mod-simple" |
| 184 | '(("good/thing" "v1.4.5") | 223 | '(("good/thing" "v1.4.5") |
| @@ -214,6 +253,98 @@ require github.com/kr/pretty v0.2.1 | |||
| 214 | ("github.com/go-check/check" "v0.0.0-20140225173054-eb6ee6f84d0a")) | 253 | ("github.com/go-check/check" "v0.0.0-20140225173054-eb6ee6f84d0a")) |
| 215 | fixture-go-mod-complete) | 254 | fixture-go-mod-complete) |
| 216 | 255 | ||
| 256 | (test-equal "parse-go.mod: simple" | ||
| 257 | `((module (module-path "my/thing")) | ||
| 258 | (go (version "1.12")) | ||
| 259 | (require (module-path "other/thing") (version "v1.0.2")) | ||
| 260 | (require (module-path "new/thing/v2") (version "v2.3.4")) | ||
| 261 | (exclude (module-path "old/thing") (version "v1.2.3")) | ||
| 262 | (replace (original (module-path "bad/thing") (version "v1.4.5")) | ||
| 263 | (with (module-path "good/thing") (version "v1.4.5")))) | ||
| 264 | (parse-go.mod fixture-go-mod-simple)) | ||
| 265 | |||
| 266 | (test-equal "parse-go.mod: comments and unparseable lines" | ||
| 267 | `((module (module-path "my/thing")) | ||
| 268 | (go (version "1.12") (comment "avoid feature X")) | ||
| 269 | (require (module-path "other/thing") (version "v1.0.2")) | ||
| 270 | (comment "Security issue: CVE-XXXXX") | ||
| 271 | (exclude (module-path "old/thing") (version "v1.2.3")) | ||
| 272 | (unknown "new-directive another/thing yet-another/thing") | ||
| 273 | (replace (original (module-path "bad/thing") (version "v1.4.5")) | ||
| 274 | (with (module-path "good/thing") (version "v1.4.5"))) | ||
| 275 | (comment "Unparseable") | ||
| 276 | (unknown "bad/thing [v1.4.5, v1.9.7] => good/thing v2.0.0")) | ||
| 277 | (parse-go.mod fixture-go-mod-unparseable)) | ||
| 278 | |||
| 279 | (test-equal "parse-go.mod: retract" | ||
| 280 | `((retract (version "v0.9.1")) | ||
| 281 | (retract (version "v1.9.2")) | ||
| 282 | (retract (range (version "v1.0.0") (version "v1.7.9")))) | ||
| 283 | (parse-go.mod fixture-go-mod-retract)) | ||
| 284 | |||
| 285 | (test-equal "parse-go.mod: raw strings and quoted strings" | ||
| 286 | `((require (module-path "example.com/\"some-repo\"") (version "v1.9.3")) | ||
| 287 | (require (module-path "example.com/\"another.repo\"") (version "v1.0.0")) | ||
| 288 | (require (module-path "example.com/special!repo") (version "v9.3.1")) | ||
| 289 | (replace (original (module-path "example.com/\"some-repo\"")) | ||
| 290 | (with (module-path "launchpad.net/some-repo") (version "v1.9.3"))) | ||
| 291 | (replace (original (module-path "example.com/\"another.repo\"")) | ||
| 292 | (with (module-path "launchpad.net/another-repo") (version "v1.0.0")))) | ||
| 293 | (parse-go.mod fixture-go-mod-strings)) | ||
| 294 | |||
| 295 | (test-equal "parse-go.mod: complete" | ||
| 296 | `((module (module-path "M")) | ||
| 297 | (go (version "1.13")) | ||
| 298 | (replace (original (module-path "github.com/myname/myproject/myapi")) | ||
| 299 | (with (file-path "./api"))) | ||
| 300 | (replace (original (module-path "github.com/mymname/myproject/thissdk")) | ||
| 301 | (with (file-path "../sdk"))) | ||
| 302 | (replace (original (module-path "launchpad.net/gocheck")) | ||
| 303 | (with (module-path "github.com/go-check/check") | ||
| 304 | (version "v0.0.0-20140225173054-eb6ee6f84d0a"))) | ||
| 305 | (require (module-path "github.com/user/project") | ||
| 306 | (version "v1.1.11")) | ||
| 307 | (require (module-path "github.com/user/project/sub/directory") | ||
| 308 | (version "v1.1.12")) | ||
| 309 | (require (module-path "bitbucket.org/user/project") | ||
| 310 | (version "v1.11.20")) | ||
| 311 | (require (module-path "bitbucket.org/user/project/sub/directory") | ||
| 312 | (version "v1.11.21")) | ||
| 313 | (require (module-path "launchpad.net/project") | ||
| 314 | (version "v1.1.13")) | ||
| 315 | (require (module-path "launchpad.net/project/series") | ||
| 316 | (version "v1.1.14")) | ||
| 317 | (require (module-path "launchpad.net/project/series/sub/directory") | ||
| 318 | (version "v1.1.15")) | ||
| 319 | (require (module-path "launchpad.net/~user/project/branch") | ||
| 320 | (version "v1.1.16")) | ||
| 321 | (require (module-path "launchpad.net/~user/project/branch/sub/directory") | ||
| 322 | (version "v1.1.17")) | ||
| 323 | (require (module-path "hub.jazz.net/git/user/project") | ||
| 324 | (version "v1.1.18")) | ||
| 325 | (require (module-path "hub.jazz.net/git/user/project/sub/directory") | ||
| 326 | (version "v1.1.19")) | ||
| 327 | (require (module-path "k8s.io/kubernetes/subproject") | ||
| 328 | (version "v1.1.101")) | ||
| 329 | (require (module-path "one.example.com/abitrary/repo") | ||
| 330 | (version "v1.1.111")) | ||
| 331 | (require (module-path "two.example.com/abitrary/repo") | ||
| 332 | (version "v0.0.2")) | ||
| 333 | (require (module-path "quoted.example.com/abitrary/repo") | ||
| 334 | (version "v0.0.2")) | ||
| 335 | (replace (original (module-path "two.example.com/abitrary/repo")) | ||
| 336 | (with (module-path "github.com/corp/arbitrary-repo") | ||
| 337 | (version "v0.0.2"))) | ||
| 338 | (replace (original (module-path "golang.org/x/sys")) | ||
| 339 | (with (module-path "golang.org/x/sys") | ||
| 340 | (version "v0.0.0-20190813064441-fde4db37ae7a")) | ||
| 341 | (comment "pinned to release-branch.go1.13")) | ||
| 342 | (replace (original (module-path "golang.org/x/tools")) | ||
| 343 | (with (module-path "golang.org/x/tools") | ||
| 344 | (version "v0.0.0-20190821162956-65e3620a7ae7")) | ||
| 345 | (comment "pinned to release-branch.go1.13"))) | ||
| 346 | (parse-go.mod fixture-go-mod-complete)) | ||
| 347 | |||
| 217 | ;;; End-to-end tests for (guix import go) | 348 | ;;; End-to-end tests for (guix import go) |
| 218 | (define (mock-http-fetch testcase) | 349 | (define (mock-http-fetch testcase) |
| 219 | (lambda (url . rest) | 350 | (lambda (url . rest) |
