aboutsummaryrefslogtreecommitdiff
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2017, 2018 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com>
;;;
;;; 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/>.

;;; Commentary:
;;;
;;; This scripts updates the definition of the 'guix' package in Guix for the
;;; current commit.  It requires Git to be installed.
;;;
;;; Code:

(use-modules (guix)
             (guix ui)
             (guix git-download)
             (guix upstream)
             (guix utils)
             (guix base32)
             (guix build utils)
             (guix scripts hash)
             (gnu packages package-management)
             (ice-9 match)
             (ice-9 popen)
             (ice-9 regex)
             (ice-9 textual-ports)
             (srfi srfi-1)
             (srfi srfi-2)
             (srfi srfi-26))

(define %top-srcdir
  (string-append (current-source-directory) "/.."))

(define (package-definition-location)
  "Return the source properties of the definition of the 'guix' package."
  (call-with-input-file (location-file (package-location guix))
    (lambda (port)
      (let loop ()
        (match (read port)
          ((? eof-object?)
           (error "definition of 'guix' package could not be found"
                  (port-filename port)))
          (('define-public 'guix value)
           (source-properties value))
          (_
           (loop)))))))

(define* (update-definition commit hash
                            #:key version old-hash)
  "Return a one-argument procedure that takes a string, the definition of the
'guix' package, and returns a string, the update definition for VERSION,
COMMIT."
  (define (linear-offset str line column)
    ;; Return the offset in characters to reach LINE and COLUMN (both
    ;; zero-indexed) in STR.
    (call-with-input-string str
      (lambda (port)
        (let loop ((offset 0))
          (cond ((and (= (port-column port) column)
                      (= (port-line port) line))
                 offset)
                ((eof-object? (read-char port))
                 (error "line and column not reached!"
                        str))
                (else
                 (loop (+ 1 offset))))))))

  (define (update-hash str)
    ;; Replace OLD-HASH with HASH in STR.
    (string-replace-substring str
                              (bytevector->nix-base32-string old-hash)
                              (bytevector->nix-base32-string hash)))

  (lambda (str)
    (match (call-with-input-string str read)
      (('let (('version old-version)
              ('commit old-commit)
              ('revision old-revision))
         defn)
       (let* ((location (source-properties defn))
              (line     (assq-ref location 'line))
              (column   0)
              (offset   (linear-offset str line column)))
         (string-append (format #f "(let ((version \"~a\")
        (commit \"~a\")
        (revision ~a))\n"
                                (or version old-version)
                                commit
                                (if (and version
                                         (not (string=? version old-version)))
                                    0
                                    (+ 1 old-revision)))
                        (string-drop (update-hash str) offset))))
      (exp
       (error "'guix' package definition is not as expected" exp)))))

(define (git-add-worktree directory commit)
  "Create a new git worktree at DIRECTORY, detached on commit COMMIT."
  (invoke "git" "worktree" "add" "--detach" directory commit))

(define (call-with-temporary-git-worktree commit proc)
  "Execute PROC in the context of a temporary git worktree created from
COMMIT.  PROC receives the temporary directory file name as an argument."
  (call-with-temporary-directory
   (lambda (tmp-directory)
     (dynamic-wind
       (lambda ()
         #t)
       (lambda ()
         (git-add-worktree tmp-directory commit)
         (proc tmp-directory))
       (lambda ()
         (invoke "git" "worktree" "remove" "--force" tmp-directory))))))

(define %savannah-guix-git-repo-push-url-regexp
  "git.(savannah|sv).gnu.org:?/srv/git/guix.git \\(push\\)")

(define-syntax-rule (with-input-pipe-to-string prog arg ...)
  (let* ((input-pipe (open-pipe* OPEN_READ prog arg ...))
	 (output (get-string-all input-pipe))
	 (exit-val (status:exit-val (close-pipe input-pipe))))
    (unless (zero? exit-val)
      (error (format #f "Command ~s exited with non-zero exit status: ~s"
                     (string-join (list prog arg ...)) exit-val)))
    (string-trim-both output)))

(define (find-origin-remote)
  "Find the name of the git remote with the Savannah Guix git repo URL."
  (and-let* ((remotes (string-split (with-input-pipe-to-string
                                     "git" "remote" "-v")
                                    #\newline))
             (origin-entry (find (cut string-match
                                      %savannah-guix-git-repo-push-url-regexp
                                      <>)
                                 remotes)))
    (first (string-split origin-entry #\tab))))

(define (commit-already-pushed? remote commit)
  "True if COMMIT is found in the REMOTE repository."
  (not (string-null? (with-input-pipe-to-string
                      "git" "branch" "-r" "--contains" commit
                      (string-append remote "/master")))))

(define (keep-source-in-store store source)
  "Add SOURCE to the store under the name that the 'guix' package expects."

  ;; Add SOURCE to the store, but this time under the real name used in the
  ;; 'origin'.  This allows us to build the package without having to make a
  ;; real checkout; thus, it also works when working on a private branch.
  (reload-module
   (resolve-module '(gnu packages package-management)))

  (let* ((source (add-to-store store
                               (origin-file-name (package-source guix))
                               #t "sha256" source
                               #:select? (git-predicate source)))
         (root   (store-path-package-name source)))

    ;; Add an indirect GC root for SOURCE in the current directory.
    (false-if-exception (delete-file root))
    (symlink source root)
    (add-indirect-root store
                       (string-append (getcwd) "/" root))

    (info (G_ "source code kept in ~a (GC root: ~a)~%")
          source root)))


(define (main . args)
  (match args
    ((commit version)
     (with-directory-excursion %top-srcdir
       (or (getenv "GUIX_ALLOW_ME_TO_USE_PRIVATE_COMMIT")
           (let ((remote (find-origin-remote)))
             (unless remote
               (leave (G_ "Failed to find the origin git remote.~%")))
             (commit-already-pushed? remote commit))
           (leave (G_ "Commit ~a is not pushed upstream.  Aborting.~%") commit))
       (call-with-temporary-git-worktree commit
           (lambda (tmp-directory)
             (let* ((hash (nix-base32-string->bytevector
                           (string-trim-both
                            (with-output-to-string
		              (lambda ()
		                (guix-hash "-rx" tmp-directory))))))
                    (location (package-definition-location))
                    (old-hash (content-hash-value
                               (origin-hash (package-source guix)))))
               (edit-expression location
                                (update-definition commit hash
                                                   #:old-hash old-hash
                                                   #:version version))
               ;; When GUIX_ALLOW_ME_TO_USE_PRIVATE_COMMIT is set, the sources are
               ;; added to the store.  This is used as part of 'make release'.
               (when (getenv "GUIX_ALLOW_ME_TO_USE_PRIVATE_COMMIT")
                 (with-store store
                   (keep-source-in-store store tmp-directory))))))))
    ((commit)
     ;; Automatically deduce the version and revision numbers.
     (main commit #f))))

(apply main (cdr (command-line)))
rror message. Tobias Geerinckx-Rice 2021-11-25daemon: Read substitute nar size as 'unsigned long long'....Fixes <https://issues.guix.gnu.org/46212>. Reported by Christopher Baines <mail@cbaines.net>. Previously, the nar size returned by 'guix substitute' would be read as an 'int'; thus, values above 2^31 - 1 would be read and then stored as negative integers in the database. Regression introduced in 9dfa20a22ae0be3d3b01a7b3d422af97428c627e. * nix/libstore/build.cc (SubstitutionGoal::finished): Use templatized 'string2Int' instead of 'std::atoi' to get an 'unsigned long long', which is the type of 'hash.second'. * tests/store.scm ("substitute and large size"): New test. Ludovic Courtès 2021-11-25daemon: Read unsigned nar size and download size from substituter....Fixes <https://issues.guix.gnu.org/51983>. Reported by Tobias Geerinckx-Rice <me@tobias.gr>. * nix/libstore/local-store.cc (LocalStore::querySubstitutablePathInfos): Expect 'unsigned long long' for 'downloadSize' and 'narSize'. * tests/store.scm ("substitute query and large size"): New test. Ludovic Courtès 2021-11-19daemon: Micro-optimize 'deletePath'....'remove' calls 'unlink' first and falls back to 'rmdir' upon EISDIR. This change gets rid of the 'unlink' call for every directory being removed. * nix/libutil/util.cc (_deletePath): Call 'unlink' or 'rmdir' depending on 'st.st_mode', rather than call 'remove'. Ludovic Courtès 2021-11-16daemon: Do not deduplicate files smaller than 8 KiB....Files smaller than 8 KiB typically represent ~70% of the entries in /gnu/store/.links but only contribute to ~4% of the space savings afforded by deduplication. Not considering these files for deduplication speeds up file insertion in the store and, more importantly, leaves 'removeUnusedLinks' with fewer entries to traverse, thereby speeding it up proportionally. Partly fixes <https://issues.guix.gnu.org/24937>. * config-daemon.ac: Remove symlink hard link check and CAN_LINK_SYMLINK definition. * guix/store/deduplication.scm (%deduplication-minimum-size): New variable. (deduplicate)[loop]: Do not recurse when FILE's size is below %DEDUPLICATION-MINIMUM-SIZE. (dump-port): New procedure. (dump-file/deduplicate)[hash]: Turn into... [dump-and-compute-hash]: ... this thunk. Call 'deduplicate' only when SIZE is greater than %DEDUPLICATION-MINIMUM-SIZE; otherwise call 'dump-port'. * nix/libstore/gc.cc (LocalStore::removeUnusedLinks): Drop files where st.st_size < deduplicationMinSize. * nix/libstore/local-store.hh (deduplicationMinSize): New declaration. * nix/libstore/optimise-store.cc (deduplicationMinSize): New variable. (LocalStore::optimisePath_): Return when PATH is a symlink or smaller than 'deduplicationMinSize'. * tests/derivations.scm ("identical files are deduplicated"): Produce files bigger than %DEDUPLICATION-MINIMUM-SIZE. * tests/nar.scm ("restore-file-set with directories (signed, valid)"): Likewise. * tests/store-deduplication.scm ("deduplicate, below %deduplication-minimum-size"): New test. ("deduplicate", "deduplicate, ENOSPC"): Produce files bigger than %DEDUPLICATION-MINIMUM-SIZE. * tests/store.scm ("substitute, deduplication"): Likewise. Ludovic Courtès 2021-10-01build: Don’t delete ‘guix-gc.timer’ file....Without this invoking ‘make clean’ would remove ‘guix-gc.timer’, and ‘make’ would fail with. make[2]: *** No rule to make target 'etc/guix-gc.timer', needed by 'all-am'. Stop. * nix/local.mk (nodist_systemdservice_DATA): Remove ‘guix-gc.timer’. Xinglu Chen 2021-09-20etc: Add systemd files for running ‘guix gc’ periodically...* etc/guix-gc.service.in: New file. * etc/guix-gc.timer: Likewise. * .gitignore: Ignore generated ‘guix-gc.service’. * nix/local.mk (nodist_systemdservice_DATA): Add ‘guix-gc.service’ and ‘guix-gc.timer’. (EXTRA_DIST): Add ‘guix-gc.service.in’ and ‘guix-gc.timer’. * doc/guix.texi (Binary Installation): Mention the new systemd files. Signed-off-by: Mathieu Othacehe <othacehe@gnu.org> Thiago Jung Bauermann 2021-04-09daemon: 'guix substitute' replies on FD 4....This avoids the situation where error messages would unintentionally go to stderr and be wrongfully interpreted as a reply by the daemon. Fixes <https://bugs.gnu.org/46362>. This is a followup to ee3226e9d54891c7e696912245e4904435be191c. * guix/scripts/substitute.scm (display-narinfo-data): Add 'port' parameter and honor it. (process-query): Likewise. (process-substitution): Likewise. (%error-to-file-descriptor-4?, with-redirected-error-port): Remove. (%reply-file-descriptor): New variable. (guix-substitute): Remove use of 'with-redirected-error-port'. Define 'reply-port' and pass it to 'process-query' and 'process-substitution'. * nix/libstore/build.cc (SubstitutionGoal::handleChildOutput): Swap 'builderOut' and 'fromAgent'. * nix/libstore/local-store.cc (LocalStore::getLineFromSubstituter): Likewise. * tests/substitute.scm <top level>: Set '%reply-file-descriptor' rather than '%error-to-file-descriptor-4?'. Ludovic Courtès 2021-04-03daemon: Remove dead code....Reported by Noisytoot on #guix. * nix/nix-daemon/shared.hh (showManPage): Remove. * nix/nix-daemon/nix-daemon.cc (printHelp, programId): Remove. Ludovic Courtès 2021-03-30gnu: guix: Fix openrc init scripts....Fixes <https://bugs.gnu.org/46871>. * gnu/packages/package-management.scm (guix)[arguments]: Fix openrc init. * nix/local.mk (openrcservicedir): Likewise. Signed-off-by: Efraim Flashner <efraim@flashner.co.il> zimoun 2021-03-18daemon: Prevent privilege escalation with '--keep-failed' [security]....Fixes <https://bugs.gnu.org/47229>. Reported by Nathan Nye of WhiteBeam Security. * nix/libstore/build.cc (DerivationGoal::startBuilder): When 'useChroot' is true, add "/top" to 'tmpDir'. (DerivationGoal::deleteTmpDir): Adjust accordingly. When 'settings.keepFailed' is true, chown in two steps: first the "/top" sub-directory, and then rename "/top" to its parent. Ludovic Courtès 2021-03-17daemon: Correctly handle '--discover' with no value....Previously, we'd get: $ guix-daemon --discover error: basic_string::_M_construct null not valid * nix/nix-daemon/guix-daemon.cc (parse_opt): Change second argument to 'settings.set' to properly handle case where ARG is NULL. Ludovic Courtès