From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp2 ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms11 with LMTPS id RNl5Cs3jkl9BGQAA0tVLHw (envelope-from ) for ; Fri, 23 Oct 2020 14:08:13 +0000 Received: from aspmx1.migadu.com ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp2 with LMTPS id INuMBM3jkl9iGAAAB5/wlQ (envelope-from ) for ; Fri, 23 Oct 2020 14:08:13 +0000 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 3BB5F9404CA for ; Fri, 23 Oct 2020 14:08:12 +0000 (UTC) Received: from localhost ([::1]:37630 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kVxjm-00016M-3X for larch@yhetil.org; Fri, 23 Oct 2020 10:08:10 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:36964) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kVxje-00015t-BR for guix-patches@gnu.org; Fri, 23 Oct 2020 10:08:03 -0400 Received: from debbugs.gnu.org ([209.51.188.43]:46381) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1kVxje-0007GS-2t for guix-patches@gnu.org; Fri, 23 Oct 2020 10:08:02 -0400 Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1kVxjd-0007dX-Sn for guix-patches@gnu.org; Fri, 23 Oct 2020 10:08:01 -0400 X-Loop: help-debbugs@gnu.org Subject: [bug#44178] Add a Go Module Importer Resent-From: Katherine Cox-Buday Original-Sender: "Debbugs-submit" Resent-CC: guix-patches@gnu.org Resent-Date: Fri, 23 Oct 2020 14:08:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 44178 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: To: 44178@debbugs.gnu.org X-Debbugs-Original-To: guix-patches@gnu.org Received: via spool by submit@debbugs.gnu.org id=B.160346203429291 (code B ref -1); Fri, 23 Oct 2020 14:08:01 +0000 Received: (at submit) by debbugs.gnu.org; 23 Oct 2020 14:07:14 +0000 Received: from localhost ([127.0.0.1]:57927 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1kVxin-0007cI-I6 for submit@debbugs.gnu.org; Fri, 23 Oct 2020 10:07:14 -0400 Received: from lists.gnu.org ([209.51.188.17]:53784) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1kVxil-0007cA-0o for submit@debbugs.gnu.org; Fri, 23 Oct 2020 10:07:07 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:36654) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kVxik-0000iM-Rx for guix-patches@gnu.org; Fri, 23 Oct 2020 10:07:06 -0400 Received: from mail-il1-x130.google.com ([2607:f8b0:4864:20::130]:41332) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1kVxii-0007AU-26 for guix-patches@gnu.org; Fri, 23 Oct 2020 10:07:06 -0400 Received: by mail-il1-x130.google.com with SMTP id w17so1461012ilg.8 for ; Fri, 23 Oct 2020 07:07:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:user-agent:mime-version; bh=uyjhv6K/f097J75OsRfsyDtv4uU4brSBk+/EuWxGjPI=; b=qDOpGWxL8hmmEXsmarrpLyyVRNsgle6lHQ4SUFxfWMOMpXoGhrP5Y/YIVOMz7WOSje gGq41lpWnLzP3wvZFIN3KMdT7H9lrpjjLQg+Ue5BzDrvmVN1+uouoARfpuVhLpZFIMao q4qMjqJh3MghTAUXIL6nHsMJKj+lhLPHLRLuDDZv4paZ91gKLmqePqjZSgDRvYTijFZ/ 6toIr7QDexToUVMfTzMq80iU5eX9cIh0BuI/Gqn1Q4fEbA7WS+xuycolQ6d4RcWERCIm JSRJ8QGU5QWiyn0vTS8wn+UBwtDi1LdkGt9x5oyQQMTtA+28Ji8VnWdrHQ91F6hNqg6a mAEA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:user-agent :mime-version; bh=uyjhv6K/f097J75OsRfsyDtv4uU4brSBk+/EuWxGjPI=; b=Pnu9svffzmMftqOwmTZ6eCFDhhfqwOTmL8K+WkMideHSyKLr0OOen8PQTbG5DazhkA 7HJZdoSCgL/zRu/DoEZICAbP2JAWJMFZG7tAsbuZRF1GjQZFZhFDP8U6Bi667X4ZZwDP 9bKMax5yEZpDuGPI8k+3klmS6Ebch+lKAtxz/cDB5C38jJBmP7UairJ4wFmxHB0tzWR1 nQEbTj0Pb/+QqHCls2CfKyuD1+nSGgCZ0iMARFg3fFRGi61hhjKx+AhVp1L9zAiGze+3 xvXh6ib3Ym8M7YVcEjRpqcFVLt7hnwpcXH2J0bUjuJiVg651ss+Md7t0RedwFrxoyPkO SdWg== X-Gm-Message-State: AOAM530Sq1WGVwZPFV1PInVcPLZTgjgHC7JUcluWEBPHPK0V6AbepHB9 JFn02I+eX5TOpf0Vgj65P85HqM5h9D/t4Q== X-Google-Smtp-Source: ABdhPJwfzRxnccMMmCEJl4PzPVrYqW/IrhfFCoRkp+SxEa1zSFBA/pskk0kbXrJGJrT3j21fWovYFQ== X-Received: by 2002:a92:d28b:: with SMTP id p11mr1841370ilp.264.1603462020810; Fri, 23 Oct 2020 07:07:00 -0700 (PDT) Received: from washu-v4 (172-221-246-205.res.spectrum.com. [172.221.246.205]) by smtp.gmail.com with ESMTPSA id g26sm692059ion.25.2020.10.23.07.06.59 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 23 Oct 2020 07:06:59 -0700 (PDT) From: Katherine Cox-Buday Date: Fri, 23 Oct 2020 09:06:58 -0500 Message-ID: <87sga5kpdp.fsf@gmail.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Received-SPF: pass client-ip=2607:f8b0:4864:20::130; envelope-from=cox.katherine.e@gmail.com; helo=mail-il1-x130.google.com X-detected-operating-system: by eggs.gnu.org: No matching host in p0f cache. That's all we know. X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Spam-Score: 0.7 (/) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-Spam-Score: -2.3 (--) X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+larch=yhetil.org@gnu.org Sender: "Guix-patches" X-Scanner: scn0 Authentication-Results: aspmx1.migadu.com; dkim=fail (body hash did not verify) header.d=gmail.com header.s=20161025 header.b=qDOpGWxL; dmarc=fail reason="SPF not aligned (relaxed)" header.from=gmail.com (policy=none); spf=pass (aspmx1.migadu.com: domain of guix-patches-bounces@gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=guix-patches-bounces@gnu.org X-Spam-Score: 0.09 X-TUID: g2lpH2eeJeHo --=-=-= Content-Type: text/x-patch; charset=utf-8 Content-Disposition: inline; filename=0001-guix-import-go.scm-Created-Go-Importer.patch Content-Transfer-Encoding: quoted-printable >From cc92cbcf5ae89891f478f319e955419800bdfcf9 Mon Sep 17 00:00:00 2001 From: Katherine Cox-Buday Date: Thu, 22 Oct 2020 19:40:17 -0500 Subject: [PATCH] * guix/import/go.scm: Created Go Importer * guix/scripts/import.scm: Created Go Importer Subcommand * guix/import/go.s= cm (importers): Added Go Importer Subcommand --- guix/import/go.scm | 276 +++++++++++++++++++++++++++++++++++++ guix/scripts/import.scm | 2 +- guix/scripts/import/go.scm | 118 ++++++++++++++++ 3 files changed, 395 insertions(+), 1 deletion(-) create mode 100644 guix/import/go.scm create mode 100644 guix/scripts/import/go.scm diff --git a/guix/import/go.scm b/guix/import/go.scm new file mode 100644 index 0000000000..61009f3565 --- /dev/null +++ b/guix/import/go.scm @@ -0,0 +1,276 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright =C2=A9 2020 Katherine Cox-Buday +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see . + +(define-module (guix import go) + #:use-module (ice-9 match) + #:use-module (ice-9 rdelim) + #:use-module (ice-9 receive) + #:use-module (ice-9 regex) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-9) + #:use-module (guix json) + #:use-module ((guix download) #:prefix download:) + #:use-module (guix import utils) + #:use-module (guix import json) + #:use-module (guix packages) + #:use-module (guix upstream) + #:use-module (guix utils) + #:use-module ((guix licenses) #:prefix license:) + #:use-module (guix base16) + #:use-module (guix base32) + #:use-module (guix build download) + #:use-module (web uri) + + #:export (go-module->guix-package + go-module-recursive-import + infer-module-root)) + +(define (escape-capital-letters s) + "To avoid ambiguity when serving from case-insensitive file systems, the +$module and $version elements are case-encoded by replacing every uppercase +letter with an exclamation mark followed by the corresponding lower-case +letter." + (let ((escaped-string (string))) + (string-for-each-index + (lambda (i) + (let ((c (string-ref s i))) + (set! escaped-string + (string-concatenate + (list escaped-string + (if (char-upper-case? c) "!" "") + (string (char-downcase c))))))) + s) + escaped-string)) + +(define (fetch-latest-version goproxy-url module-path) + "Fetches the version number of the latest version for MODULE-PATH from t= he +given GOPROXY-URL server." + (assoc-ref + (json-fetch (format #f "~a/~a/@latest" goproxy-url + (escape-capital-letters module-path))) + "Version")) + +(define (fetch-go.mod goproxy-url module-path version file) + "Fetches go.mod from the given GOPROXY-URL server for the given MODULE-P= ATH +and VERSION." + (url-fetch (format #f "~a/~a/@v/~a.mod" goproxy-url + (escape-capital-letters module-path) + (escape-capital-letters version)) + file + #:print-build-trace? #f)) + +(define (parse-go.mod go.mod-path) + "Parses a go.mod file and returns an alist of module path to version." + (with-input-from-file go.mod-path + (lambda () + (let ((in-require? #f) + (requirements (list))) + (do ((line (read-line) (read-line))) + ((eof-object? line)) + (set! line (string-trim line)) + ;; The parser is either entering, within, exiting, or after the + ;; require block. The Go toolchain is trustworthy so edge-cases = like + ;; double-entry, etc. need not complect the parser. + (cond + ((string=3D? line "require (") + (set! in-require? #t)) + ((and in-require? (string=3D? line ")")) + (set! in-require? #f)) + (in-require? + (let* ((requirement (string-split line #\space)) + ;; Modules should be unquoted + (module-path (string-delete #\" (car requirement))) + (version (list-ref requirement 1))) + (set! requirements (acons module-path version requirements))= )) + ((string-prefix? "replace" line) + (let* ((requirement (string-split line #\space)) + (module-path (list-ref requirement 1)) + (new-module-path (list-ref requirement 3)) + (version (list-ref requirement 4))) + (set! requirements (assoc-remove! requirements module-path)) + (set! requirements (acons new-module-path version requiremen= ts)))))) + requirements)))) + +(define (module-path-without-major-version module-path) + "Go modules can be appended with a major version indicator, +e.g. /v3. Sometimes it is desirable to work with the root module path. For +instance, for a module path github.com/foo/bar/v3 this function returns +github.com/foo/bar." + (let ((m (string-match "(.*)\\/v[0-9]+$" module-path))) + (if m + (match:substring m 1) + module-path))) + +(define (infer-module-root module-path) + "Go modules can be defined at any level of a repository's tree, but quer= ying +for the meta tag usually can only be done at the webpage at the root of the +repository. Therefore, it is sometimes necessary to try and derive a modul= e's +root path from its path. For a set of well-known forges, the pattern of wh= at +consists of a module's root page is known before hand." + ;; See the following URL for the official Go equivalent: + ;; https://github.com/golang/go/blob/846dce9d05f19a1f53465e62a304dea21b9= 9f910/src/cmd/go/internal/vcs/vcs.go#L1026-L1087 + (define-record-type + (make-scs url-prefix root-regex type) + scs? + (url-prefix scs-url-prefix) + (root-regex scs-root-regex) + (type scs-type)) + (let* ((known-scs + (list + (make-scs + "github.com" + "^(github\\.com/[A-Za-z0-9_.\\-]+/[A-Za-z0-9_.\\-]+)(/[A-Za-z0= -9_.\\-]+)*$" + 'git) + (make-scs + "bitbucket.org" + "^(bitbucket\\.org/([A-Za-z0-9_.\\-]+/[A-Za-z0-9_.\\-]+))(/[A-= Za-z0-9_.\\-]+)*$`" + 'unknown) + (make-scs + "hub.jazz.net/git/" + "^(hub\\.jazz\\.net/git/[a-z0-9]+/[A-Za-z0-9_.\\-]+)(/[A-Za-z0= -9_.\\-]+)*$" + 'git) + (make-scs + "git.apache.org" + "^(git\\.apache\\.org/[a-z0-9_.\\-]+\\.git)(/[A-Za-z0-9_.\\-]+= )*$" + 'git) + (make-scs + "git.openstack.org" + "^(git\\.openstack\\.org/[A-Za-z0-9_.\\-]+/[A-Za-z0-9_.\\-]+)(= \\.git)?(/[A-Za-z0-9_.\\-]+)*$" + 'git))) + (scs (find (lambda (scs) (string-prefix? (scs-url-prefix scs) mod= ule-path)) + known-scs))) + (if scs + (match:substring (string-match (scs-root-regex scs) module-path) 1) + module-path))) + +(define (to-guix-package-name module-path) + "Converts a module's path to the canonical Guix format for Go packages." + (string-downcase + (string-append "go-" + (string-replace-substring + (string-replace-substring + ;; Guix has its own field for version + (module-path-without-major-version module-path) + "." "-") + "/" "-")))) + +(define (fetch-module-meta-data module-path) + "Fetches module meta-data from a module's landing page. This is necessary +because goproxy servers don't currently provide all the information needed= to +build a package." + (let* ((port (http-fetch (string->uri (format #f "https://~a?go-get=3D1"= module-path)))) + (module-metadata #f) + (meta-tag-prefix "symbol (list-ref meta-data 1))) + +(define (module-meta-data-repo-url meta-data goproxy-url) + "Return the URL where the fetcher which will be used can download the so= urce +control." + (if (member (module-meta-data-scs meta-data) '(fossil mod)) + goproxy-url + (list-ref meta-data 2))) + +(define (source-uri scs-type scs-repo-url file) + "Generate the `origin' block of a package depending on what type of sour= ce +control system is being used." + (case scs-type + ((git) + `(origin + (method git-fetch) + (uri (git-reference + (url ,scs-repo-url) + (commit (string-append "v" version)))) + (file-name (git-file-name name version)) + (sha256 + (base32 + ,(guix-hash-url file))))) + ((hg) + `(origin + (method hg-fetch) + (uri (hg-reference + (url ,scs-repo-url) + (changeset ,version))) + (file-name (format #f "~a-~a-checkout" name version)))) + ((svn) + `(origin + (method svn-fetch) + (uri (svn-reference + (url ,scs-repo-url) + (revision (string->number version)) + (recursive? #f))) + (file-name (format #f "~a-~a-checkout" name version)) + (sha256 + (base32 + ,(guix-hash-url file))))) + (else + (raise-exception (format #f "unsupported scs type: ~a" scs-type))))) + +(define* (go-module->guix-package module-path #:key (goproxy-url "https://= proxy.golang.org")) + (call-with-temporary-output-file + (lambda (temp port) + (let* ((latest-version (fetch-latest-version goproxy-url module-path)) + (go.mod-path (fetch-go.mod goproxy-url module-path latest-vers= ion + temp)) + (dependencies (map car (parse-go.mod temp))) + (guix-name (to-guix-package-name module-path)) + (root-module-path (infer-module-root module-path)) + ;; SCS type and URL are not included in goproxy information. F= or + ;; this we need to fetch it from the official module page. + (meta-data (fetch-module-meta-data root-module-path)) + (scs-type (module-meta-data-scs meta-data)) + (scs-repo-url (module-meta-data-repo-url meta-data goproxy-url= ))) + (values + `(package + (name ,guix-name) + ;; Elide the "v" prefix Go uses + (version ,(string-trim latest-version #\v)) + (source + ,(source-uri scs-type scs-repo-url temp)) + (build-system go-build-system) + ,@(maybe-inputs (map to-guix-package-name dependencies)) + ;; TODO(katco): It would be nice to make an effort to fetch this + ;; from known forges, e.g. GitHub + (home-page ,(format #f "https://~a" root-module-path)) + (synopsis "A Go package") + (description ,(format #f "~a is a Go package." guix-name)) + (license #f)) + dependencies))))) + +(define* (go-module-recursive-import package-name + #:key (goproxy-url "https://proxy.gol= ang.org")) + (recursive-import package-name #f + #:repo->guix-package + (lambda (name _) + (go-module->guix-package name + #:goproxy-url goproxy-url)) + #:guix-name to-guix-package-name)) diff --git a/guix/scripts/import.scm b/guix/scripts/import.scm index 0a3863f965..1d2b45d942 100644 --- a/guix/scripts/import.scm +++ b/guix/scripts/import.scm @@ -77,7 +77,7 @@ rather than \\n." ;;; =20 (define importers '("gnu" "nix" "pypi" "cpan" "hackage" "stackage" "elpa" = "gem" - "cran" "crate" "texlive" "json" "opam")) + "go" "cran" "crate" "texlive" "json" "opam")) =20 (define (resolve-importer name) (let ((module (resolve-interface diff --git a/guix/scripts/import/go.scm b/guix/scripts/import/go.scm new file mode 100644 index 0000000000..000039769c --- /dev/null +++ b/guix/scripts/import/go.scm @@ -0,0 +1,118 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright =C2=A9 2020 Katherine Cox-Buday +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see . + +(define-module (guix scripts import go) + #:use-module (guix ui) + #:use-module (guix utils) + #:use-module (guix scripts) + #:use-module (guix import go) + #:use-module (guix scripts import) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-11) + #:use-module (srfi srfi-37) + #:use-module (ice-9 match) + #:use-module (ice-9 format) + #:export (guix-import-go)) + + +;;; +;;; Command-line options. +;;; + +(define %default-options + '()) + +(define (show-help) + (display (G_ "Usage: guix import go PACKAGE-PATH +Import and convert the Go module for PACKAGE-PATH.\n")) + (display (G_ " + -h, --help display this help and exit")) + (display (G_ " + -V, --version display version information and exit")) + (display (G_ " + -r, --recursive generate package expressions for all Go modules\ + that are not yet in Guix")) + (display (G_ " + -p, --goproxy=3DGOPROXY specify which goproxy server to use")) + (newline) + (show-bug-report-information)) + +(define %options + ;; Specification of the command-line options. + (cons* (option '(#\h "help") #f #f + (lambda args + (show-help) + (exit 0))) + (option '(#\V "version") #f #f + (lambda args + (show-version-and-exit "guix import go"))) + (option '(#\r "recursive") #f #f + (lambda (opt name arg result) + (alist-cons 'recursive #t result))) + (option '(#\p "goproxy") #t #f + (lambda (opt name arg result) + (alist-cons 'goproxy + (string->symbol arg) + (alist-delete 'goproxy result)))) + %standard-import-options)) + + +;;; +;;; Entry point. +;;; + +(define (guix-import-go . args) + (define (parse-options) + ;; Return the alist of option values. + (args-fold* args %options + (lambda (opt name arg result) + (leave (G_ "~A: unrecognized option~%") name)) + (lambda (arg result) + (alist-cons 'argument arg result)) + %default-options)) + + (let* ((opts (parse-options)) + (args (filter-map (match-lambda + (('argument . value) + value) + (_ #f)) + (reverse opts)))) + (match args + ((module-name) + (if (assoc-ref opts 'recursive) + (map (match-lambda + ((and ('package ('name name) . rest) pkg) + `(define-public ,(string->symbol name) + ,pkg)) + (_ #f)) + (go-module-recursive-import module-name + #:goproxy-url + (or (assoc-ref opts 'goproxy) + "https://proxy.golang.org"= ))) + (let ((sexp (go-module->guix-package module-name + #:goproxy-url + (or (assoc-ref opts 'gopro= xy) + "https://proxy.golang.= org")))) + (unless sexp + (leave (G_ "failed to download meta-data for module '~a'~%") + module-name)) + sexp))) + (() + (leave (G_ "too few arguments~%"))) + ((many ...) + (leave (G_ "too many arguments~%")))))) --=20 2.28.0 --=-=-= Content-Type: text/plain -- Katherine --=-=-=--