guix_mirror_bot pushed a commit to branch master
in repository guix.
commit 7ee79c9e82245b83ef76c84b67466ad93bea84aa
Author: Maxim Cournoyer <[email protected]>
AuthorDate: Tue Jun 9 09:34:25 2026 +0900
import: npm-binary: Add a '--binary' option.
This changes the default behavior of the 'npm-binary' importer to prefer
using
pristine sources from Git when available, along with attempting to build the
package. It adds a '--binary' option to the importer to preserve the
previous
behavior.
* guix/import/git.scm (get-tags): Export.
* guix/import/npm-binary.scm (<repository>): New JSON mapping.
(<package-revision>): Add a repository field.
(npm-package->package-sexp) [#:binary?]: New keyword. Update doc.
Conditionally splice #:phases argument.
<normalize-git-url, sexpify-git-tag/maybe>: New nested procedures.
(npm-package->package-sexp) [binary?]: Expand origin conditionally to
BINARY?.
(npm-binary->guix-package) [#:binary?]: New keyword.
(npm-binary-recursive-import): Likewise.
* guix/import/utils.scm (git->origin) [#:eager?]: New keyword. Update doc.
* guix/scripts/import/npm-binary.scm (show-help): Document new --binary
option.
(guix-import-npm-binary): Honor the new option.
Change-Id: I8e8c1c333faa8f5c96184ada064b63bdf2b4e165
---
doc/guix.texi | 15 +++--
guix/import/git.scm | 3 +-
guix/import/npm-binary.scm | 129 ++++++++++++++++++++++++++++++-------
guix/import/utils.scm | 18 ++++--
guix/scripts/import/npm-binary.scm | 14 +++-
5 files changed, 139 insertions(+), 40 deletions(-)
diff --git a/doc/guix.texi b/doc/guix.texi
index d974a5cdf2..7950843c3c 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -15732,13 +15732,6 @@ The npm-binary importer also allows you to specify a
version string:
guix import npm-binary buffer-crc32@@1.0.0
@end example
-@quotation Note
-Generated package expressions skip the build step of the
-@code{node-build-system}. As such, generated package expressions often
-refer to transpiled or generated files, instead of being built from
-source.
-@end quotation
-
@quotation Note
Currently, the generated package definition contains a phase to delete
the development dependencies (@code{devDependencies}) from the
@@ -15756,6 +15749,14 @@ Additional options include:
Traverse the dependency graph of the given upstream package recursively
and generate package expressions for all those packages that are not yet
in Guix.
+@item --binary
+@itemx -b
+Import a binary NPM package. This means preferring the use of a
+@url{https://registry.npmjs.org/} source origin, which typically bundles
+pre-built artifacts, along removing the @code{build} phase to avoid
+rebuilding such artifacts from source. This should only be used as a
+last resort, as it is preferable building everything from source, to
+preserve referential transparency and the chain of trust.
@end table
@item opam
diff --git a/guix/import/git.scm b/guix/import/git.scm
index 81ea9b7398..e6dab7f25f 100644
--- a/guix/import/git.scm
+++ b/guix/import/git.scm
@@ -34,7 +34,8 @@
#:use-module (srfi srfi-34)
#:use-module (srfi srfi-35)
#:use-module (srfi srfi-71)
- #:export (%generic-git-updater))
+ #:export (%generic-git-updater
+ get-tags))
;;; Commentary:
;;;
diff --git a/guix/import/npm-binary.scm b/guix/import/npm-binary.scm
index b924ebdf0a..e299c25960 100644
--- a/guix/import/npm-binary.scm
+++ b/guix/import/npm-binary.scm
@@ -26,6 +26,7 @@
#:use-module (gnu packages)
#:use-module (guix base32)
#:use-module (guix http-client)
+ #:use-module ((guix import git) #:select (get-tags))
#:use-module (guix import json)
#:use-module (guix import utils)
#:use-module (guix memoization)
@@ -83,6 +84,14 @@
json->dist
(tarball dist-tarball))
+(define (string-or-#f s)
+ (and (string? s) s))
+
+(define-json-mapping <repository> make-repository repository?
+ json->repository
+ (type repository-type "type" string-or-#f)
+ (url repository-url "url" string-or-#f))
+
(define (empty-or-string s)
(if (string? s) s ""))
@@ -120,7 +129,18 @@
license)))))
(description package-revision-description ;string
"description" empty-or-string)
- (dist package-revision-dist "dist" json->dist)) ;dist
+ (dist package-revision-dist "dist" json->dist)
+ (repository package-revision-repository "repository" ;<repository>
+ (lambda (value)
+ ;; Guard against historical values such as strings or anything
+ ;; else.
+ (match value
+ ((? alist?)
+ (json->repository value))
+ ((? unspecified?) #f)
+ ((? string? url)
+ (make-repository "git" url))
+ (_ #f)))))
(define (versions->package-revisions versions)
(match versions
@@ -203,8 +223,10 @@
(name (npm-name->name npm-name)))
(name+version->symbol name version)))
-(define (npm-package->package-sexp npm-package)
- "Return the `package' s-expression for an NPM-PACKAGE."
+(define* (npm-package->package-sexp npm-package #:key binary?)
+ "Return the `package' s-expression for an NPM-PACKAGE. Prefer the binary
+distribution URL as the source origin when BINARY? is #t, otherwise use the
+associated Git repository, if available."
(define resolve-spec
(match-lambda
(($ <versioned-package> name version)
@@ -221,6 +243,20 @@
(string-drop-right url 7)
url))
+ (define (normalize-git-url url)
+ (let ((url (cond
+ ((string-prefix? "git+https://" url)
+ (string-replace-substring url "git+https://" "https://"))
+ ((string-prefix? "git+ssh://" url)
+ (string-replace-substring url "git+ssh://" "https://"))
+ ((string-prefix? "git://" url)
+ (string-replace-substring url "git://" "https://"))
+ (else url))))
+ (if (and (github-hosted? url)
+ (string-suffix? ".git" url))
+ (string-drop-right url 4)
+ url)))
+
(define (sexpify-url/maybe dist-url name version)
;; Return a S-exp for the package URL, which is computed using the package
;; version, if it matches the distribution tarball DIST-URL.
@@ -245,14 +281,51 @@
,(string-append "/-/" name "-") version ".tgz")
dist-url)))
+ (define (sexpify-git-tag/maybe tag version)
+ ;; Try to find a string-append pattern to build TAG from VERSION. For
example,
+ ;;
+ ;; (sexpify-git-tag/maybe "3.0.0v3.0.0v3.0.0" "3.0.0")
+ ;; => '(string-append version "v" version "v" version)
+ (match (string-split (string-replace-substring
+ tag version ":")
+ #\:)
+ (("" "") ;tag == version
+ 'version)
+ ((prefix "")
+ `(string-append ,prefix version))
+ (("" suffix)
+ `(string-append version ,suffix))
+ ((component components* ..1)
+ ;; Intersperse 'version between each component.
+ (let ((interspersed (append
+ (append-map
+ (lambda (x)
+ (list x 'version))
+ (drop-right `(,component ,@components*) 1))
+ (list (last components*)))))
+ `(string-append ,@(remove (lambda (x)
+ (and (string? x)
+ (string-null? x)))
+ interspersed))))
+ (_ tag)))
+
(match npm-package
(($ <package-revision>
name version home-page dependencies dev-dependencies
- peer-dependencies license description dist)
+ peer-dependencies license description dist repository)
(let* ((version-string (semver->string
(package-revision-version npm-package)))
(dist-url (dist-tarball dist))
(url (sexpify-url/maybe dist-url name version-string))
+ (git-url? (and (not binary?) repository
+ (and=> (repository-type repository)
+ (cut string=? "git" <>))))
+ (git-url (and git-url? (and=> (repository-url repository)
+ normalize-git-url)))
+ (git-tag (and git-url
+ (and=> (false-if-exception (get-tags git-url))
+ (cut assoc-ref <> version-string))))
+ (use-git-url? (and git-url git-tag))
(name (npm-name->name name))
(home-page (if (string? home-page)
(sanitize-home-page-url home-page)
@@ -264,28 +337,39 @@
(dev-names (append (map versioned-package-name dev-dependencies)
peer-names))
(extra-phases
- (match dev-names
- (() '())
- ((dev-names ...)
- `((add-after 'patch-dependencies 'delete-dev-dependencies
- (lambda _
- (modify-json (delete-dev-dependencies)))))))))
+ (append
+ (if binary?
+ '((delete 'build))
+ '())
+ (match dev-names
+ (() '())
+ ((dev-names ...)
+ `((add-after 'patch-dependencies 'delete-dev-dependencies
+ (lambda _
+ (modify-json (delete-dev-dependencies))))))))))
(values
`(package
(name ,name)
(version ,version-string)
- (source (origin
- (method url-fetch)
- (uri ,url)
- (sha256 (base32 ,(hash-url dist-url)))))
+ (source ,(if use-git-url?
+ (git->origin git-url sexpify-git-tag/maybe
+ #:ref (cons 'tag git-tag)
+ #:eager? #t
+ git-tag version-string)
+ `(origin
+ (method url-fetch)
+ (uri ,url)
+ (sha256 (base32 ,(hash-url dist-url))))))
(build-system node-build-system)
(arguments
(list
#:tests? #f
- #:phases
- #~(modify-phases %standard-phases
- (delete 'build)
- ,@extra-phases)))
+ ,@(match extra-phases
+ (() '())
+ ((phase ..1)
+ `(#:phases
+ #~(modify-phases %standard-phases
+ ,@extra-phases))))))
,@(match dependencies
(() '())
((dependencies ...)
@@ -309,15 +393,16 @@
;;;
(define npm-binary->guix-package
- (lambda* (name #:key (version *semver-range-any*) #:allow-other-keys)
+ (lambda* (name #:key (version *semver-range-any*) binary? #:allow-other-keys)
(let* ((svr (match version
((? string?) (string->semver-range version))
(_ version)))
(pkg (resolve-package name svr)))
- (npm-package->package-sexp pkg))))
+ (npm-package->package-sexp pkg #:binary? binary?))))
-(define* (npm-binary-recursive-import package-name #:key version)
+(define* (npm-binary-recursive-import package-name #:key version binary?)
(recursive-import package-name
#:repo->guix-package (memoize npm-binary->guix-package)
#:version version
- #:guix-name npm-name->name))
+ #:guix-name npm-name->name
+ #:binary? binary?))
diff --git a/guix/import/utils.scm b/guix/import/utils.scm
index a9ff90f0ee..4e0e2876af 100644
--- a/guix/import/utils.scm
+++ b/guix/import/utils.scm
@@ -7,7 +7,7 @@
;;; Copyright © 2019 Robert Vollmert <[email protected]>
;;; Copyright © 2020 Helio Machado <[email protected]>
;;; Copyright © 2020 Martin Becze <[email protected]>
-;;; Copyright © 2021, 2024 Maxim Cournoyer <[email protected]>
+;;; Copyright © 2021, 2024, 2026 Maxim Cournoyer <[email protected]>
;;; Copyright © 2021 Sarah Morgensen <[email protected]>
;;; Copyright © 2021 Xinglu Chen <[email protected]>
;;; Copyright © 2022 Alice Brenon <[email protected]>
@@ -265,22 +265,26 @@ the commit hash, the downloaded directory and its content
hash."
(sha256
(base32 ,hash))))
-(define* (git->origin url proc #:key ref #:rest rest)
+(define* (git->origin url proc #:key ref eager? #:rest rest)
"Return a generated `origin' block of a package depending on the Git version
control system, and the directory in the store where the package has been
downloaded, in case further processing is necessary.
-Unless overwritten with REF, the ref (as defined by the (guix git) module)
-is calculated from the evaluation of PROC with trailing arguments. PROC must
-be a procedure with a 'body property, used to generate the origin sexp."
- (let* ((args (strip-keyword-arguments '(#:ref) rest))
+Unless overwritten with REF, the ref (as defined by the (guix git) module) is
+calculated from the evaluation of PROC with trailing arguments. If EAGER? is
+#t, the same computation (PROC applied to the trailing arguments) is used as
+value of the `commit' field of the generated `git-reference' S-expression.
+Otherwise, PROC must be a procedure with a 'body property, which is used as
+the `commit' field S-expression."
+ (let* ((args (strip-keyword-arguments '(#:ref #:eager?) rest))
(commit (apply proc args))
(ref (or ref (and commit `(tag-or-commit . ,commit))))
(_ directory hash
(if (or ref commit)
(download-git-repository url ref)
(values #f #f #f))))
- (values (git-origin url (peek-body proc) hash) directory)))
+ (values (git-origin url (if eager? commit (peek-body proc)) hash)
+ directory)))
(define* (default-git-error home-page #:optional location)
"Return a procedure to be passed to a `git-error' `catch' for HOME-PAGE at
diff --git a/guix/scripts/import/npm-binary.scm
b/guix/scripts/import/npm-binary.scm
index b2771bc539..a2fd446065 100644
--- a/guix/scripts/import/npm-binary.scm
+++ b/guix/scripts/import/npm-binary.scm
@@ -50,6 +50,8 @@ Import and convert the npm package PACKAGE-NAME using the
-r, --recursive import packages recursively"))
(display (G_ "
-V, --version display version information and exit"))
+ (display (G_ "
+ -b, --binary prefer pre-built sources and skip building"))
(newline)
(show-bug-report-information))
@@ -65,6 +67,9 @@ Import and convert the npm package PACKAGE-NAME using the
(option '(#\r "recursive") #f #f
(lambda (opt name arg result)
(alist-cons 'recursive #t result)))
+ (option '(#\b "binary") #f #f
+ (lambda (opt name arg result)
+ (alist-cons 'binary #t result)))
%standard-import-options))
(define* (package-name->name+version* spec)
@@ -94,16 +99,19 @@ contain '@'."
(('argument . value)
value)
(_ #f))
- (reverse opts))))
+ (reverse opts)))
+ (binary? (assoc-ref opts 'binary)))
(match args
((spec)
(define-values (package-name version)
(package-name->name+version* spec))
(match (if (assoc-ref opts 'recursive)
;; Recursive import
- (npm-binary-recursive-import package-name #:version version)
+ (npm-binary-recursive-import package-name #:version version
+ #:binary? binary?)
;; Single import
- (npm-binary->guix-package package-name #:version version))
+ (npm-binary->guix-package package-name #:version version
+ #:binary? binary?))
((or #f '())
(leave (G_ "failed to download meta-data for package '~a@~a'~%")
package-name version))