aboutsummaryrefslogtreecommitdiff
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2019 Jakob L. Kreuze <zerodaysfordays@sdf.org>
;;; Copyright © 2020 Brice Waegeneire <brice@waegenei.re>
;;; Copyright © 2022 Matthew James Kraai <kraai@ftbfs.org>
;;; Copyright © 2022 Ricardo Wurmus <rekado@elephly.net>
;;;
;;; 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 <http://www.gnu.org/licenses/>.

(define-module (gnu machine digital-ocean)
  #:use-module (gnu machine ssh)
  #:use-module (gnu machine)
  #:use-module (gnu services)
  #:use-module (gnu services base)
  #:use-module (gnu services networking)
  #:use-module (gnu system)
  #:use-module (gnu system pam)
  #:use-module (guix base32)
  #:use-module (guix derivations)
  #:use-module (guix i18n)
  #:use-module ((guix diagnostics) #:select (formatted-message))
  #:use-module (guix import json)
  #:use-module (guix monads)
  #:use-module (guix records)
  #:use-module (guix ssh)
  #:use-module (guix store)
  #:use-module (ice-9 format)
  #:use-module (ice-9 iconv)
  #:use-module (ice-9 string-fun)
  #:use-module (json)
  #:use-module (rnrs bytevectors)
  #:use-module (srfi srfi-1)
  #:use-module (srfi srfi-2)
  #:use-module (srfi srfi-34)
  #:use-module (srfi srfi-35)
  #:use-module (ssh key)
  #:use-module (ssh sftp)
  #:use-module (ssh shell)
  #:use-module (web client)
  #:use-module (web request)
  #:use-module (web response)
  #:use-module (web uri)
  #:export (digital-ocean-configuration
            digital-ocean-configuration?

            digital-ocean-configuration-ssh-key
            digital-ocean-configuration-tags
            digital-ocean-configuration-region
            digital-ocean-configuration-size
            digital-ocean-configuration-enable-ipv6?

            digital-ocean-environment-type))

;;; Commentary:
;;;
;;; This module implements a high-level interface for provisioning "droplets"
;;; from the Digital Ocean virtual private server (VPS) service.
;;;
;;; Code:

(define %api-base "https://api.digitalocean.com")

(define %digital-ocean-token
  (make-parameter (getenv "GUIX_DIGITAL_OCEAN_TOKEN")))

(define* (post-endpoint endpoint body)
  "Encode BODY as JSON and send it to the Digital Ocean API endpoint
ENDPOINT. This procedure is quite a bit more specialized than 'http-post', as
it takes care to set headers such as 'Content-Type', 'Content-Length', and
'Authorization' appropriately."
  (let* ((uri (string->uri (string-append %api-base endpoint)))
         (body (string->bytevector (scm->json-string body) "UTF-8"))
         (headers `((User-Agent . "Guix Deploy")
                    (Accept . "application/json")
                    (Content-Type . "application/json")
                    (Authorization . ,(format #f "Bearer ~a"
                                              (%digital-ocean-token)))
                    (Content-Length . ,(number->string
                                        (bytevector-length body)))))
         (port (open-socket-for-uri uri))
         (request (build-request uri
                                 #:method 'POST
                                 #:version '(1 . 1)
                                 #:headers headers
                                 #:port port))
         (request (write-request request port)))
    (write-request-body request body)
    (force-output (request-port request))
    (let* ((response (read-response port))
           (body (read-response-body response)))
      (unless (= 2 (floor/ (response-code response) 100))
        (raise
         (condition (&message
                     (message (format
                               #f
                               (G_ "~a: HTTP post failed: ~a (~s)")
                               (uri->string uri)
                               (response-code response)
                               (response-reason-phrase response)))))))
      (close-port port)
      (bytevector->string body "UTF-8"))))

(define (fetch-endpoint endpoint)
  "Return the contents of the Digital Ocean API endpoint ENDPOINT as an
alist. This procedure is quite a bit more specialized than 'json-fetch', as it
takes care to set headers such as 'Accept' and 'Authorization' appropriately."
  (define headers
    `((user-agent . "Guix Deploy")
      (Accept . "application/json")
      (Authorization . ,(format #f "Bearer ~a" (%digital-ocean-token)))))
  (json-fetch (string-append %api-base endpoint) #:headers headers))


;;;
;;; Parameters for droplet creation.
;;;

(define-record-type* <digital-ocean-configuration> digital-ocean-configuration
  make-digital-ocean-configuration
  digital-ocean-configuration?
  this-digital-ocean-configuration
  (ssh-key     digital-ocean-configuration-ssh-key)      ; string
  (tags        digital-ocean-configuration-tags)         ; list of strings
  (region      digital-ocean-configuration-region)       ; string
  (size        digital-ocean-configuration-size)         ; string
  (enable-ipv6? digital-ocean-configuration-enable-ipv6?)) ; boolean

(define (read-key-fingerprint file-name)
  "Read the private key at FILE-NAME and return the key's fingerprint as a hex
string."
  (let* ((privkey (private-key-from-file file-name))
         (pubkey (private-key->public-key privkey))
         (hash (get-public-key-hash pubkey 'md5)))
    (bytevector->hex-string hash)))

(define (machine-droplet machine)
  "Return an alist describing the droplet allocated to MACHINE."
  (let ((tags (digital-ocean-configuration-tags
               (machine-configuration machine))))
    (find (lambda (droplet)
            (equal? (assoc-ref droplet "tags") (list->vector tags)))
          (vector->list
           (assoc-ref (fetch-endpoint "/v2/droplets") "droplets")))))

(define (machine-public-ipv4-network machine)
  "Return the public IPv4 network interface of the droplet allocated to
MACHINE as an alist. The expected fields are 'ip_address', 'netmask', and
'gateway'."
  (and-let* ((droplet (machine-droplet machine))
             (networks (assoc-ref droplet "networks"))
             (network (find (lambda (network)
                              (string= "public" (assoc-ref network "type")))
                            (vector->list (assoc-ref networks "v4")))))
    network))


;;;
;;; Remote evaluation.
;;;

(define (digital-ocean-remote-eval target exp)
  "Internal implementation of 'machine-remote-eval' for MACHINE instances with
an environment type of 'digital-ocean-environment-type'."
  (let* ((network (machine-public-ipv4-network target))
         (address (assoc-ref network "ip_address"))
         (ssh-key (digital-ocean-configuration-ssh-key
                   (machine-configuration target)))
         (delegate (machine
                    (inherit target)
                    (environment managed-host-environment-type)
                    (configuration
                     (machine-ssh-configuration
                      (host-name address)
                      (identity ssh-key)
                      (system "x86_64-linux"))))))
    (machine-remote-eval delegate exp)))


;;;
;;; System deployment.
;;;

;; XXX Copied from (gnu services base)
(define* (ip+netmask->cidr ip netmask #:optional (family AF_INET))
  "Return the CIDR notation (a string) for @var{ip} and @var{netmask}, two
@var{family} address strings, where @var{family} is @code{AF_INET} or
@code{AF_INET6}."
  (let* ((netmask (inet-pton family netmask))
         (bits    (logcount netmask)))
    (string-append ip "/" (number->string bits))))

;; The following script was adapted from the guide available at
;; <https://wiki.pantherx.org/Installation-digital-ocean/>.
(define (guix-infect network)
  "Given NETWORK, an alist describing the Droplet's public IPv4 network
interface, return a Bash script that will install the Guix system."
  (define os
    `(operating-system
       (host-name "gnu-bootstrap")
       (timezone "Etc/UTC")
       (bootloader (bootloader-configuration
                    (bootloader grub-bootloader)
                    (targets '("/dev/vda"))
                    (terminal-outputs '(console))))
       (file-systems (cons (file-system
                             (mount-point "/")
                             (device "/dev/vda1")
                             (type "ext4"))
                           %base-file-systems))
       (services
        (append (list (service static-networking-service-type
                               (list (static-networking
                                      (addresses
                                       (list (network-address
                                              (device "eth0")
                                              (value ,(ip+netmask->cidr
                                                       (assoc-ref network "ip_address")
                                                       (assoc-ref network "netmask"))))))
                                      (routes
                                       (list (network-route
                                              (destination "default")
                                              (gateway ,(assoc-ref network "gateway")))))
                                      (name-servers '("84.200.69.80" "84.200.70.40")))))
                      (simple-service 'guile-load-path-in-global-env
                                      session-environment-service-type
                                      `(("GUILE_LOAD_PATH"
                                         . "/run/current-system/profile/share/guile/site/3.0")
                                        ("GUILE_LOAD_COMPILED_PATH"
                                         . ,(string-append "/run/current-system/profile/lib/guile/3.0/site-ccache:"
                                                           "/run/current-system/profile/share/guile/site/3.0"))))
                      (service openssh-service-type
                               (openssh-configuration
                                (log-level 'debug)
                                (permit-root-login 'prohibit-password))))
            %base-services))))
  (format #f "#!/bin/bash

apt-get update
apt-get install xz-utils -y
wget -nv https://ci.guix.gnu.org/search/latest/archive?query=spec:tarball+status:success+system:x86_64-linux+guix-binary.tar.xz -O guix-binary-nightly.x86_64-linux.tar.xz
cd /tmp
tar --warning=no-timestamp -xf ~~/guix-binary-nightly.x86_64-linux.tar.xz
mv var/guix /var/ && mv gnu /
mkdir -p ~~root/.config/guix
ln -sf /var/guix/profiles/per-user/root/current-guix ~~root/.config/guix/current
export GUIX_PROFILE=\"`echo ~~root`/.config/guix/current\" ;
source $GUIX_PROFILE/etc/profile
groupadd --system guixbuild
for i in `seq -w 1 10`; do
   useradd -g guixbuild -G guixbuild         \
           -d /var/empty -s `which nologin`  \
           -c \"Guix build user $i\" --system  \
           guixbuilder$i;
done;
cp ~~root/.config/guix/current/lib/systemd/system/guix-daemon.service /etc/systemd/system/
systemctl start guix-daemon && systemctl enable guix-daemon
mkdir -p /usr/local/bin
cd /usr/local/bin
ln -s /var/guix/profiles/per-user/root/current-guix/bin/guix
mkdir -p /usr/local/share/info
cd /usr/local/share/info
for i in /var/guix/profiles/per-user/root/current-guix/share/info/*; do
    ln -s $i;
done
guix archive --authorize < ~~root/.config/guix/current/share/guix/ci.guix.gnu.org.pub
# guix pull
guix package -i glibc-utf8-locales
export GUIX_LOCPATH=\"$HOME/.guix-profile/lib/locale\"
guix package -i openssl
cat > /etc/bootstrap-config.scm << EOF
(use-modules (gnu))
(use-service-modules base networking ssh)

~a
EOF
# guix pull
guix system build /etc/bootstrap-config.scm
guix system reconfigure /etc/bootstrap-config.scm
mv /etc /old-etc
mkdir /etc
cp -r /old-etc/{passwd,group,shadow,gshadow,mtab,guix,bootstrap-config.scm} /etc/
guix system reconfigure /etc/bootstrap-config.scm"
          ;; Escape the bare backtick to avoid having it interpreted by Bash.
          (string-replace-substring
           (format #f "~y" os) "`" "\\`")))

(define (machine-wait-until-available machine)
  "Block until the initial Debian image has been installed on the droplet
named DROPLET-NAME."
  (and-let* ((droplet (machine-droplet machine))
             (droplet-id (assoc-ref droplet "id"))
             (endpoint (format #f "/v2/droplets/~a/actions" droplet-id)))
    (let loop ()
      (let ((actions (assoc-ref (fetch-endpoint endpoint) "actions")))
        (unless (every (lambda (action)
                         (string= "completed" (assoc-ref action "status")))
                       (vector->list actions))
          (sleep 5)
          (loop))))))

(define (wait-for-ssh address ssh-key)
  "Block until the an SSH session can be made as 'root' with SSH-KEY at ADDRESS."
  (let loop ()
    (catch #t
      (lambda ()
        (open-ssh-session address #:user "root" #:identity ssh-key))
      (lambda args
        (sleep 5)
        (loop)))))

(define (add-static-networking target network)
  "Return an <operating-system> based on TARGET with a static networking
configuration for the public IPv4 network described by the alist NETWORK."
  (operating-system
    (inherit (machine-operating-system target))
    (services (cons* (service static-networking-service-type
                              (list (static-networking
                                     (addresses
                                      (list (network-address
                                             (device "eth0")
                                             (value (ip+netmask->cidr
                                                     (assoc-ref network "ip_address")
                                                     (assoc-ref network "netmask"))))))
                                     (routes
                                      (list (network-route
                                             (destination "default")
                                             (gateway (assoc-ref network "gateway")))))
                                     (name-servers '("84.200.69.80" "84.200.70.40")))))
                    (simple-service 'guile-load-path-in-global-env
                                    session-environment-service-type
                                    `(("GUILE_LOAD_PATH"
                                       . "/run/current-system/profile/share/guile/site/3.0")
                                      ("GUILE_LOAD_COMPILED_PATH"
                                       . ,(string-append "/run/current-system/profile/lib/guile/3.0/site-ccache:"
                                                         "/run/current-system/profile/share/guile/site/3.0"))))
                    (operating-system-user-services
                     (machine-operating-system target))))))

(define (deploy-digital-ocean target)
  "Internal implementation of 'deploy-machine' for 'machine' instances with an
environment type of 'digital-ocean-environment-type'."
  (maybe-raise-missing-api-key-error)
  (maybe-raise-unsupported-configuration-error target)
  (let* ((config (machine-configuration target))
         (name (machine-display-name target))
         (region (digital-ocean-configuration-region config))
         (size (digital-ocean-configuration-size config))
         (ssh-key (digital-ocean-configuration-ssh-key config))
         (fingerprint (read-key-fingerprint ssh-key))
         (enable-ipv6? (digital-ocean-configuration-enable-ipv6? config))
         (tags (digital-ocean-configuration-tags config))
         (request-body `(("name" . ,name)
                         ("region" . ,region)
                         ("size" . ,size)
                         ("image" . "debian-9-x64")
                         ("ssh_keys" . ,(vector fingerprint))
                         ("backups" . #f)
                         ("ipv6" . ,enable-ipv6?)
                         ("user_data" . #nil)
                         ("private_networking" . #nil)
                         ("volumes" . #nil)
                         ("tags" . ,(list->vector tags))))
         (response (post-endpoint "/v2/droplets" request-body)))
    (machine-wait-until-available target)
    (let* ((network (machine-public-ipv4-network target))
           (address (assoc-ref network "ip_address")))
      (wait-for-ssh address ssh-key)
      (let* ((ssh-session (open-ssh-session address #:user "root" #:identity ssh-key))
             (sftp-session (make-sftp-session ssh-session)))
        (call-with-remote-output-file sftp-session "/tmp/guix-infect.sh"
                                      (lambda (port)
                                        (display (guix-infect network) port)))
        (rexec ssh-session "/bin/bash /tmp/guix-infect.sh")
        ;; Session will close upon rebooting, which will raise 'guile-ssh-error.
        (catch 'guile-ssh-error
          (lambda () (rexec ssh-session "reboot"))
          (lambda args #t)))
      (wait-for-ssh address ssh-key)
      (let ((delegate (machine
                       (operating-system (add-static-networking target network))
                       (environment managed-host-environment-type)
                       (configuration
                        (machine-ssh-configuration
                         (host-name address)
                         (identity ssh-key)
                         (system "x86_64-linux"))))))
        (deploy-machine delegate)))))


;;;
;;; Roll-back.
;;;

(define (roll-back-digital-ocean target)
  "Internal implementation of 'roll-back-machine' for MACHINE instances with an
environment type of 'digital-ocean-environment-type'."
  (let* ((network (machine-public-ipv4-network target))
         (address (assoc-ref network "ip_address"))
         (ssh-key (digital-ocean-configuration-ssh-key
                   (machine-configuration target)))
         (delegate (machine
                    (inherit target)
                    (environment managed-host-environment-type)
                    (configuration
                     (machine-ssh-configuration
                      (host-name address)
                      (identity ssh-key)
                      (system "x86_64-linux"))))))
    (roll-back-machine delegate)))


;;;
;;; Environment type.
;;;

(define digital-ocean-environment-type
  (environment-type
   (machine-remote-eval digital-ocean-remote-eval)
   (deploy-machine      deploy-digital-ocean)
   (roll-back-machine   roll-back-digital-ocean)
   (name                'digital-ocean-environment-type)
   (description         "Provisioning of \"droplets\": virtual machines
 provided by the Digital Ocean virtual private server (VPS) service.")))


(define (maybe-raise-missing-api-key-error)
  (unless (%digital-ocean-token)
    (raise (condition
            (&message
             (message (G_ "No Digital Ocean access token was provided. This \
may be fixed by setting the environment variable GUIX_DIGITAL_OCEAN_TOKEN to \
one procured from https://cloud.digitalocean.com/account/api/tokens.")))))))

(define (maybe-raise-unsupported-configuration-error machine)
  "Raise an error if MACHINE's configuration is not an instance of
<digital-ocean-configuration>."
  (let ((config (machine-configuration machine))
        (environment (environment-type-name (machine-environment machine))))
    (unless (and config (digital-ocean-configuration? config))
      (raise (formatted-message (G_ "unsupported machine configuration '~a' \
for environment of type '~a'")
                                config
                                environment)))))
lation-test): New procedures. (%extra-packages, installation-os-for-gui-tests) (%test-gui-installed-os): New variable. Ludovic Courtès 2020-02-22tests: Factorize LUKS passphrase....* gnu/tests/install.scm (%luks-passphrase): New variable. (%encrypted-root-installation-script): Use it. (enter-luks-passphrase): Use it. Ludovic Courtès 2020-01-19tests: install: "raid-root-os" test uses RAID-1 instead of RAID-0....Fixes <https://bugs.gnu.org/38086>. Thanks to Vagrant and Tobias! * gnu/tests/install.scm (%raid-root-os)[initrd-modules]: Add "raid1" instead of "raid0". (%raid-root-installation-script): Make the partitions twice as big. Invoke 'mdadm' with '--level=mirror' instead of '--level=stripe'; connect "yes" to its stdin. (%test-raid-root-os): Set #:target-size to 2.8 GiB. Ludovic Courtès 2020-01-03tests: install: Test a JFS root file system....* gnu/tests/install.scm (%jfs-root-os, %jfs-root-installation-script) (%test-jfs-root-os): New variables. Tobias Geerinckx-Rice 2019-11-18tests: install: Fix typo....* gnu/tests/install.scm (run-install): Fix typo in docstring. Maxim Cournoyer 2019-11-06tests: install: Increase root partition size....1.2G had become slightly too small on x86_64. This is a followup to 8dfb0c969e513276c632b8d26fb3601fa02993ca. * gnu/tests/install.scm (%simple-installation-script) (%extlinux-gpt-installation-script) (%simple-installation-script-for-/dev/vda): Switch from 1.2G to 1.4G. Ludovic Courtès 2019-07-06tests: encrypted-root-os: Increase root partition size....1.2G had become slightly too small on x86_64. * gnu/tests/install.scm (%encrypted-root-installation-script): Increase root partition size to 1.3G. Ludovic Courtès dures. Move %xz-parallel-args to the new 'compression helpers' section. * tests/packages.scm: Add tests. Add missing copyright year for Jan. * guix/build/gnu-build-system.scm (first-subdirectory): Return #f when no sub-directory was found. (unpack): Support more file types, including uncompressed plain files. Maxim Cournoyer 2020-11-29Merge remote-tracking branch 'origin/master' into core-updatesChristopher Baines 2020-10-20packages: Better preserve object identity when rewriting....Fixes a bug whereby the presence of propagated inputs could lead to two non-eq? but actually equal packages in a bag's inputs. The problem would manifest itself when running, for instance: guix build inkscape -d --with-graft=glib=glib-networking --no-grafts The resulting derivation would differ due from that without '--with-graft'. This was due to the fact that glib propagates libffi; this instance of libffi was not rewritten even though other instances in the graph were rewritten. Thus, glib would end up with two non-eq? libffi instances, which in turn would lead to duplicate entries in its '%build-inputs' variable. Fixes <https://bugs.gnu.org/43890>. * guix/packages.scm (package-mapping)[rewrite]: Remove call to 'cut?' and call 'replace' unconditionally. [replace]: Add 'cut?' case. * tests/guix-build.sh: Add test combining '--no-grafts' and '--with-graft'. * tests/packages.scm ("package-input-rewriting/spec, identity") ("package-input-rewriting, identity"): New tests. Ludovic Courtès 2020-10-19Merge branch 'staging'...Conflicts: gnu/packages/admin.scm gnu/packages/commencement.scm gnu/packages/gdb.scm gnu/packages/llvm.scm gnu/packages/package-management.scm gnu/packages/tls.scm Maxim Cournoyer 2020-10-15packages: Delete duplicate inputs when lowering bags....This is a followup to 18fa433bf5c420868562b9f4b017c5c97251a44b and <https://issues.guix.gnu.org/43508>. * guix/packages.scm (derivation=?, input=?): New procedures. (bag->derivation, bag->cross-derivation): Add calls to 'delete-duplicates'. * tests/packages.scm ("package-derivation, inputs deduplicated"): New test. Ludovic Courtès 2020-10-12packages: Add 'package-with-c-toolchain'....* guix/build-system.scm (build-system-with-c-toolchain): New procedure. * guix/packages.scm (package-with-c-toolchain): New procedure. * tests/packages.scm ("package-with-c-toolchain"): New test. * doc/guix.texi (package Reference): Document 'package-with-c-toolchain'. (Build Systems): Mention it. Ludovic Courtès 2020-10-02guix package: Re-apply package transformation when upgrading....* guix/scripts/package.scm (transaction-upgrade-entry)[upgrade]: Add 'transform' parameter. Pass PKG through it. Use 'manifest-entry-with-transformations'. Call 'options->transformation' to get the transformation procedure. * tests/guix-package.sh: Add 'guix package -u' test. * tests/packages.scm ("transaction-upgrade-entry, transformation options preserved"): New test. * doc/guix.texi (Invoking guix package): Mention that transformations are preserved across upgrades. (Package Transformation Options): Likewise. Ludovic Courtès 2020-09-27packages: 'package-input-rewriting' has a #:deep? parameter....* guix/packages.scm (package-input-rewriting): Add #:deep? and pass it to 'package-mapping'. [replacement-property]: New variable. [rewrite]: Check it. [cut?]: New procedure. * tests/packages.scm ("package-input-rewriting"): Pass #:deep? #f and ensure implicit inputs were not rewritten. Avoid 'eq?' comparisons. ("package-input-rewriting, deep"): New test. * gnu/packages/guile.scm (package-for-guile-2.0, package-for-guile-3.0): Pass #:deep? #f. Ludovic Courtès 2020-09-27packages: 'package-mapping' correctly recurses into 'replacement'....Previously, something like: guix build glib --with-graft=glibc=glibc@2.29 would produce a result showing that rewriting rules were not applied to libx11@1.6.A (a replacement). * guix/packages.scm (package-mapping): Call REPLACE instead of PROC to 'replacement'. * tests/packages.scm ("package-input-rewriting/spec, graft"): New test. Ludovic Courtès 2020-09-27packages: 'package-input-rewriting/spec' can rewrite implicit dependencies....With this change, '--with-input', '--with-graft', etc. also apply to implicit dependencies. Thus, it's now possible to do: guix build python-itsdangerous --with-input=python-wrapper=python@2 or: guix build hello --with-graft=glibc=glibc@2.29 Additionally, before, implicit inputs were not rewritten, which could lead to duplicates in the output of 'bag-transitive-inputs' (packages that are not 'eq?' but lead to the same derivation). This in turn would lead to unnecessary rebuilds when using '--with-input' & co. This change fixes it by ensuring even implicit inputs are rewritten. Fixes <https://bugs.gnu.org/42156>. * guix/packages.scm (package-input-rewriting/spec): Add #:deep? defaulting to #true, and pass it to 'package-mapping'. [replacement-property]: New variable. [rewrite]: Check that property and set it on the result of PROC. [cut?]: New procedure. * tests/packages.scm ("package-input-rewriting/spec"): Ensure implicit inputs were unchanged. ("package-input-rewriting/spec, partial match"): Pass #:deep? #f. ("package-input-rewriting/spec, deep") ("package-input-rewriting/spec, no duplicates"): New tests. (package/inherit): Move before use. * tests/guix-build.sh: Add tests. * tests/scripts-build.scm ("options->transformation, with-graft"): Compare dependencies by package name or derivation file name. * doc/guix.texi (Defining Packages): Adjust accordingly. Ludovic Courtès 2020-09-27packages: 'package-mapping' can recurse on implicit inputs....* guix/packages.scm (build-system-with-package-mapping): New procedure. (package-mapping): Add #:deep? and honor it. * tests/packages.scm ("package-mapping"): Compare the direct inputs of the bag of P0 and that of P1. ("package-mapping, deep"): New test. Ludovic Courtès 2020-08-24tests: Add a debug output to "fold-available-packages with/without cache"....This should help to debug test failures due to duplicated packages. * tests/packages ("fold-available-packages with/without cache"): Print duplicated packages. Mathieu Othacehe 2020-07-25Use 'formatted-message' instead of '&message' where appropriate....* gnu.scm (%try-use-modules): Use 'formatted-message' instead of '&message'. * gnu/machine/digital-ocean.scm (maybe-raise-unsupported-configuration-error): Likewise. * gnu/machine/ssh.scm (machine-check-file-system-availability): Likewise. (machine-check-building-for-appropriate-system): Likewise. (deploy-managed-host): Likewise. (maybe-raise-unsupported-configuration-error): Likewise. * gnu/packages.scm (search-patch): Likewise. * gnu/services.scm (%service-with-default-value): Likewise. (files->etc-directory): Likewise. (fold-services): Likewise. * gnu/system.scm (locale-name->definition*): Likewise. * gnu/system/mapped-devices.scm (check-device-initrd-modules): Likewise. (check-luks-device): Likewise. * guix/channels.scm (latest-channel-instance): Likewise. * guix/cve.scm (json->cve-items): Likewise. * guix/git-authenticate.scm (commit-signing-key): Likewise. (commit-authorized-keys): Likewise. (authenticate-commit): Likewise. (verify-introductory-commit): Likewise. * guix/remote.scm (remote-pipe-for-gexp): Likewise. * guix/scripts/graph.scm (assert-package): Likewise. * guix/scripts/offload.scm (private-key-from-file*): Likewise. * guix/ssh.scm (authenticate-server*): Likewise. (open-ssh-session): Likewise. (remote-inferior): Likewise. * guix/ui.scm (matching-generations): Likewise. * guix/upstream.scm (package-update): Likewise. * tests/channels.scm ("latest-channel-instances, missing introduction for 'guix'"): Catch 'formatted-message?'. ("authenticate-channel, wrong first commit signer"): Likewise. * tests/lint.scm ("patches: not found"): Adjust message string. * tests/packages.scm ("patch not found yields a run-time error"): Catch 'formatted-message?'. * guix/lint.scm (check-patch-file-names): Handle 'formatted-message?'. (check-derivation): Ditto. Ludovic Courtès 2020-07-25utils: Move <location> and '&error-location' to (guix diagnostics)....* guix/utils.scm (<location>, source-properties->location) (location->source-properties, &error-location): Move to... * guix/diagnostics.scm: ... here. * gnu.scm: Adjust imports accordingly. * gnu/machine.scm: Likewise. * gnu/system.scm: Likewise. * gnu/tests.scm: Likewise. * guix/inferior.scm: Likewise. * tests/channels.scm: Likewise. * tests/packages.scm: Likewise. Ludovic Courtès 2020-07-13packages: Ensure bags are insensitive to '%current-system'....Fixes <https://bugs.gnu.org/42327>. Reported by Jan Nieuwenhuizen <janneke@gnu.org>. This is a followup to f52fbf7094c9c346d38ad469cc8d92d18387786e. * guix/packages.scm (bag-transitive-inputs, bag-transitive-build-inputs) (bag-transitive-host-inputs, bag-transitive-target-inputs): Parameterize %CURRENT-SYSTEM in addition to %CURRENT-TARGET-SYSTEM. * tests/packages.scm ("package->bag, sensitivity to %current-system"): New test. Ludovic Courtès 2020-06-27packages: Recognize SHA3 and BLAKE2s for 'content-hash'....* guix/packages.scm (build-content-hash): Add 'sha3-256', 'sha3-512', and 'blake2s-256'. * tests/packages.scm ("package-source-derivation, origin, sha3-512"): New test. Ludovic Courtès 2020-06-11packages: 'package-grafts' returns grafts for all the relevant outputs....Fixes <https://bugs.gnu.org/41796>. Reported by Jakub Kądziołka <kuba@kadziolka.net>. * guix/packages.scm (input-graft): Add 'output' parameter and honor it. Add OUTPUT to the cache key. (input-cross-graft): Likewise. (fold-bag-dependencies): Operate on inputs instead of nodes. Turn VISITED into a vhash instead of a set. Pass PROC HEAD and OUTPUT instead of just HEAD. (bag-grafts): Adjust accordingly. * tests/packages.scm ("package-grafts, dependency on several outputs"): New test. Ludovic Courtès 2020-06-06packages: Make 'bag-grafts' insensitive to '%current-target-system'....Fixes <https://bugs.gnu.org/41713>. Reported by Mathieu Othacehe. * guix/packages.scm (bag-grafts): Wrap 'fold-bag-dependencies' calls in 'parameterize'. * tests/packages.scm ("package->bag, sensitivity to %current-target-system"): New test. Ludovic Courtès 2020-05-23tests: Use a #:prefix for (gcrypt hash)....* tests/packages.scm: Use #:prefix instead of #:hide for (gcrypt hash). This accomodates for 'sha512' syntax literal matches with Guile-Gcrypt 0.3.0, which exports 'sha512' in addition to 'sha256'. Ludovic Courtès 2020-05-22packages: Introduce <content-hash> and use it in <origin>....* guix/packages.scm (<content-hash>): New record type. (define-content-hash-constructor, build-content-hash) (content-hash): New macros. (print-content-hash): New procedure. (<origin>): Rename constructor to '%origin'. [sha256]: Remove field. [hash]: New field. Adjust users. (origin-compatibility-helper, origin): New macros. (origin-sha256): New deprecated procedure. (origin->derivation): Adjust accordingly. * tests/packages.scm ("package-source-derivation, origin, sha512"): New test. * guix/tests.scm: Hide (gcrypt hash) 'sha256' for proper syntax matching. * tests/challenge.scm: Add #:prefix for (gcrypt hash) and adjust users. * tests/derivations.scm: Likewise. * tests/store.scm: Likewise. * tests/graph.scm ("bag DAG, including origins"): Provide 'sha256' field with the right length. * gnu/packages/aspell.scm (aspell-dictionary) (aspell-dict-ca, aspell-dict-it): Use 'hash' and 'content-hash' for proper syntax matching. * gnu/packages/bash.scm (bash-patch): Rename 'sha256' to 'sha256-bv'. * gnu/packages/bootstrap.scm (bootstrap-executable): Rename 'sha256' to 'bv'. * gnu/packages/readline.scm (readline-patch): Likewise. * gnu/packages/virtualization.scm (qemu-patch): Rename 'sha256' to 'sha256-bv'. * guix/import/utils.scm: Hide (gcrypt hash) 'sha256'. Ludovic Courtès