2018-08-27 18:05:49 +02:00
|
|
|
|
;;; GNU Guix --- Functional package management for GNU
|
2020-01-09 11:21:31 +01:00
|
|
|
|
;;; Copyright © 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
|
2018-10-13 08:39:23 +02:00
|
|
|
|
;;; Copyright © 2018 Ricardo Wurmus <rekado@elephly.net>
|
2019-07-13 16:31:50 +02:00
|
|
|
|
;;; Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
|
2018-08-27 18:05:49 +02:00
|
|
|
|
;;;
|
|
|
|
|
;;; 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 (guix channels)
|
2019-09-14 23:16:54 +02:00
|
|
|
|
#:use-module (git)
|
2018-08-27 18:05:49 +02:00
|
|
|
|
#:use-module (guix git)
|
2020-06-08 12:01:24 +02:00
|
|
|
|
#:use-module (guix git-authenticate)
|
|
|
|
|
#:use-module ((guix openpgp)
|
|
|
|
|
#:select (openpgp-public-key-fingerprint
|
|
|
|
|
openpgp-format-fingerprint))
|
|
|
|
|
#:use-module (guix base16)
|
2018-08-27 18:05:49 +02:00
|
|
|
|
#:use-module (guix records)
|
|
|
|
|
#:use-module (guix gexp)
|
2019-01-11 17:23:39 +01:00
|
|
|
|
#:use-module (guix modules)
|
2018-08-27 18:05:49 +02:00
|
|
|
|
#:use-module (guix discovery)
|
|
|
|
|
#:use-module (guix monads)
|
|
|
|
|
#:use-module (guix profiles)
|
2019-10-02 11:15:48 +02:00
|
|
|
|
#:use-module (guix packages)
|
2020-06-08 12:01:24 +02:00
|
|
|
|
#:use-module (guix progress)
|
2018-08-27 18:05:49 +02:00
|
|
|
|
#:use-module (guix derivations)
|
2019-01-20 18:45:40 +01:00
|
|
|
|
#:use-module (guix combinators)
|
2019-06-03 23:00:42 +02:00
|
|
|
|
#:use-module (guix diagnostics)
|
2019-09-14 23:16:54 +02:00
|
|
|
|
#:use-module (guix sets)
|
2018-08-27 18:05:49 +02:00
|
|
|
|
#:use-module (guix store)
|
|
|
|
|
#:use-module (guix i18n)
|
2019-01-20 18:11:11 +01:00
|
|
|
|
#:use-module ((guix utils)
|
|
|
|
|
#:select (source-properties->location
|
2020-01-09 11:21:31 +01:00
|
|
|
|
&error-location
|
|
|
|
|
&fix-hint))
|
2018-08-27 18:05:49 +02:00
|
|
|
|
#:use-module (srfi srfi-1)
|
2018-10-13 08:39:23 +02:00
|
|
|
|
#:use-module (srfi srfi-2)
|
2018-08-27 18:05:49 +02:00
|
|
|
|
#:use-module (srfi srfi-9)
|
|
|
|
|
#:use-module (srfi srfi-11)
|
2019-09-21 21:29:30 +02:00
|
|
|
|
#:use-module (srfi srfi-26)
|
2019-01-20 18:11:11 +01:00
|
|
|
|
#:use-module (srfi srfi-34)
|
|
|
|
|
#:use-module (srfi srfi-35)
|
2019-01-11 17:23:39 +01:00
|
|
|
|
#:autoload (guix self) (whole-package make-config.scm)
|
|
|
|
|
#:autoload (guix inferior) (gexp->derivation-in-inferior) ;FIXME: circular dep
|
2020-05-14 22:53:44 +02:00
|
|
|
|
#:autoload (guix quirks) (%quirks %patches applicable-patch? apply-patch)
|
2020-06-08 12:01:24 +02:00
|
|
|
|
#:use-module (ice-9 format)
|
2018-08-27 18:05:49 +02:00
|
|
|
|
#:use-module (ice-9 match)
|
2019-01-17 16:57:53 +01:00
|
|
|
|
#:use-module (ice-9 vlist)
|
2019-10-02 11:15:48 +02:00
|
|
|
|
#:use-module ((ice-9 rdelim) #:select (read-string))
|
2020-06-08 12:01:24 +02:00
|
|
|
|
#:use-module ((rnrs bytevectors) #:select (bytevector=?))
|
2018-08-27 18:05:49 +02:00
|
|
|
|
#:export (channel
|
|
|
|
|
channel?
|
|
|
|
|
channel-name
|
|
|
|
|
channel-url
|
|
|
|
|
channel-branch
|
|
|
|
|
channel-commit
|
2020-06-08 12:01:24 +02:00
|
|
|
|
channel-introduction
|
2018-08-27 18:05:49 +02:00
|
|
|
|
channel-location
|
|
|
|
|
|
2020-06-08 12:01:24 +02:00
|
|
|
|
channel-introduction?
|
|
|
|
|
;; <channel-introduction> accessors purposefully omitted for now.
|
|
|
|
|
|
2018-08-27 18:05:49 +02:00
|
|
|
|
%default-channels
|
2019-04-17 13:56:40 +02:00
|
|
|
|
guix-channel?
|
2018-08-27 18:05:49 +02:00
|
|
|
|
|
|
|
|
|
channel-instance?
|
|
|
|
|
channel-instance-channel
|
|
|
|
|
channel-instance-commit
|
|
|
|
|
channel-instance-checkout
|
|
|
|
|
|
2020-06-08 12:01:24 +02:00
|
|
|
|
authenticate-channel
|
2018-08-27 18:05:49 +02:00
|
|
|
|
latest-channel-instances
|
2018-11-26 17:16:18 +01:00
|
|
|
|
checkout->channel-instance
|
2018-09-05 10:35:43 +02:00
|
|
|
|
latest-channel-derivation
|
2018-09-18 12:08:05 +02:00
|
|
|
|
channel-instances->manifest
|
2019-01-11 17:23:39 +01:00
|
|
|
|
%channel-profile-hooks
|
2019-08-16 14:57:06 +02:00
|
|
|
|
channel-instances->derivation
|
2020-05-20 22:15:54 +02:00
|
|
|
|
ensure-forward-channel-update
|
2019-08-16 14:57:06 +02:00
|
|
|
|
|
2019-09-14 23:16:54 +02:00
|
|
|
|
profile-channels
|
|
|
|
|
|
|
|
|
|
channel-news-entry?
|
|
|
|
|
channel-news-entry-commit
|
2019-09-21 21:29:30 +02:00
|
|
|
|
channel-news-entry-tag
|
2019-09-14 23:16:54 +02:00
|
|
|
|
channel-news-entry-title
|
|
|
|
|
channel-news-entry-body
|
|
|
|
|
|
|
|
|
|
channel-news-for-commit))
|
2018-08-27 18:05:49 +02:00
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
;;;
|
|
|
|
|
;;; This module implements "channels." A channel is usually a source of
|
|
|
|
|
;;; package definitions. There's a special channel, the 'guix' channel, that
|
|
|
|
|
;;; provides all of Guix, including its commands and its documentation.
|
|
|
|
|
;;; User-defined channels are expected to typically provide a bunch of .scm
|
|
|
|
|
;;; files meant to be added to the '%package-search-path'.
|
|
|
|
|
;;;
|
|
|
|
|
;;; This module provides tools to fetch and update channels from a Git
|
|
|
|
|
;;; repository and to build them.
|
|
|
|
|
;;;
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
|
|
(define-record-type* <channel> channel make-channel
|
|
|
|
|
channel?
|
|
|
|
|
(name channel-name)
|
|
|
|
|
(url channel-url)
|
|
|
|
|
(branch channel-branch (default "master"))
|
|
|
|
|
(commit channel-commit (default #f))
|
2020-06-08 12:01:24 +02:00
|
|
|
|
(introduction channel-introduction (default #f))
|
2018-08-27 18:05:49 +02:00
|
|
|
|
(location channel-location
|
|
|
|
|
(default (current-source-location)) (innate)))
|
|
|
|
|
|
2020-06-08 12:01:24 +02:00
|
|
|
|
;; Channel introductions. A "channel introduction" provides a commit/signer
|
|
|
|
|
;; pair that specifies the first commit of the authentication process as well
|
|
|
|
|
;; as its signer's fingerprint. The pair must be signed by the signer of that
|
|
|
|
|
;; commit so that only them may emit this introduction. Introductions are
|
|
|
|
|
;; used to bootstrap trust in a channel.
|
|
|
|
|
(define-record-type <channel-introduction>
|
|
|
|
|
(make-channel-introduction first-signed-commit first-commit-signer
|
|
|
|
|
signature)
|
|
|
|
|
channel-introduction?
|
|
|
|
|
(first-signed-commit channel-introduction-first-signed-commit) ;hex string
|
|
|
|
|
(first-commit-signer channel-introduction-first-commit-signer) ;bytevector
|
|
|
|
|
(signature channel-introduction-signature)) ;string
|
|
|
|
|
|
|
|
|
|
(define %guix-channel-introduction
|
|
|
|
|
;; Introduction of the official 'guix channel. The chosen commit is the
|
|
|
|
|
;; first one that introduces '.guix-authorizations' on the 'staging'
|
|
|
|
|
;; branch that was eventually merged in 'master'. Any branch starting
|
|
|
|
|
;; before that commit cannot be merged or it will be rejected by 'guix pull'
|
|
|
|
|
;; & co.
|
|
|
|
|
(make-channel-introduction
|
|
|
|
|
"9edb3f66fd807b096b48283debdcddccfea34bad" ;2020-05-26
|
|
|
|
|
(base16-string->bytevector
|
|
|
|
|
(string-downcase
|
|
|
|
|
(string-filter char-set:hex-digit ;mbakke
|
|
|
|
|
"BBB0 2DDF 2CEA F6A8 0D1D E643 A2A0 6DF2 A33A 54FA")))
|
|
|
|
|
#f)) ;TODO: Add an intro signature so it can be exported.
|
|
|
|
|
|
2020-06-08 22:22:36 +02:00
|
|
|
|
(define %default-channel-url
|
|
|
|
|
;; URL of the default 'guix' channel.
|
|
|
|
|
"https://git.savannah.gnu.org/git/guix.git")
|
|
|
|
|
|
2018-08-27 18:05:49 +02:00
|
|
|
|
(define %default-channels
|
|
|
|
|
;; Default list of channels.
|
|
|
|
|
(list (channel
|
|
|
|
|
(name 'guix)
|
2018-09-05 23:31:51 +02:00
|
|
|
|
(branch "master")
|
2020-06-08 22:22:36 +02:00
|
|
|
|
(url %default-channel-url)
|
2020-06-08 12:01:24 +02:00
|
|
|
|
(introduction %guix-channel-introduction))))
|
2018-08-27 18:05:49 +02:00
|
|
|
|
|
|
|
|
|
(define (guix-channel? channel)
|
|
|
|
|
"Return true if CHANNEL is the 'guix' channel."
|
|
|
|
|
(eq? 'guix (channel-name channel)))
|
|
|
|
|
|
2020-06-08 22:22:36 +02:00
|
|
|
|
(define (ensure-default-introduction chan)
|
|
|
|
|
"If CHAN represents the \"official\" 'guix' channel and lacks an
|
|
|
|
|
introduction, add it."
|
|
|
|
|
(if (and (guix-channel? chan)
|
|
|
|
|
(not (channel-introduction chan))
|
|
|
|
|
(string=? (channel-url chan) %default-channel-url))
|
|
|
|
|
(channel (inherit chan)
|
|
|
|
|
(introduction %guix-channel-introduction))
|
|
|
|
|
chan))
|
|
|
|
|
|
2018-08-27 18:05:49 +02:00
|
|
|
|
(define-record-type <channel-instance>
|
|
|
|
|
(channel-instance channel commit checkout)
|
|
|
|
|
channel-instance?
|
|
|
|
|
(channel channel-instance-channel)
|
|
|
|
|
(commit channel-instance-commit)
|
|
|
|
|
(checkout channel-instance-checkout))
|
|
|
|
|
|
2018-10-13 08:39:23 +02:00
|
|
|
|
(define-record-type <channel-metadata>
|
2020-06-15 16:20:14 +02:00
|
|
|
|
(channel-metadata directory dependencies news-file keyring-reference url)
|
2018-10-13 08:39:23 +02:00
|
|
|
|
channel-metadata?
|
2019-07-17 00:41:10 +02:00
|
|
|
|
(directory channel-metadata-directory) ;string with leading slash
|
2019-09-14 23:16:54 +02:00
|
|
|
|
(dependencies channel-metadata-dependencies) ;list of <channel>
|
2020-06-08 12:01:24 +02:00
|
|
|
|
(news-file channel-metadata-news-file) ;string | #f
|
2020-06-15 16:20:14 +02:00
|
|
|
|
(keyring-reference channel-metadata-keyring-reference) ;string
|
|
|
|
|
(url channel-metadata-url)) ;string | #f
|
2020-06-08 12:01:24 +02:00
|
|
|
|
|
|
|
|
|
(define %default-keyring-reference
|
|
|
|
|
;; Default value of the 'keyring-reference' field.
|
|
|
|
|
"keyring")
|
2018-10-13 08:39:23 +02:00
|
|
|
|
|
2018-08-27 18:05:49 +02:00
|
|
|
|
(define (channel-reference channel)
|
|
|
|
|
"Return the \"reference\" for CHANNEL, an sexp suitable for
|
|
|
|
|
'latest-repository-commit'."
|
|
|
|
|
(match (channel-commit channel)
|
|
|
|
|
(#f `(branch . ,(channel-branch channel)))
|
|
|
|
|
(commit `(commit . ,(channel-commit channel)))))
|
|
|
|
|
|
2019-07-17 00:04:41 +02:00
|
|
|
|
(define (read-channel-metadata port)
|
|
|
|
|
"Read from PORT channel metadata in the format expected for the
|
|
|
|
|
'.guix-channel' file. Return a <channel-metadata> record, or raise an error
|
|
|
|
|
if valid metadata could not be read from PORT."
|
|
|
|
|
(match (read port)
|
|
|
|
|
(('channel ('version 0) properties ...)
|
|
|
|
|
(let ((directory (and=> (assoc-ref properties 'directory) first))
|
2019-09-14 23:16:54 +02:00
|
|
|
|
(dependencies (or (assoc-ref properties 'dependencies) '()))
|
2020-06-08 12:01:24 +02:00
|
|
|
|
(news-file (and=> (assoc-ref properties 'news-file) first))
|
2020-06-15 16:20:14 +02:00
|
|
|
|
(url (and=> (assoc-ref properties 'url) first))
|
2020-06-08 12:01:24 +02:00
|
|
|
|
(keyring-reference
|
|
|
|
|
(or (and=> (assoc-ref properties 'keyring-reference) first)
|
|
|
|
|
%default-keyring-reference)))
|
2019-07-17 00:04:41 +02:00
|
|
|
|
(channel-metadata
|
2019-09-14 23:16:54 +02:00
|
|
|
|
(cond ((not directory) "/") ;directory
|
2019-07-17 00:41:10 +02:00
|
|
|
|
((string-prefix? "/" directory) directory)
|
|
|
|
|
(else (string-append "/" directory)))
|
2019-09-14 23:16:54 +02:00
|
|
|
|
(map (lambda (item) ;dependencies
|
2019-07-17 00:04:41 +02:00
|
|
|
|
(let ((get (lambda* (key #:optional default)
|
|
|
|
|
(or (and=> (assoc-ref item key) first) default))))
|
|
|
|
|
(and-let* ((name (get 'name))
|
|
|
|
|
(url (get 'url))
|
|
|
|
|
(branch (get 'branch "master")))
|
|
|
|
|
(channel
|
|
|
|
|
(name name)
|
|
|
|
|
(branch branch)
|
|
|
|
|
(url url)
|
|
|
|
|
(commit (get 'commit))))))
|
2019-09-14 23:16:54 +02:00
|
|
|
|
dependencies)
|
2020-06-08 12:01:24 +02:00
|
|
|
|
news-file
|
2020-06-15 16:20:14 +02:00
|
|
|
|
keyring-reference
|
|
|
|
|
url)))
|
2019-07-17 00:04:41 +02:00
|
|
|
|
((and ('channel ('version version) _ ...) sexp)
|
|
|
|
|
(raise (condition
|
|
|
|
|
(&message (message "unsupported '.guix-channel' version"))
|
|
|
|
|
(&error-location
|
|
|
|
|
(location (source-properties->location
|
|
|
|
|
(source-properties sexp)))))))
|
|
|
|
|
(sexp
|
|
|
|
|
(raise (condition
|
|
|
|
|
(&message (message "invalid '.guix-channel' file"))
|
|
|
|
|
(&error-location
|
|
|
|
|
(location (source-properties->location
|
|
|
|
|
(source-properties sexp)))))))))
|
|
|
|
|
|
2019-07-13 16:31:50 +02:00
|
|
|
|
(define (read-channel-metadata-from-source source)
|
|
|
|
|
"Return a channel-metadata record read from channel's SOURCE/.guix-channel
|
2019-07-17 00:41:10 +02:00
|
|
|
|
description file, or return the default channel-metadata record if that file
|
|
|
|
|
doesn't exist."
|
2019-07-17 00:04:41 +02:00
|
|
|
|
(catch 'system-error
|
|
|
|
|
(lambda ()
|
|
|
|
|
(call-with-input-file (string-append source "/.guix-channel")
|
|
|
|
|
read-channel-metadata))
|
|
|
|
|
(lambda args
|
|
|
|
|
(if (= ENOENT (system-error-errno args))
|
2020-06-15 16:20:14 +02:00
|
|
|
|
(channel-metadata "/" '() #f %default-keyring-reference #f)
|
2019-07-17 00:04:41 +02:00
|
|
|
|
(apply throw args)))))
|
|
|
|
|
|
|
|
|
|
(define (channel-instance-metadata instance)
|
2019-07-13 16:31:50 +02:00
|
|
|
|
"Return a channel-metadata record read from the channel INSTANCE's
|
2019-07-17 00:41:10 +02:00
|
|
|
|
description file or its default value."
|
2019-07-13 16:31:50 +02:00
|
|
|
|
(read-channel-metadata-from-source (channel-instance-checkout instance)))
|
|
|
|
|
|
2018-10-13 08:39:23 +02:00
|
|
|
|
(define (channel-instance-dependencies instance)
|
|
|
|
|
"Return the list of channels that are declared as dependencies for the given
|
|
|
|
|
channel INSTANCE."
|
2019-07-17 00:41:10 +02:00
|
|
|
|
(channel-metadata-dependencies (channel-instance-metadata instance)))
|
2018-10-13 08:39:23 +02:00
|
|
|
|
|
2020-05-06 22:45:31 +02:00
|
|
|
|
(define (apply-patches checkout commit patches)
|
|
|
|
|
"Apply the matching PATCHES to CHECKOUT, modifying files in place. The
|
|
|
|
|
result is unspecified."
|
|
|
|
|
(let loop ((patches patches))
|
|
|
|
|
(match patches
|
|
|
|
|
(() #t)
|
2020-05-14 22:53:44 +02:00
|
|
|
|
((patch rest ...)
|
|
|
|
|
(when (applicable-patch? patch checkout commit)
|
|
|
|
|
(apply-patch patch checkout))
|
2020-05-06 22:45:31 +02:00
|
|
|
|
(loop rest)))))
|
|
|
|
|
|
2020-06-08 12:01:24 +02:00
|
|
|
|
(define commit-short-id
|
|
|
|
|
(compose (cut string-take <> 7) oid->string commit-id))
|
|
|
|
|
|
|
|
|
|
(define (verify-introductory-commit repository introduction keyring)
|
|
|
|
|
"Raise an exception if the first commit described in INTRODUCTION doesn't
|
|
|
|
|
have the expected signer."
|
|
|
|
|
(define commit-id
|
|
|
|
|
(channel-introduction-first-signed-commit introduction))
|
|
|
|
|
|
|
|
|
|
(define actual-signer
|
|
|
|
|
(openpgp-public-key-fingerprint
|
|
|
|
|
(commit-signing-key repository (string->oid commit-id)
|
|
|
|
|
keyring)))
|
|
|
|
|
|
|
|
|
|
(define expected-signer
|
|
|
|
|
(channel-introduction-first-commit-signer introduction))
|
|
|
|
|
|
|
|
|
|
(unless (bytevector=? expected-signer actual-signer)
|
|
|
|
|
(raise (condition
|
|
|
|
|
(&message
|
|
|
|
|
(message (format #f (G_ "initial commit ~a is signed by '~a' \
|
|
|
|
|
instead of '~a'")
|
|
|
|
|
commit-id
|
|
|
|
|
(openpgp-format-fingerprint actual-signer)
|
|
|
|
|
(openpgp-format-fingerprint expected-signer))))))))
|
|
|
|
|
|
|
|
|
|
(define* (authenticate-channel channel checkout commit
|
|
|
|
|
#:key (keyring-reference-prefix "origin/"))
|
|
|
|
|
"Authenticate the given COMMIT of CHANNEL, available at CHECKOUT, a
|
|
|
|
|
directory containing a CHANNEL checkout. Raise an error if authentication
|
|
|
|
|
fails."
|
|
|
|
|
;; XXX: Too bad we need to re-open CHECKOUT.
|
|
|
|
|
(with-repository checkout repository
|
|
|
|
|
(define start-commit
|
|
|
|
|
(commit-lookup repository
|
|
|
|
|
(string->oid
|
|
|
|
|
(channel-introduction-first-signed-commit
|
|
|
|
|
(channel-introduction channel)))))
|
|
|
|
|
|
|
|
|
|
(define end-commit
|
|
|
|
|
(commit-lookup repository (string->oid commit)))
|
|
|
|
|
|
|
|
|
|
(define cache-key
|
|
|
|
|
(string-append "channels/" (symbol->string (channel-name channel))))
|
|
|
|
|
|
|
|
|
|
(define keyring-reference
|
|
|
|
|
(channel-metadata-keyring-reference
|
|
|
|
|
(read-channel-metadata-from-source checkout)))
|
|
|
|
|
|
|
|
|
|
(define keyring
|
|
|
|
|
(load-keyring-from-reference repository
|
|
|
|
|
(string-append keyring-reference-prefix
|
|
|
|
|
keyring-reference)))
|
|
|
|
|
|
|
|
|
|
(define authenticated-commits
|
|
|
|
|
;; Previously-authenticated commits that don't need to be checked again.
|
|
|
|
|
(filter-map (lambda (id)
|
|
|
|
|
(false-if-exception
|
|
|
|
|
(commit-lookup repository (string->oid id))))
|
|
|
|
|
(previously-authenticated-commits cache-key)))
|
|
|
|
|
|
|
|
|
|
(define commits
|
|
|
|
|
;; Commits to authenticate, excluding the closure of
|
|
|
|
|
;; AUTHENTICATED-COMMITS.
|
|
|
|
|
(commit-difference end-commit start-commit
|
|
|
|
|
authenticated-commits))
|
|
|
|
|
|
|
|
|
|
(define reporter
|
|
|
|
|
(progress-reporter/bar (length commits)))
|
|
|
|
|
|
2020-06-21 16:39:27 +02:00
|
|
|
|
;; When COMMITS is empty, it's because END-COMMIT is in the closure of
|
|
|
|
|
;; START-COMMIT and/or AUTHENTICATED-COMMITS, in which case it's known to
|
|
|
|
|
;; be authentic already.
|
|
|
|
|
(unless (null? commits)
|
|
|
|
|
(format (current-error-port)
|
|
|
|
|
(G_ "Authenticating channel '~a', \
|
2020-06-08 12:01:24 +02:00
|
|
|
|
commits ~a to ~a (~h new commits)...~%")
|
2020-06-21 16:39:27 +02:00
|
|
|
|
(channel-name channel)
|
|
|
|
|
(commit-short-id start-commit)
|
|
|
|
|
(commit-short-id end-commit)
|
|
|
|
|
(length commits))
|
|
|
|
|
|
|
|
|
|
;; If it's our first time, verify CHANNEL's introductory commit.
|
|
|
|
|
(when (null? authenticated-commits)
|
|
|
|
|
(verify-introductory-commit repository
|
|
|
|
|
(channel-introduction channel)
|
|
|
|
|
keyring))
|
|
|
|
|
|
|
|
|
|
(call-with-progress-reporter reporter
|
|
|
|
|
(lambda (report)
|
|
|
|
|
(authenticate-commits repository commits
|
|
|
|
|
#:keyring keyring
|
|
|
|
|
#:report-progress report)))
|
|
|
|
|
|
|
|
|
|
(cache-authenticated-commit cache-key
|
|
|
|
|
(oid->string
|
|
|
|
|
(commit-id end-commit))))))
|
2020-06-08 12:01:24 +02:00
|
|
|
|
|
2020-05-06 22:45:31 +02:00
|
|
|
|
(define* (latest-channel-instance store channel
|
2020-05-20 22:15:54 +02:00
|
|
|
|
#:key (patches %patches)
|
2020-06-08 22:46:06 +02:00
|
|
|
|
starting-commit
|
2020-06-08 23:22:17 +02:00
|
|
|
|
(authenticate? #f)
|
2020-06-08 22:46:06 +02:00
|
|
|
|
(validate-pull
|
|
|
|
|
ensure-forward-channel-update))
|
|
|
|
|
"Return the latest channel instance for CHANNEL. When STARTING-COMMIT is
|
|
|
|
|
true, call VALIDATE-PULL with CHANNEL, STARTING-COMMIT, the target commit, and
|
2020-06-08 23:22:17 +02:00
|
|
|
|
their relation. When AUTHENTICATE? is false, CHANNEL is not authenticated."
|
2020-05-06 22:45:31 +02:00
|
|
|
|
(define (dot-git? file stat)
|
|
|
|
|
(and (string=? (basename file) ".git")
|
|
|
|
|
(eq? 'directory (stat:type stat))))
|
|
|
|
|
|
2020-06-08 22:22:36 +02:00
|
|
|
|
(let-values (((channel)
|
|
|
|
|
(ensure-default-introduction channel))
|
|
|
|
|
((checkout commit relation)
|
2020-05-06 22:45:31 +02:00
|
|
|
|
(update-cached-checkout (channel-url channel)
|
2020-05-20 22:15:54 +02:00
|
|
|
|
#:ref (channel-reference channel)
|
|
|
|
|
#:starting-commit starting-commit)))
|
2020-06-08 22:46:06 +02:00
|
|
|
|
(when relation
|
|
|
|
|
(validate-pull channel starting-commit commit relation))
|
|
|
|
|
|
2020-06-08 23:22:17 +02:00
|
|
|
|
(if authenticate?
|
|
|
|
|
(if (channel-introduction channel)
|
|
|
|
|
(authenticate-channel channel checkout commit)
|
|
|
|
|
;; TODO: Warn for all the channels once the authentication interface
|
|
|
|
|
;; is public.
|
|
|
|
|
(when (guix-channel? channel)
|
|
|
|
|
(warning (G_ "channel '~a' lacks an introduction and \
|
2020-06-08 12:01:24 +02:00
|
|
|
|
cannot be authenticated~%")
|
2020-06-08 23:22:17 +02:00
|
|
|
|
(channel-name channel))))
|
|
|
|
|
(warning (G_ "channel authentication disabled~%")))
|
2020-06-08 12:01:24 +02:00
|
|
|
|
|
2020-05-06 22:45:31 +02:00
|
|
|
|
(when (guix-channel? channel)
|
|
|
|
|
;; Apply the relevant subset of PATCHES directly in CHECKOUT. This is
|
|
|
|
|
;; safe to do because 'switch-to-ref' eventually does a hard reset.
|
|
|
|
|
(apply-patches checkout commit patches))
|
|
|
|
|
|
|
|
|
|
(let* ((name (url+commit->name (channel-url channel) commit))
|
|
|
|
|
(checkout (add-to-store store name #t "sha256" checkout
|
|
|
|
|
#:select? (negate dot-git?))))
|
2020-06-08 22:46:06 +02:00
|
|
|
|
(channel-instance channel commit checkout))))
|
2020-05-20 22:15:54 +02:00
|
|
|
|
|
2020-06-08 22:46:06 +02:00
|
|
|
|
(define (ensure-forward-channel-update channel start commit relation)
|
2020-05-20 22:15:54 +02:00
|
|
|
|
"Raise an error if RELATION is not 'ancestor, meaning that START is not an
|
2020-06-08 22:46:06 +02:00
|
|
|
|
ancestor of COMMIT, unless CHANNEL specifies a commit.
|
2020-05-20 22:15:54 +02:00
|
|
|
|
|
|
|
|
|
This procedure implements a channel update policy meant to be used as a
|
|
|
|
|
#:validate-pull argument."
|
|
|
|
|
(match relation
|
|
|
|
|
('ancestor #t)
|
|
|
|
|
('self #t)
|
|
|
|
|
(_
|
2020-05-20 23:18:09 +02:00
|
|
|
|
(raise (make-compound-condition
|
|
|
|
|
(condition
|
|
|
|
|
(&message (message
|
|
|
|
|
(format #f (G_ "\
|
2020-05-20 22:15:54 +02:00
|
|
|
|
aborting update of channel '~a' to commit ~a, which is not a descendant of ~a")
|
2020-05-20 23:18:09 +02:00
|
|
|
|
(channel-name channel)
|
2020-06-08 22:46:06 +02:00
|
|
|
|
commit start))))
|
2020-05-20 22:15:54 +02:00
|
|
|
|
|
2020-05-20 23:18:09 +02:00
|
|
|
|
;; If the user asked for a specific commit, they might want
|
|
|
|
|
;; that to happen nevertheless, so tell them about the
|
|
|
|
|
;; relevant 'guix pull' option.
|
|
|
|
|
(if (channel-commit channel)
|
|
|
|
|
(condition
|
|
|
|
|
(&fix-hint
|
|
|
|
|
(hint (G_ "Use @option{--allow-downgrades} to force
|
|
|
|
|
this downgrade."))))
|
|
|
|
|
(condition
|
|
|
|
|
(&fix-hint
|
|
|
|
|
(hint (G_ "This could indicate that the channel has
|
2020-05-20 22:15:54 +02:00
|
|
|
|
been tampered with and is trying to force a roll-back, preventing you from
|
|
|
|
|
getting the latest updates. If you think this is not the case, explicitly
|
2020-05-20 23:18:09 +02:00
|
|
|
|
allow non-forward updates."))))))))))
|
2020-05-20 22:15:54 +02:00
|
|
|
|
|
2020-06-15 16:20:14 +02:00
|
|
|
|
(define (channel-instance-primary-url instance)
|
|
|
|
|
"Return the primary URL advertised for INSTANCE, or #f if there is no such
|
|
|
|
|
information."
|
|
|
|
|
(channel-metadata-url (channel-instance-metadata instance)))
|
|
|
|
|
|
2020-05-20 22:15:54 +02:00
|
|
|
|
(define* (latest-channel-instances store channels
|
|
|
|
|
#:key
|
|
|
|
|
(current-channels '())
|
2020-06-08 23:22:17 +02:00
|
|
|
|
(authenticate? #t)
|
2020-05-20 22:15:54 +02:00
|
|
|
|
(validate-pull
|
|
|
|
|
ensure-forward-channel-update))
|
2018-08-27 18:05:49 +02:00
|
|
|
|
"Return a list of channel instances corresponding to the latest checkouts of
|
2020-05-20 22:15:54 +02:00
|
|
|
|
CHANNELS and the channels on which they depend.
|
|
|
|
|
|
2020-06-08 23:22:17 +02:00
|
|
|
|
When AUTHENTICATE? is true, authenticate the subset of CHANNELS that has a
|
|
|
|
|
\"channel introduction\".
|
|
|
|
|
|
2020-05-20 22:15:54 +02:00
|
|
|
|
CURRENT-CHANNELS is the list of currently used channels. It is compared
|
|
|
|
|
against the newly-fetched instances of CHANNELS, and VALIDATE-PULL is called
|
|
|
|
|
for each channel update and can choose to emit warnings or raise an error,
|
|
|
|
|
depending on the policy it implements."
|
2018-10-13 08:39:23 +02:00
|
|
|
|
;; Only process channels that are unique, or that are more specific than a
|
|
|
|
|
;; previous channel specification.
|
|
|
|
|
(define (ignore? channel others)
|
|
|
|
|
(member channel others
|
|
|
|
|
(lambda (a b)
|
|
|
|
|
(and (eq? (channel-name a) (channel-name b))
|
|
|
|
|
(or (channel-commit b)
|
|
|
|
|
(not (or (channel-commit a)
|
|
|
|
|
(channel-commit b))))))))
|
2019-01-20 18:45:40 +01:00
|
|
|
|
|
2020-05-20 22:15:54 +02:00
|
|
|
|
(define (current-commit name)
|
|
|
|
|
;; Return the current commit for channel NAME.
|
|
|
|
|
(any (lambda (channel)
|
|
|
|
|
(and (eq? (channel-name channel) name)
|
|
|
|
|
(channel-commit channel)))
|
|
|
|
|
current-channels))
|
|
|
|
|
|
2020-05-20 15:55:37 +02:00
|
|
|
|
(let loop ((channels channels)
|
|
|
|
|
(previous-channels '()))
|
|
|
|
|
;; Accumulate a list of instances. A list of processed channels is also
|
|
|
|
|
;; accumulated to decide on duplicate channel specifications.
|
|
|
|
|
(define-values (resulting-channels instances)
|
|
|
|
|
(fold2 (lambda (channel previous-channels instances)
|
|
|
|
|
(if (ignore? channel previous-channels)
|
|
|
|
|
(values previous-channels instances)
|
|
|
|
|
(begin
|
|
|
|
|
(format (current-error-port)
|
|
|
|
|
(G_ "Updating channel '~a' from Git repository at '~a'...~%")
|
|
|
|
|
(channel-name channel)
|
|
|
|
|
(channel-url channel))
|
2020-06-08 22:46:06 +02:00
|
|
|
|
(let* ((current (current-commit (channel-name channel)))
|
|
|
|
|
(instance
|
|
|
|
|
(latest-channel-instance store channel
|
2020-06-08 23:22:17 +02:00
|
|
|
|
#:authenticate?
|
|
|
|
|
authenticate?
|
2020-06-08 22:46:06 +02:00
|
|
|
|
#:validate-pull
|
|
|
|
|
validate-pull
|
|
|
|
|
#:starting-commit
|
|
|
|
|
current)))
|
2020-06-15 16:20:14 +02:00
|
|
|
|
(when authenticate?
|
|
|
|
|
;; CHANNEL is authenticated so we can trust the
|
|
|
|
|
;; primary URL advertised in its metadata and warn
|
|
|
|
|
;; about possibly stale mirrors.
|
|
|
|
|
(let ((primary-url (channel-instance-primary-url
|
|
|
|
|
instance)))
|
|
|
|
|
(unless (or (not primary-url)
|
|
|
|
|
(channel-commit channel)
|
|
|
|
|
(string=? primary-url (channel-url channel)))
|
|
|
|
|
(warning (G_ "pulled channel '~a' from a mirror \
|
|
|
|
|
of ~a, which might be stale~%")
|
|
|
|
|
(channel-name channel)
|
|
|
|
|
primary-url))))
|
2020-05-20 22:15:54 +02:00
|
|
|
|
|
2020-05-20 15:55:37 +02:00
|
|
|
|
(let-values (((new-instances new-channels)
|
|
|
|
|
(loop (channel-instance-dependencies instance)
|
|
|
|
|
previous-channels)))
|
|
|
|
|
(values (append (cons channel new-channels)
|
|
|
|
|
previous-channels)
|
|
|
|
|
(append (cons instance new-instances)
|
|
|
|
|
instances)))))))
|
|
|
|
|
previous-channels
|
|
|
|
|
'() ;instances
|
|
|
|
|
channels))
|
2019-01-20 18:45:40 +01:00
|
|
|
|
|
2020-05-20 15:55:37 +02:00
|
|
|
|
(let ((instance-name (compose channel-name channel-instance-channel)))
|
|
|
|
|
;; Remove all earlier channel specifications if they are followed by a
|
|
|
|
|
;; more specific one.
|
|
|
|
|
(values (delete-duplicates instances
|
|
|
|
|
(lambda (a b)
|
|
|
|
|
(eq? (instance-name a) (instance-name b))))
|
|
|
|
|
resulting-channels))))
|
2018-08-27 18:05:49 +02:00
|
|
|
|
|
2018-11-26 17:16:18 +01:00
|
|
|
|
(define* (checkout->channel-instance checkout
|
|
|
|
|
#:key commit
|
|
|
|
|
(url checkout) (name 'guix))
|
|
|
|
|
"Return a channel instance for CHECKOUT, which is assumed to be a checkout
|
|
|
|
|
of COMMIT at URL. Use NAME as the channel name."
|
|
|
|
|
(let* ((commit (or commit (make-string 40 #\0)))
|
|
|
|
|
(channel (channel (name name)
|
|
|
|
|
(commit commit)
|
|
|
|
|
(url url))))
|
|
|
|
|
(channel-instance channel commit checkout)))
|
|
|
|
|
|
2018-08-27 18:05:49 +02:00
|
|
|
|
(define %self-build-file
|
|
|
|
|
;; The file containing code to build Guix. This serves the same purpose as
|
|
|
|
|
;; a makefile, and, similarly, is intended to always keep this name.
|
|
|
|
|
"build-aux/build-self.scm")
|
|
|
|
|
|
|
|
|
|
(define %pull-version
|
|
|
|
|
;; This is the version of the 'guix pull' protocol. It specifies what's
|
|
|
|
|
;; expected from %SELF-BUILD-FILE. The initial version ("0") was when we'd
|
|
|
|
|
;; place a set of compiled Guile modules in ~/.config/guix/latest.
|
|
|
|
|
1)
|
|
|
|
|
|
2019-01-18 10:15:35 +01:00
|
|
|
|
(define (standard-module-derivation name source core dependencies)
|
|
|
|
|
"Return a derivation that builds with CORE, a Guix instance, the Scheme
|
|
|
|
|
modules in SOURCE and that depend on DEPENDENCIES, a list of lowerable
|
|
|
|
|
objects. The assumption is that SOURCE contains package modules to be added
|
|
|
|
|
to '%package-module-path'."
|
|
|
|
|
|
2019-07-13 16:31:50 +02:00
|
|
|
|
(let* ((metadata (read-channel-metadata-from-source source))
|
2019-07-17 00:41:10 +02:00
|
|
|
|
(directory (channel-metadata-directory metadata)))
|
2019-07-13 16:31:50 +02:00
|
|
|
|
|
|
|
|
|
(define build
|
|
|
|
|
;; This is code that we'll run in CORE, a Guix instance, with its own
|
|
|
|
|
;; modules and so on. That way, we make sure these modules are built for
|
|
|
|
|
;; the right Guile version, with the right dependencies, and that they get
|
|
|
|
|
;; to see the right (gnu packages …) modules.
|
|
|
|
|
(with-extensions dependencies
|
|
|
|
|
#~(begin
|
|
|
|
|
(use-modules (guix build compile)
|
|
|
|
|
(guix build utils)
|
|
|
|
|
(srfi srfi-26))
|
|
|
|
|
|
|
|
|
|
(define go
|
|
|
|
|
(string-append #$output "/lib/guile/" (effective-version)
|
|
|
|
|
"/site-ccache"))
|
|
|
|
|
(define scm
|
|
|
|
|
(string-append #$output "/share/guile/site/"
|
|
|
|
|
(effective-version)))
|
2019-01-18 10:15:35 +01:00
|
|
|
|
|
2019-07-17 00:41:10 +02:00
|
|
|
|
(let* ((subdir #$directory)
|
2019-07-13 16:31:50 +02:00
|
|
|
|
(source (string-append #$source subdir)))
|
|
|
|
|
(compile-files source go (find-files source "\\.scm$"))
|
|
|
|
|
(mkdir-p (dirname scm))
|
|
|
|
|
(symlink (string-append #$source subdir) scm))
|
2019-01-18 10:15:35 +01:00
|
|
|
|
|
2019-07-13 16:31:50 +02:00
|
|
|
|
scm)))
|
2019-01-18 10:15:35 +01:00
|
|
|
|
|
2019-07-13 16:31:50 +02:00
|
|
|
|
(gexp->derivation-in-inferior name build core)))
|
2018-08-27 18:05:49 +02:00
|
|
|
|
|
2019-10-02 11:15:48 +02:00
|
|
|
|
(define* (guile-for-source source #:optional (quirks %quirks))
|
|
|
|
|
"Return the Guile package to use when building SOURCE or #f if the default
|
|
|
|
|
'%guile-for-build' should be good enough."
|
|
|
|
|
(let loop ((quirks quirks))
|
|
|
|
|
(match quirks
|
|
|
|
|
(()
|
|
|
|
|
#f)
|
|
|
|
|
(((predicate . guile) rest ...)
|
|
|
|
|
(if (predicate source) (guile) (loop rest))))))
|
|
|
|
|
|
2020-05-15 11:53:13 +02:00
|
|
|
|
(define (call-with-guile guile thunk)
|
|
|
|
|
(lambda (store)
|
|
|
|
|
(values (parameterize ((%guile-for-build
|
|
|
|
|
(if guile
|
|
|
|
|
(package-derivation store guile)
|
|
|
|
|
(%guile-for-build))))
|
|
|
|
|
(run-with-store store (thunk)))
|
|
|
|
|
store)))
|
|
|
|
|
|
|
|
|
|
(define-syntax-rule (with-guile guile exp ...)
|
|
|
|
|
"Set GUILE as the '%guile-for-build' parameter for the dynamic extent of
|
|
|
|
|
EXP, a series of monadic expressions."
|
|
|
|
|
(call-with-guile guile (lambda ()
|
|
|
|
|
(mbegin %store-monad exp ...))))
|
|
|
|
|
|
2020-04-06 23:21:43 +02:00
|
|
|
|
(define (with-trivial-build-handler mvalue)
|
|
|
|
|
"Run MVALUE, a monadic value, with a \"trivial\" build handler installed
|
|
|
|
|
that unconditionally resumes the continuation."
|
|
|
|
|
(lambda (store)
|
|
|
|
|
(with-build-handler (lambda (continue . _)
|
|
|
|
|
(continue #t))
|
|
|
|
|
(values (run-with-store store mvalue)
|
|
|
|
|
store))))
|
|
|
|
|
|
2018-08-27 18:05:49 +02:00
|
|
|
|
(define* (build-from-source name source
|
2019-01-18 10:15:35 +01:00
|
|
|
|
#:key core verbose? commit
|
2018-08-27 18:05:49 +02:00
|
|
|
|
(dependencies '()))
|
|
|
|
|
"Return a derivation to build Guix from SOURCE, using the self-build script
|
2019-01-18 10:15:35 +01:00
|
|
|
|
contained therein; use COMMIT as the version string. When CORE is true, build
|
|
|
|
|
package modules under SOURCE using CORE, an instance of Guix."
|
2018-08-27 18:05:49 +02:00
|
|
|
|
;; Running the self-build script makes it easier to update the build
|
|
|
|
|
;; procedure: the self-build script of the Guix-to-be-installed contains the
|
|
|
|
|
;; right dependencies, build procedure, etc., which the Guix-in-use may not
|
|
|
|
|
;; be know.
|
|
|
|
|
(define script
|
|
|
|
|
(string-append source "/" %self-build-file))
|
|
|
|
|
|
|
|
|
|
(if (file-exists? script)
|
|
|
|
|
(let ((build (save-module-excursion
|
|
|
|
|
(lambda ()
|
2019-01-28 22:42:48 +01:00
|
|
|
|
;; Disable deprecation warnings; it's OK for SCRIPT to
|
|
|
|
|
;; use deprecated APIs and the user doesn't have to know
|
|
|
|
|
;; about it.
|
2019-06-03 23:00:42 +02:00
|
|
|
|
(parameterize ((guix-warning-port
|
2019-01-28 22:42:48 +01:00
|
|
|
|
(%make-void-port "w")))
|
2019-10-02 11:15:48 +02:00
|
|
|
|
(primitive-load script)))))
|
|
|
|
|
(guile (guile-for-source source)))
|
2018-08-27 18:05:49 +02:00
|
|
|
|
;; BUILD must be a monadic procedure of at least one argument: the
|
|
|
|
|
;; source tree.
|
|
|
|
|
;;
|
|
|
|
|
;; Note: BUILD can return #f if it does not support %PULL-VERSION. In
|
|
|
|
|
;; the future we'll fall back to a previous version of the protocol
|
|
|
|
|
;; when that happens.
|
2020-05-15 11:53:13 +02:00
|
|
|
|
(with-guile guile
|
2020-04-06 23:21:43 +02:00
|
|
|
|
;; BUILD is usually quite costly. Install a "trivial" build handler
|
|
|
|
|
;; so we don't bounce an outer build-accumulator handler that could
|
|
|
|
|
;; cause us to redo half of the BUILD computation several times just
|
|
|
|
|
;; to realize it gives the same result.
|
|
|
|
|
(with-trivial-build-handler
|
|
|
|
|
(build source #:verbose? verbose? #:version commit
|
|
|
|
|
#:pull-version %pull-version))))
|
2018-08-27 18:05:49 +02:00
|
|
|
|
|
|
|
|
|
;; Build a set of modules that extend Guix using the standard method.
|
2019-01-18 10:15:35 +01:00
|
|
|
|
(standard-module-derivation name source core dependencies)))
|
2018-08-27 18:05:49 +02:00
|
|
|
|
|
2019-01-18 10:15:35 +01:00
|
|
|
|
(define* (build-channel-instance instance
|
|
|
|
|
#:optional core (dependencies '()))
|
2018-08-27 18:05:49 +02:00
|
|
|
|
"Return, as a monadic value, the derivation for INSTANCE, a channel
|
|
|
|
|
instance. DEPENDENCIES is a list of extensions providing Guile modules that
|
|
|
|
|
INSTANCE depends on."
|
|
|
|
|
(build-from-source (symbol->string
|
|
|
|
|
(channel-name (channel-instance-channel instance)))
|
|
|
|
|
(channel-instance-checkout instance)
|
|
|
|
|
#:commit (channel-instance-commit instance)
|
2019-01-18 10:15:35 +01:00
|
|
|
|
#:core core
|
2018-08-27 18:05:49 +02:00
|
|
|
|
#:dependencies dependencies))
|
|
|
|
|
|
2019-01-17 16:57:53 +01:00
|
|
|
|
(define (resolve-dependencies instances)
|
|
|
|
|
"Return a procedure that, given one of the elements of INSTANCES, returns
|
|
|
|
|
list of instances it depends on."
|
|
|
|
|
(define channel-instance-name
|
|
|
|
|
(compose channel-name channel-instance-channel))
|
|
|
|
|
|
|
|
|
|
(define table ;map a name to an instance
|
|
|
|
|
(fold (lambda (instance table)
|
|
|
|
|
(vhash-consq (channel-instance-name instance)
|
|
|
|
|
instance table))
|
|
|
|
|
vlist-null
|
|
|
|
|
instances))
|
|
|
|
|
|
|
|
|
|
(define edges
|
|
|
|
|
(fold (lambda (instance edges)
|
|
|
|
|
(fold (lambda (channel edges)
|
|
|
|
|
(let ((name (channel-name channel)))
|
|
|
|
|
(match (vhash-assq name table)
|
|
|
|
|
((_ . target)
|
|
|
|
|
(vhash-consq instance target edges)))))
|
|
|
|
|
edges
|
|
|
|
|
(channel-instance-dependencies instance)))
|
|
|
|
|
vlist-null
|
|
|
|
|
instances))
|
|
|
|
|
|
|
|
|
|
(lambda (instance)
|
|
|
|
|
(vhash-foldq* cons '() instance edges)))
|
|
|
|
|
|
2018-08-27 18:05:49 +02:00
|
|
|
|
(define (channel-instance-derivations instances)
|
|
|
|
|
"Return the list of derivations to build INSTANCES, in the same order as
|
|
|
|
|
INSTANCES."
|
|
|
|
|
(define core-instance
|
|
|
|
|
;; The 'guix' channel is treated specially: it's an implicit dependency of
|
|
|
|
|
;; all the other channels.
|
|
|
|
|
(find (lambda (instance)
|
|
|
|
|
(guix-channel? (channel-instance-channel instance)))
|
|
|
|
|
instances))
|
|
|
|
|
|
2019-01-17 16:57:53 +01:00
|
|
|
|
(define edges
|
|
|
|
|
(resolve-dependencies instances))
|
|
|
|
|
|
|
|
|
|
(define (instance->derivation instance)
|
2019-07-08 10:53:41 +02:00
|
|
|
|
(mlet %store-monad ((system (current-system)))
|
|
|
|
|
(mcached (if (eq? instance core-instance)
|
|
|
|
|
(build-channel-instance instance)
|
|
|
|
|
(mlet %store-monad ((core (instance->derivation core-instance))
|
|
|
|
|
(deps (mapm %store-monad instance->derivation
|
|
|
|
|
(edges instance))))
|
|
|
|
|
(build-channel-instance instance core deps)))
|
|
|
|
|
instance
|
|
|
|
|
system)))
|
2019-01-17 16:57:53 +01:00
|
|
|
|
|
2019-01-20 18:11:11 +01:00
|
|
|
|
(unless core-instance
|
|
|
|
|
(let ((loc (and=> (any (compose channel-location channel-instance-channel)
|
|
|
|
|
instances)
|
|
|
|
|
source-properties->location)))
|
|
|
|
|
(raise (apply make-compound-condition
|
|
|
|
|
(condition
|
|
|
|
|
(&message (message "'guix' channel is lacking")))
|
2020-01-09 11:21:31 +01:00
|
|
|
|
(condition
|
|
|
|
|
(&fix-hint (hint (G_ "Make sure your list of channels
|
|
|
|
|
contains one channel named @code{guix} providing the core of Guix."))))
|
2019-01-20 18:11:11 +01:00
|
|
|
|
(if loc
|
|
|
|
|
(list (condition (&error-location (location loc))))
|
|
|
|
|
'())))))
|
|
|
|
|
|
2019-01-17 16:57:53 +01:00
|
|
|
|
(mapm %store-monad instance->derivation instances))
|
2018-08-27 18:05:49 +02:00
|
|
|
|
|
|
|
|
|
(define (whole-package-for-legacy name modules)
|
|
|
|
|
"Return a full-blown Guix package for MODULES, a derivation that builds Guix
|
|
|
|
|
modules in the old ~/.config/guix/latest style."
|
|
|
|
|
(define packages
|
|
|
|
|
(resolve-interface '(gnu packages guile)))
|
|
|
|
|
|
2019-01-07 22:57:34 +01:00
|
|
|
|
(define modules+compiled
|
|
|
|
|
;; Since MODULES contains both .scm and .go files at its root, re-bundle
|
|
|
|
|
;; it so that it has share/guile/site and lib/guile, which is what
|
|
|
|
|
;; 'whole-package' expects.
|
|
|
|
|
(computed-file (derivation-name modules)
|
|
|
|
|
(with-imported-modules '((guix build utils))
|
|
|
|
|
#~(begin
|
|
|
|
|
(use-modules (guix build utils))
|
|
|
|
|
|
|
|
|
|
(define version
|
|
|
|
|
(effective-version))
|
|
|
|
|
(define share
|
|
|
|
|
(string-append #$output "/share/guile/site"))
|
|
|
|
|
(define lib
|
|
|
|
|
(string-append #$output "/lib/guile/" version))
|
|
|
|
|
|
|
|
|
|
(mkdir-p share) (mkdir-p lib)
|
|
|
|
|
(symlink #$modules (string-append share "/" version))
|
|
|
|
|
(symlink #$modules (string-append lib "/site-ccache"))))))
|
|
|
|
|
|
2018-08-27 18:05:49 +02:00
|
|
|
|
(letrec-syntax ((list (syntax-rules (->)
|
|
|
|
|
((_)
|
|
|
|
|
'())
|
|
|
|
|
((_ (module -> variable) rest ...)
|
|
|
|
|
(cons (module-ref (resolve-interface
|
|
|
|
|
'(gnu packages module))
|
|
|
|
|
'variable)
|
|
|
|
|
(list rest ...)))
|
|
|
|
|
((_ variable rest ...)
|
|
|
|
|
(cons (module-ref packages 'variable)
|
|
|
|
|
(list rest ...))))))
|
2019-01-07 22:57:34 +01:00
|
|
|
|
(whole-package name modules+compiled
|
2018-08-27 18:05:49 +02:00
|
|
|
|
|
|
|
|
|
;; In the "old style", %SELF-BUILD-FILE would simply return a
|
|
|
|
|
;; derivation that builds modules. We have to infer what the
|
|
|
|
|
;; dependencies of these modules were.
|
2019-10-27 23:00:39 +01:00
|
|
|
|
(list guile-json-3 guile-git guile-bytestructures
|
2018-08-27 18:05:49 +02:00
|
|
|
|
(ssh -> guile-ssh) (tls -> gnutls)))))
|
|
|
|
|
|
|
|
|
|
(define (old-style-guix? drv)
|
|
|
|
|
"Return true if DRV corresponds to a ~/.config/guix/latest style of
|
|
|
|
|
derivation."
|
|
|
|
|
;; Here we rely on a gross historical fact: that derivations produced by the
|
|
|
|
|
;; "old style" (before commit 8a0d9bc8a3f153159d9e239a151c0fa98f1e12d8,
|
|
|
|
|
;; dated May 30, 2018) did not depend on "guix-command.drv".
|
|
|
|
|
(not (find (lambda (input)
|
2019-07-10 18:51:56 +02:00
|
|
|
|
(string=? "guix-command"
|
|
|
|
|
(derivation-name
|
|
|
|
|
(derivation-input-derivation input))))
|
2018-08-27 18:05:49 +02:00
|
|
|
|
(derivation-inputs drv))))
|
|
|
|
|
|
|
|
|
|
(define (channel-instances->manifest instances)
|
|
|
|
|
"Return a profile manifest with entries for all of INSTANCES, a list of
|
|
|
|
|
channel instances."
|
2019-07-06 15:54:45 +02:00
|
|
|
|
(define (instance->entry instance drv)
|
|
|
|
|
(let ((commit (channel-instance-commit instance))
|
|
|
|
|
(channel (channel-instance-channel instance)))
|
|
|
|
|
(manifest-entry
|
|
|
|
|
(name (symbol->string (channel-name channel)))
|
|
|
|
|
(version (string-take commit 7))
|
|
|
|
|
(item (if (guix-channel? channel)
|
|
|
|
|
(if (old-style-guix? drv)
|
|
|
|
|
(whole-package-for-legacy (string-append name "-" version)
|
|
|
|
|
drv)
|
|
|
|
|
drv)
|
|
|
|
|
drv))
|
|
|
|
|
(properties
|
|
|
|
|
`((source (repository
|
|
|
|
|
(version 0)
|
|
|
|
|
(url ,(channel-url channel))
|
|
|
|
|
(branch ,(channel-branch channel))
|
|
|
|
|
(commit ,commit))))))))
|
2018-08-27 18:05:49 +02:00
|
|
|
|
|
|
|
|
|
(mlet* %store-monad ((derivations (channel-instance-derivations instances))
|
2019-07-06 15:54:45 +02:00
|
|
|
|
(entries -> (map instance->entry instances derivations)))
|
2018-08-27 18:05:49 +02:00
|
|
|
|
(return (manifest entries))))
|
2018-09-05 10:35:43 +02:00
|
|
|
|
|
2019-01-11 17:23:39 +01:00
|
|
|
|
(define (package-cache-file manifest)
|
|
|
|
|
"Build a package cache file for the instance in MANIFEST. This is meant to
|
|
|
|
|
be used as a profile hook."
|
2020-04-26 22:12:32 +02:00
|
|
|
|
(let ((profile (profile (content manifest) (hooks '()))))
|
2019-01-11 17:23:39 +01:00
|
|
|
|
(define build
|
|
|
|
|
#~(begin
|
|
|
|
|
(use-modules (gnu packages))
|
|
|
|
|
|
|
|
|
|
(if (defined? 'generate-package-cache)
|
|
|
|
|
(begin
|
|
|
|
|
;; Delegate package cache generation to the inferior.
|
|
|
|
|
(format (current-error-port)
|
|
|
|
|
"Generating package cache for '~a'...~%"
|
|
|
|
|
#$profile)
|
|
|
|
|
(generate-package-cache #$output))
|
|
|
|
|
(mkdir #$output))))
|
|
|
|
|
|
|
|
|
|
(gexp->derivation-in-inferior "guix-package-cache" build
|
|
|
|
|
profile
|
2019-03-08 12:25:25 +01:00
|
|
|
|
|
|
|
|
|
;; If the Guix in PROFILE is too old and
|
|
|
|
|
;; lacks 'guix repl', don't build the cache
|
|
|
|
|
;; instead of failing.
|
|
|
|
|
#:silent-failure? #t
|
|
|
|
|
|
2019-01-11 17:23:39 +01:00
|
|
|
|
#:properties '((type . profile-hook)
|
2019-01-29 11:58:50 +01:00
|
|
|
|
(hook . package-cache))
|
|
|
|
|
#:local-build? #t)))
|
2019-01-11 17:23:39 +01:00
|
|
|
|
|
|
|
|
|
(define %channel-profile-hooks
|
|
|
|
|
;; The default channel profile hooks.
|
|
|
|
|
(cons package-cache-file %default-profile-hooks))
|
|
|
|
|
|
2018-09-18 12:08:05 +02:00
|
|
|
|
(define (channel-instances->derivation instances)
|
|
|
|
|
"Return the derivation of the profile containing INSTANCES, a list of
|
|
|
|
|
channel instances."
|
|
|
|
|
(mlet %store-monad ((manifest (channel-instances->manifest instances)))
|
2019-01-11 17:23:39 +01:00
|
|
|
|
(profile-derivation manifest
|
|
|
|
|
#:hooks %channel-profile-hooks)))
|
2018-09-18 12:08:05 +02:00
|
|
|
|
|
2018-09-05 10:35:43 +02:00
|
|
|
|
(define latest-channel-instances*
|
|
|
|
|
(store-lift latest-channel-instances))
|
|
|
|
|
|
2020-05-20 22:15:54 +02:00
|
|
|
|
(define* (latest-channel-derivation #:optional (channels %default-channels)
|
|
|
|
|
#:key
|
|
|
|
|
(current-channels '())
|
|
|
|
|
(validate-pull
|
|
|
|
|
ensure-forward-channel-update))
|
2018-09-05 10:35:43 +02:00
|
|
|
|
"Return as a monadic value the derivation that builds the profile for the
|
2020-05-20 22:15:54 +02:00
|
|
|
|
latest instances of CHANNELS. CURRENT-CHANNELS and VALIDATE-PULL are passed
|
|
|
|
|
to 'latest-channel-instances'."
|
|
|
|
|
(mlet %store-monad ((instances
|
|
|
|
|
(latest-channel-instances* channels
|
|
|
|
|
#:current-channels
|
|
|
|
|
current-channels
|
|
|
|
|
#:validate-pull
|
|
|
|
|
validate-pull)))
|
2018-09-18 12:08:05 +02:00
|
|
|
|
(channel-instances->derivation instances)))
|
2019-08-16 14:57:06 +02:00
|
|
|
|
|
|
|
|
|
(define (profile-channels profile)
|
|
|
|
|
"Return the list of channels corresponding to entries in PROFILE. If
|
|
|
|
|
PROFILE is not a profile created by 'guix pull', return the empty list."
|
|
|
|
|
(filter-map (lambda (entry)
|
|
|
|
|
(match (assq 'source (manifest-entry-properties entry))
|
|
|
|
|
(('source ('repository ('version 0)
|
|
|
|
|
('url url)
|
|
|
|
|
('branch branch)
|
|
|
|
|
('commit commit)
|
|
|
|
|
_ ...))
|
|
|
|
|
(channel (name (string->symbol
|
|
|
|
|
(manifest-entry-name entry)))
|
|
|
|
|
(url url)
|
|
|
|
|
(commit commit)))
|
|
|
|
|
|
|
|
|
|
;; No channel information for this manifest entry.
|
|
|
|
|
;; XXX: Pre-0.15.0 Guix did not provide that information,
|
|
|
|
|
;; but there's not much we can do in that case.
|
|
|
|
|
(_ #f)))
|
|
|
|
|
|
|
|
|
|
;; Show most recently installed packages last.
|
|
|
|
|
(reverse
|
|
|
|
|
(manifest-entries (profile-manifest profile)))))
|
2019-09-14 23:16:54 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;;
|
|
|
|
|
;;; News.
|
|
|
|
|
;;;
|
|
|
|
|
|
|
|
|
|
;; Channel news.
|
|
|
|
|
(define-record-type <channel-news>
|
|
|
|
|
(channel-news entries)
|
|
|
|
|
channel-news?
|
|
|
|
|
(entries channel-news-entries)) ;list of <channel-news-entry>
|
|
|
|
|
|
|
|
|
|
;; News entry, associated with a specific commit of the channel.
|
|
|
|
|
(define-record-type <channel-news-entry>
|
2019-09-21 21:29:30 +02:00
|
|
|
|
(channel-news-entry commit tag title body)
|
2019-09-14 23:16:54 +02:00
|
|
|
|
channel-news-entry?
|
2019-09-21 21:29:30 +02:00
|
|
|
|
(commit channel-news-entry-commit) ;hex string | #f
|
|
|
|
|
(tag channel-news-entry-tag) ;#f | string
|
2019-09-14 23:16:54 +02:00
|
|
|
|
(title channel-news-entry-title) ;list of language tag/string pairs
|
|
|
|
|
(body channel-news-entry-body)) ;list of language tag/string pairs
|
|
|
|
|
|
|
|
|
|
(define (sexp->channel-news-entry entry)
|
|
|
|
|
"Return the <channel-news-entry> record corresponding to ENTRY, an sexp."
|
|
|
|
|
(define (pair language message)
|
|
|
|
|
(cons (symbol->string language) message))
|
|
|
|
|
|
|
|
|
|
(match entry
|
2019-09-21 21:29:30 +02:00
|
|
|
|
(('entry ((and (or 'commit 'tag) type) commit-or-tag)
|
2019-09-14 23:16:54 +02:00
|
|
|
|
('title ((? symbol? title-tags) (? string? titles)) ...)
|
|
|
|
|
('body ((? symbol? body-tags) (? string? bodies)) ...)
|
|
|
|
|
_ ...)
|
2019-09-21 21:29:30 +02:00
|
|
|
|
(channel-news-entry (and (eq? type 'commit) commit-or-tag)
|
|
|
|
|
(and (eq? type 'tag) commit-or-tag)
|
2019-09-14 23:16:54 +02:00
|
|
|
|
(map pair title-tags titles)
|
|
|
|
|
(map pair body-tags bodies)))
|
|
|
|
|
(_
|
|
|
|
|
(raise (condition
|
|
|
|
|
(&message (message "invalid channel news entry"))
|
|
|
|
|
(&error-location
|
|
|
|
|
(location (source-properties->location
|
|
|
|
|
(source-properties entry)))))))))
|
|
|
|
|
|
|
|
|
|
(define (read-channel-news port)
|
|
|
|
|
"Read a channel news feed from PORT and return it as a <channel-news>
|
|
|
|
|
record."
|
|
|
|
|
(match (false-if-exception (read port))
|
|
|
|
|
(('channel-news ('version 0) entries ...)
|
|
|
|
|
(channel-news (map sexp->channel-news-entry entries)))
|
|
|
|
|
(('channel-news ('version version) _ ...)
|
|
|
|
|
;; This is an unsupported version from the future. There's nothing wrong
|
|
|
|
|
;; with that (the user may simply need to upgrade the 'guix' channel to
|
|
|
|
|
;; be able to read it), so silently ignore it.
|
|
|
|
|
(channel-news '()))
|
|
|
|
|
(#f
|
|
|
|
|
(raise (condition
|
|
|
|
|
(&message (message "syntactically invalid channel news file")))))
|
|
|
|
|
(sexp
|
|
|
|
|
(raise (condition
|
|
|
|
|
(&message (message "invalid channel news file"))
|
|
|
|
|
(&error-location
|
|
|
|
|
(location (source-properties->location
|
|
|
|
|
(source-properties sexp)))))))))
|
|
|
|
|
|
2019-09-21 21:29:30 +02:00
|
|
|
|
(define (resolve-channel-news-entry-tag repository entry)
|
|
|
|
|
"If ENTRY has its 'commit' field set, return ENTRY. Otherwise, lookup
|
|
|
|
|
ENTRY's 'tag' in REPOSITORY and return ENTRY with its 'commit' field set to
|
|
|
|
|
the field its 'tag' refers to. A 'git-error' exception is raised if the tag
|
|
|
|
|
cannot be found."
|
|
|
|
|
(if (channel-news-entry-commit entry)
|
|
|
|
|
entry
|
|
|
|
|
(let* ((tag (channel-news-entry-tag entry))
|
|
|
|
|
(reference (string-append "refs/tags/" tag))
|
|
|
|
|
(oid (reference-name->oid repository reference)))
|
|
|
|
|
(channel-news-entry (oid->string oid) tag
|
|
|
|
|
(channel-news-entry-title entry)
|
|
|
|
|
(channel-news-entry-body entry)))))
|
|
|
|
|
|
2019-09-14 23:16:54 +02:00
|
|
|
|
(define* (channel-news-for-commit channel new #:optional old)
|
|
|
|
|
"Return a list of <channel-news-entry> for CHANNEL between commits OLD and
|
|
|
|
|
NEW. When OLD is omitted or is #f, return all the news entries of CHANNEL."
|
|
|
|
|
(catch 'git-error
|
|
|
|
|
(lambda ()
|
|
|
|
|
(let* ((checkout (update-cached-checkout (channel-url channel)
|
|
|
|
|
#:ref `(commit . ,new)))
|
|
|
|
|
(metadata (read-channel-metadata-from-source checkout))
|
|
|
|
|
(news-file (channel-metadata-news-file metadata))
|
|
|
|
|
(news-file (and news-file
|
|
|
|
|
(string-append checkout "/" news-file))))
|
|
|
|
|
(if (and news-file (file-exists? news-file))
|
2019-09-21 21:29:30 +02:00
|
|
|
|
(with-repository checkout repository
|
|
|
|
|
(let* ((news (call-with-input-file news-file
|
|
|
|
|
read-channel-news))
|
|
|
|
|
(entries (map (lambda (entry)
|
|
|
|
|
(resolve-channel-news-entry-tag repository
|
|
|
|
|
entry))
|
|
|
|
|
(channel-news-entries news))))
|
|
|
|
|
(if old
|
2019-09-14 23:16:54 +02:00
|
|
|
|
(let* ((new (commit-lookup repository (string->oid new)))
|
|
|
|
|
(old (commit-lookup repository (string->oid old)))
|
|
|
|
|
(commits (list->set
|
|
|
|
|
(map (compose oid->string commit-id)
|
|
|
|
|
(commit-difference new old)))))
|
|
|
|
|
(filter (lambda (entry)
|
|
|
|
|
(set-contains? commits
|
|
|
|
|
(channel-news-entry-commit entry)))
|
2019-09-21 21:29:30 +02:00
|
|
|
|
entries))
|
|
|
|
|
entries)))
|
2019-09-14 23:16:54 +02:00
|
|
|
|
'())))
|
|
|
|
|
(lambda (key error . rest)
|
|
|
|
|
;; If commit NEW or commit OLD cannot be found, then something must be
|
|
|
|
|
;; wrong (for example, the history of CHANNEL was rewritten and these
|
|
|
|
|
;; commits no longer exist upstream), so quietly return the empty list.
|
|
|
|
|
(if (= GIT_ENOTFOUND (git-error-code error))
|
|
|
|
|
'()
|
|
|
|
|
(apply throw key error rest)))))
|
2020-05-15 11:53:13 +02:00
|
|
|
|
|
|
|
|
|
;;; Local Variables:
|
|
|
|
|
;;; eval: (put 'with-guile 'scheme-indent-function 1)
|
|
|
|
|
;;; End:
|