From a98712785e0b042a290420fd74e5a4a5da4fc68f Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Sun, 5 Jul 2020 23:40:29 +0200 Subject: Add 'guix git authenticate'. * guix/scripts/git.scm, guix/scripts/git/authenticate.scm, tests/guix-git-authenticate.sh: New files. * Makefile.am (MODULES): Add the *.scm files. (SH_TESTS): Add 'tests/guix-git-authenticate.sh'. * doc/guix.texi (Channels)[Specifying Channel Authorizations]: Mention 'guix git authenticate'. (Invoking guix git authenticate): New node. * po/guix/POTFILES.in: Add 'guix/scripts/git.scm' and 'guix/scripts/git/authenticate.scm'. --- guix/scripts/git.scm | 63 ++++++++++++++ guix/scripts/git/authenticate.scm | 179 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 242 insertions(+) create mode 100644 guix/scripts/git.scm create mode 100644 guix/scripts/git/authenticate.scm (limited to 'guix') diff --git a/guix/scripts/git.scm b/guix/scripts/git.scm new file mode 100644 index 0000000000..bc829cbe99 --- /dev/null +++ b/guix/scripts/git.scm @@ -0,0 +1,63 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2020 Ludovic Courtès +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see . + +(define-module (guix scripts git) + #:use-module (ice-9 match) + #:use-module (guix ui) + #:export (guix-git)) + +(define (show-help) + (display (G_ "Usage: guix git COMMAND ARGS... +Operate on Git repositories.\n")) + (newline) + (display (G_ "The valid values for ACTION are:\n")) + (newline) + (display (G_ "\ + authenticate verify commit signatures and authorizations\n")) + (newline) + (display (G_ " + -h, --help display this help and exit")) + (display (G_ " + -V, --version display version information and exit")) + (newline) + (show-bug-report-information)) + +(define %sub-commands '("authenticate")) + +(define (resolve-sub-command name) + (let ((module (resolve-interface + `(guix scripts git ,(string->symbol name)))) + (proc (string->symbol (string-append "guix-git-" name)))) + (module-ref module proc))) + +(define (guix-git . args) + (with-error-handling + (match args + (() + (format (current-error-port) + (G_ "guix git: missing sub-command~%"))) + ((or ("-h") ("--help")) + (show-help) + (exit 0)) + ((or ("-V") ("--version")) + (show-version-and-exit "guix git")) + ((sub-command args ...) + (if (member sub-command %sub-commands) + (apply (resolve-sub-command sub-command) args) + (format (current-error-port) + (G_ "guix git: invalid sub-command~%"))))))) diff --git a/guix/scripts/git/authenticate.scm b/guix/scripts/git/authenticate.scm new file mode 100644 index 0000000000..5f5d423f28 --- /dev/null +++ b/guix/scripts/git/authenticate.scm @@ -0,0 +1,179 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2020 Ludovic Courtès +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see . + +(define-module (guix scripts git authenticate) + #:use-module (git) + #:use-module (guix ui) + #:use-module (guix scripts) + #:use-module (guix git-authenticate) + #:autoload (guix openpgp) (openpgp-format-fingerprint + openpgp-public-key-fingerprint) + #:use-module ((guix channels) #:select (openpgp-fingerprint)) + #:use-module ((guix git) #:select (with-git-error-handling)) + #:use-module (guix progress) + #:use-module (guix base64) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-26) + #:use-module (srfi srfi-37) + #:use-module (ice-9 format) + #:use-module (ice-9 match) + #:export (guix-git-authenticate)) + +;;; Commentary: +;;; +;;; Authenticate a Git checkout by reading '.guix-authorizations' files and +;;; following the "authorizations invariant" also used by (guix channels). +;;; +;;; Code: + +(define %options + ;; Specifications of the command-line options. + (list (option '(#\h "help") #f #f + (lambda args + (show-help) + (exit 0))) + (option '(#\V "version") #f #f + (lambda args + (show-version-and-exit "guix git authenticate"))) + + (option '(#\r "repository") #t #f + (lambda (opt name arg result) + (alist-cons 'directory arg result))) + (option '(#\e "end") #t #f + (lambda (opt name arg result) + (alist-cons 'end-commit (string->oid arg) result))) + (option '(#\k "keyring") #t #f + (lambda (opt name arg result) + (alist-cons 'keyring-reference arg result))) + (option '("cache-key") #t #f + (lambda (opt name arg result) + (alist-cons 'cache-key arg result))) + (option '("historical-authorizations") #t #f + (lambda (opt name arg result) + (alist-cons 'historical-authorizations arg + result))) + (option '("stats") #f #f + (lambda (opt name arg result) + (alist-cons 'show-stats? #t result))))) + +(define %default-options + '((directory . ".") + (keyring-reference . "keyring"))) + +(define (show-stats stats) + "Display STATS, an alist containing commit signing stats as returned by +'authenticate-repository'." + (format #t (G_ "Signing statistics:~%")) + (for-each (match-lambda + ((signer . count) + (format #t " ~a ~10d~%" + (openpgp-format-fingerprint + (openpgp-public-key-fingerprint signer)) + count))) + (sort stats + (match-lambda* + (((_ . count1) (_ . count2)) + (> count1 count2)))))) + +(define (show-help) + (display (G_ "Usage: guix git authenticate COMMIT SIGNER [OPTIONS...] +Authenticate the given Git checkout using COMMIT/SIGNER as its introduction.\n")) + (display (G_ " + -r, --repository=DIRECTORY + open the Git repository at DIRECTORY")) + (display (G_ " + -k, --keyring=REFERENCE + load keyring from REFERENCE, a Git branch")) + (display (G_ " + --stats display commit signing statistics upon completion")) + (display (G_ " + --cache-key=KEY cache authenticated commits under KEY")) + (display (G_ " + --historical-authorizations=FILE + read historical authorizations from FILE")) + (newline) + (display (G_ " + -h, --help display this help and exit")) + (display (G_ " + -V, --version display version information and exit")) + (newline) + (show-bug-report-information)) + + +;;; +;;; Entry point. +;;; + +(define (guix-git-authenticate . args) + (define options + (parse-command-line args %options (list %default-options) + #:build-options? #f)) + + (define (command-line-arguments lst) + (reverse (filter-map (match-lambda + (('argument . arg) arg) + (_ #f)) + lst))) + + (define commit-short-id + (compose (cut string-take <> 7) oid->string commit-id)) + + (define (make-reporter start-commit end-commit commits) + (format (current-error-port) + (G_ "Authenticating commits ~a to ~a (~h new \ +commits)...~%") + (commit-short-id start-commit) + (commit-short-id end-commit) + (length commits)) + + (if (isatty? (current-error-port)) + (progress-reporter/bar (length commits)) + progress-reporter/silent)) + + (with-error-handling + (with-git-error-handling + (match (command-line-arguments options) + ((commit signer) + (let* ((directory (assoc-ref options 'directory)) + (show-stats? (assoc-ref options 'show-stats?)) + (keyring (assoc-ref options 'keyring-reference)) + (repository (repository-open directory)) + (end (match (assoc-ref options 'end-commit) + (#f (reference-target + (repository-head repository))) + (oid oid))) + (history (match (assoc-ref options 'historical-authorizations) + (#f '()) + (file (call-with-input-file file + read-authorizations)))) + (cache-key (or (assoc-ref options 'cache-key) + (repository-cache-key repository)))) + (define stats + (authenticate-repository repository (string->oid commit) + (openpgp-fingerprint signer) + #:end end + #:keyring-reference keyring + #:historical-authorizations history + #:cache-key cache-key + #:make-reporter make-reporter)) + + (when (and show-stats? (not (null? stats))) + (show-stats stats)))) + (_ + (leave (G_ "wrong number of arguments; \ +expected COMMIT and SIGNER~%"))))))) -- cgit v1.2.3