mirror of
https://git.in.rschanz.org/ryan77627/guix.git
synced 2025-01-25 20:19:18 -05:00
channels: 'latest-channel-instances' guards against non-forward updates.
* guix/channels.scm (latest-channel-instance): Add #:starting-commit and pass it to 'update-cached-checkout'. Return the commit relation as a second value. (ensure-forward-channel-update): New procedure. (latest-channel-instances): Add #:current-channels and #:validate-pull. [current-commit]: New procedure. Pass #:starting-commit to 'latest-channel-instance'. When the returned relation is true, call VALIDATE-PULL. (latest-channel-derivation): Add #:current-channels and #:validate-pull. Pass them to 'latest-channel-instances*'. * tests/channels.scm ("latest-channel-instances #:validate-pull"): New test.
This commit is contained in:
parent
8d1d56578a
commit
872898f768
2 changed files with 114 additions and 10 deletions
|
@ -73,6 +73,7 @@ (define-module (guix channels)
|
||||||
channel-instances->manifest
|
channel-instances->manifest
|
||||||
%channel-profile-hooks
|
%channel-profile-hooks
|
||||||
channel-instances->derivation
|
channel-instances->derivation
|
||||||
|
ensure-forward-channel-update
|
||||||
|
|
||||||
profile-channels
|
profile-channels
|
||||||
|
|
||||||
|
@ -212,15 +213,18 @@ (define (apply-patches checkout commit patches)
|
||||||
(loop rest)))))
|
(loop rest)))))
|
||||||
|
|
||||||
(define* (latest-channel-instance store channel
|
(define* (latest-channel-instance store channel
|
||||||
#:key (patches %patches))
|
#:key (patches %patches)
|
||||||
"Return the latest channel instance for CHANNEL."
|
starting-commit)
|
||||||
|
"Return two values: the latest channel instance for CHANNEL, and its
|
||||||
|
relation to STARTING-COMMIT when provided."
|
||||||
(define (dot-git? file stat)
|
(define (dot-git? file stat)
|
||||||
(and (string=? (basename file) ".git")
|
(and (string=? (basename file) ".git")
|
||||||
(eq? 'directory (stat:type stat))))
|
(eq? 'directory (stat:type stat))))
|
||||||
|
|
||||||
(let-values (((checkout commit relation)
|
(let-values (((checkout commit relation)
|
||||||
(update-cached-checkout (channel-url channel)
|
(update-cached-checkout (channel-url channel)
|
||||||
#:ref (channel-reference channel))))
|
#:ref (channel-reference channel)
|
||||||
|
#:starting-commit starting-commit)))
|
||||||
(when (guix-channel? channel)
|
(when (guix-channel? channel)
|
||||||
;; Apply the relevant subset of PATCHES directly in CHECKOUT. This is
|
;; Apply the relevant subset of PATCHES directly in CHECKOUT. This is
|
||||||
;; safe to do because 'switch-to-ref' eventually does a hard reset.
|
;; safe to do because 'switch-to-ref' eventually does a hard reset.
|
||||||
|
@ -229,11 +233,51 @@ (define (dot-git? file stat)
|
||||||
(let* ((name (url+commit->name (channel-url channel) commit))
|
(let* ((name (url+commit->name (channel-url channel) commit))
|
||||||
(checkout (add-to-store store name #t "sha256" checkout
|
(checkout (add-to-store store name #t "sha256" checkout
|
||||||
#:select? (negate dot-git?))))
|
#:select? (negate dot-git?))))
|
||||||
(channel-instance channel commit checkout))))
|
(values (channel-instance channel commit checkout)
|
||||||
|
relation))))
|
||||||
|
|
||||||
(define* (latest-channel-instances store channels)
|
(define (ensure-forward-channel-update channel start instance relation)
|
||||||
|
"Raise an error if RELATION is not 'ancestor, meaning that START is not an
|
||||||
|
ancestor of the commit in INSTANCE, unless CHANNEL specifies a commit.
|
||||||
|
|
||||||
|
This procedure implements a channel update policy meant to be used as a
|
||||||
|
#:validate-pull argument."
|
||||||
|
(match relation
|
||||||
|
('ancestor #t)
|
||||||
|
('self #t)
|
||||||
|
(_
|
||||||
|
(raise (apply make-compound-condition
|
||||||
|
(condition
|
||||||
|
(&message (message
|
||||||
|
(format #f (G_ "\
|
||||||
|
aborting update of channel '~a' to commit ~a, which is not a descendant of ~a")
|
||||||
|
(channel-name channel)
|
||||||
|
(channel-instance-commit instance)
|
||||||
|
start))))
|
||||||
|
|
||||||
|
;; Don't show the hint when the user explicitly specified a
|
||||||
|
;; commit in CHANNEL.
|
||||||
|
(if (channel-commit channel)
|
||||||
|
'()
|
||||||
|
(list (condition
|
||||||
|
(&fix-hint
|
||||||
|
(hint (G_ "This could indicate that the channel has
|
||||||
|
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
|
||||||
|
allow non-forward updates.")))))))))))
|
||||||
|
|
||||||
|
(define* (latest-channel-instances store channels
|
||||||
|
#:key
|
||||||
|
(current-channels '())
|
||||||
|
(validate-pull
|
||||||
|
ensure-forward-channel-update))
|
||||||
"Return a list of channel instances corresponding to the latest checkouts of
|
"Return a list of channel instances corresponding to the latest checkouts of
|
||||||
CHANNELS and the channels on which they depend."
|
CHANNELS and the channels on which they depend.
|
||||||
|
|
||||||
|
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."
|
||||||
;; Only process channels that are unique, or that are more specific than a
|
;; Only process channels that are unique, or that are more specific than a
|
||||||
;; previous channel specification.
|
;; previous channel specification.
|
||||||
(define (ignore? channel others)
|
(define (ignore? channel others)
|
||||||
|
@ -244,6 +288,13 @@ (define (ignore? channel others)
|
||||||
(not (or (channel-commit a)
|
(not (or (channel-commit a)
|
||||||
(channel-commit b))))))))
|
(channel-commit b))))))))
|
||||||
|
|
||||||
|
(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))
|
||||||
|
|
||||||
(let loop ((channels channels)
|
(let loop ((channels channels)
|
||||||
(previous-channels '()))
|
(previous-channels '()))
|
||||||
;; Accumulate a list of instances. A list of processed channels is also
|
;; Accumulate a list of instances. A list of processed channels is also
|
||||||
|
@ -257,7 +308,15 @@ (define-values (resulting-channels instances)
|
||||||
(G_ "Updating channel '~a' from Git repository at '~a'...~%")
|
(G_ "Updating channel '~a' from Git repository at '~a'...~%")
|
||||||
(channel-name channel)
|
(channel-name channel)
|
||||||
(channel-url channel))
|
(channel-url channel))
|
||||||
(let ((instance (latest-channel-instance store channel)))
|
(let*-values (((current)
|
||||||
|
(current-commit (channel-name channel)))
|
||||||
|
((instance relation)
|
||||||
|
(latest-channel-instance store channel
|
||||||
|
#:starting-commit
|
||||||
|
current)))
|
||||||
|
(when relation
|
||||||
|
(validate-pull channel current instance relation))
|
||||||
|
|
||||||
(let-values (((new-instances new-channels)
|
(let-values (((new-instances new-channels)
|
||||||
(loop (channel-instance-dependencies instance)
|
(loop (channel-instance-dependencies instance)
|
||||||
previous-channels)))
|
previous-channels)))
|
||||||
|
@ -617,10 +676,20 @@ (define (channel-instances->derivation instances)
|
||||||
(define latest-channel-instances*
|
(define latest-channel-instances*
|
||||||
(store-lift latest-channel-instances))
|
(store-lift latest-channel-instances))
|
||||||
|
|
||||||
(define* (latest-channel-derivation #:optional (channels %default-channels))
|
(define* (latest-channel-derivation #:optional (channels %default-channels)
|
||||||
|
#:key
|
||||||
|
(current-channels '())
|
||||||
|
(validate-pull
|
||||||
|
ensure-forward-channel-update))
|
||||||
"Return as a monadic value the derivation that builds the profile for the
|
"Return as a monadic value the derivation that builds the profile for the
|
||||||
latest instances of CHANNELS."
|
latest instances of CHANNELS. CURRENT-CHANNELS and VALIDATE-PULL are passed
|
||||||
(mlet %store-monad ((instances (latest-channel-instances* channels)))
|
to 'latest-channel-instances'."
|
||||||
|
(mlet %store-monad ((instances
|
||||||
|
(latest-channel-instances* channels
|
||||||
|
#:current-channels
|
||||||
|
current-channels
|
||||||
|
#:validate-pull
|
||||||
|
validate-pull)))
|
||||||
(channel-instances->derivation instances)))
|
(channel-instances->derivation instances)))
|
||||||
|
|
||||||
(define (profile-channels profile)
|
(define (profile-channels profile)
|
||||||
|
|
|
@ -37,6 +37,7 @@ (define-module (test-channels)
|
||||||
#:use-module (srfi srfi-34)
|
#:use-module (srfi srfi-34)
|
||||||
#:use-module (srfi srfi-35)
|
#:use-module (srfi srfi-35)
|
||||||
#:use-module (srfi srfi-64)
|
#:use-module (srfi srfi-64)
|
||||||
|
#:use-module (ice-9 control)
|
||||||
#:use-module (ice-9 match))
|
#:use-module (ice-9 match))
|
||||||
|
|
||||||
(test-begin "channels")
|
(test-begin "channels")
|
||||||
|
@ -178,6 +179,40 @@ (define channel-metadata-dependencies
|
||||||
"abc1234")))
|
"abc1234")))
|
||||||
instances)))))))
|
instances)))))))
|
||||||
|
|
||||||
|
(unless (which (git-command)) (test-skip 1))
|
||||||
|
(test-equal "latest-channel-instances #:validate-pull"
|
||||||
|
'descendant
|
||||||
|
|
||||||
|
;; Make sure the #:validate-pull procedure receives the right values.
|
||||||
|
(let/ec return
|
||||||
|
(with-temporary-git-repository directory
|
||||||
|
'((add "a.txt" "A")
|
||||||
|
(commit "first commit")
|
||||||
|
(add "b.scm" "#t")
|
||||||
|
(commit "second commit"))
|
||||||
|
(with-repository directory repository
|
||||||
|
(let* ((commit1 (find-commit repository "first"))
|
||||||
|
(commit2 (find-commit repository "second"))
|
||||||
|
(spec (channel (url (string-append "file://" directory))
|
||||||
|
(name 'foo)))
|
||||||
|
(new (channel (inherit spec)
|
||||||
|
(commit (oid->string (commit-id commit2)))))
|
||||||
|
(old (channel (inherit spec)
|
||||||
|
(commit (oid->string (commit-id commit1))))))
|
||||||
|
(define (validate-pull channel current instance relation)
|
||||||
|
(return (and (eq? channel old)
|
||||||
|
(string=? (oid->string (commit-id commit2))
|
||||||
|
current)
|
||||||
|
(string=? (oid->string (commit-id commit1))
|
||||||
|
(channel-instance-commit instance))
|
||||||
|
relation)))
|
||||||
|
|
||||||
|
(with-store store
|
||||||
|
;; Attempt a downgrade from NEW to OLD.
|
||||||
|
(latest-channel-instances store (list old)
|
||||||
|
#:current-channels (list new)
|
||||||
|
#:validate-pull validate-pull)))))))
|
||||||
|
|
||||||
(test-assert "channel-instances->manifest"
|
(test-assert "channel-instances->manifest"
|
||||||
;; Compute the manifest for a graph of instances and make sure we get a
|
;; Compute the manifest for a graph of instances and make sure we get a
|
||||||
;; derivation graph that mirrors the instance graph. This test also ensures
|
;; derivation graph that mirrors the instance graph. This test also ensures
|
||||||
|
|
Loading…
Reference in a new issue