diff --git a/core/libs/package-build.el b/core/libs/package-build.el index 9bacd6e7c..5b6559045 100644 --- a/core/libs/package-build.el +++ b/core/libs/package-build.el @@ -195,42 +195,21 @@ Otherwise do nothing. FORMAT-STRING and ARGS are as per that function." version))) (defun package-build-get-timestamp-version (rcp) - (let ((rev (and (cl-typep rcp 'package-git-recipe) - (or (oref rcp commit) - (when-let ((branch (oref rcp branch))) - (concat "origin/" branch)) - "origin/HEAD")))) + (let* ((rev (and (cl-typep rcp 'package-git-recipe) + (or (oref rcp commit) + (when-let ((branch (oref rcp branch))) + (concat "origin/" branch)) + "origin/HEAD"))) + (time (package-build--get-timestamp rcp rev))) (cons (package-build--get-commit rcp rev) - (package-build--parse-time - (package-build--get-timestamp rcp rev) - (oref rcp time-regexp))))) + ;; We remove zero-padding of the HH portion, as + ;; that is lost when stored in archive-contents. + (concat (format-time-string "%Y%m%d." time t) + (format "%d" (string-to-number + (format-time-string "%H%M" time t))))))) ;;;; Internal -(defun package-build--parse-time (str &optional regexp) - "Parse STR as a time, and format as a YYYYMMDD.HHMM string. -Always use Coordinated Universal Time (UTC) for output string. -If REGEXP is provided, it is applied to STR and the function -parses the first match group instead of STR." - (unless str - (error "No valid timestamp found")) - (setq str (substring-no-properties str)) - (when regexp - (if (string-match regexp str) - (setq str (match-string 1 str)) - (error "No valid timestamp found"))) - ;; We remove zero-padding the HH portion, as it is lost - ;; when stored in the archive-contents - (let ((time (date-to-time - (if (string-match "\ -^\\([0-9]\\{4\\}\\)/\\([0-9]\\{2\\}\\)/\\([0-9]\\{2\\}\\) \ -\\([0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\}\\)$" str) - (concat (match-string 1 str) "-" (match-string 2 str) "-" - (match-string 3 str) " " (match-string 4 str)) - str)))) - (concat (format-time-string "%Y%m%d." time t) - (format "%d" (string-to-number (format-time-string "%H%M" time t)))))) - (defun package-build--find-version-newest (tags &optional regexp) "Find the newest version in TAGS matching REGEXP. If optional REGEXP is nil, then `package-build-version-regexp' @@ -353,9 +332,11 @@ is used instead." (let ((default-directory (package-recipe--working-tree rcp))) ;; `package-build-expand-files-spec' expects REV to be checked out. (package-build--checkout-1 rcp rev) - (car (apply #'process-lines - "git" "log" "--first-parent" "-n1" "--pretty=format:'\%ci'" rev - "--" (mapcar #'car (package-build-expand-files-spec rcp)))))) + (string-to-number + (car (apply #'process-lines + "git" "log" "-n1" "--first-parent" + "--pretty=format:%cd" "--date=unix" + rev "--" (mapcar #'car (package-build-expand-files-spec rcp))))))) (cl-defmethod package-build--used-url ((rcp package-git-recipe)) (let ((default-directory (package-recipe--working-tree rcp))) @@ -401,10 +382,13 @@ is used instead." (cl-defmethod package-build--get-timestamp ((rcp package-hg-recipe) rev) (let ((default-directory (package-recipe--working-tree rcp))) - (car (apply #'process-lines - "hg" "log" "--style" "compact" "-l1" - `(,@(and rev (list "--rev" rev)) - ,@(mapcar #'car (package-build-expand-files-spec rcp))))))) + (string-to-number + (car (split-string ; "hgdate" is " " + (car (apply #'process-lines + "hg" "log" "--limit" "1" "--template" "{date|hgdate}\n" + `(,@(and rev (list "--rev" rev)) + ,@(mapcar #'car (package-build-expand-files-spec rcp))))) + " "))))) (cl-defmethod package-build--used-url ((rcp package-hg-recipe)) (let ((default-directory (package-recipe--working-tree rcp))) @@ -444,20 +428,28 @@ is used instead." (princ ";; Local Variables:\n;; no-byte-compile: t\n;; End:\n" (current-buffer))))) -(defun package-build--create-tar (name version directory) - "Create a tar file containing the contents of VERSION of package NAME." +(defun package-build--create-tar (name version directory mtime) + "Create a tar file containing the contents of VERSION of package NAME. +DIRECTORY is a temporary directory that contains the directory +that is put in the tarball. MTIME is used as the modification +time of all files, making the tarball reproducible." (let ((tar (expand-file-name (concat name "-" version ".tar") package-build-archive-dir)) (dir (concat name "-" version))) (when (eq system-type 'windows-nt) (setq tar (replace-regexp-in-string "^\\([a-z]\\):" "/\\1" tar))) (let ((default-directory directory)) - (process-file package-build-tar-executable nil - (get-buffer-create "*package-build-checkout*") nil - "-cvf" tar - "--exclude=.git" - "--exclude=.hg" - dir)) + (process-file + package-build-tar-executable nil + (get-buffer-create "*package-build-checkout*") nil + "-cf" tar dir + ;; Arguments that are need to strip metadata that + ;; prevent a reproducable tarball as described at + ;; https://reproducible-builds.org/docs/archives. + "--sort=name" + (format "--mtime=@%d" mtime) + "--owner=0" "--group=0" "--numeric-owner" + "--pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime")) (when (and package-build-verbose noninteractive) (message "Created %s containing:" (file-name-nondirectory tar)) (dolist (line (sort (process-lines package-build-tar-executable @@ -683,8 +675,8 @@ be returned. If ASSERT and `files' are both non-nil and using `files' results in the same set of files as the default spec, then show a warning. -A file specification SPEC is a list. Its elements are processes -in order and can have the following form: +A files specification is a list. Its elements are processed in +order and can have the following form: - :default @@ -863,11 +855,12 @@ in `package-build-archive-dir'." (package-build--desc-from-library name version commit files 'tar) (error "%s[-pkg].el matching package name is missing" - name))))) + name)))) + (mtime (package-build--get-timestamp rcp commit))) (package-build--copy-package-files files source-dir target) (package-build--write-pkg-file desc target) (package-build--generate-info-files files source-dir target) - (package-build--create-tar name version tmp-dir) + (package-build--create-tar name version tmp-dir mtime) (package-build--write-pkg-readme name files source-dir) (package-build--write-archive-entry desc)) (delete-directory tmp-dir t nil)))) diff --git a/core/libs/package-recipe.el b/core/libs/package-recipe.el index 5c9f0c38b..8bcab9ed8 100644 --- a/core/libs/package-recipe.el +++ b/core/libs/package-recipe.el @@ -38,7 +38,6 @@ (defclass package-recipe () ((url-format :allocation :class :initform nil) (repopage-format :allocation :class :initform nil) - (time-regexp :allocation :class :initform nil) (stable-p :allocation :class :initform nil) (name :initarg :name :initform nil) (url :initarg :url :initform nil) @@ -72,10 +71,7 @@ ;;;; Git -(defclass package-git-recipe (package-recipe) - ((time-regexp :initform "\ -\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} \ -[0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\}\\( [+-][0-9]\\{4\\}\\)?\\)"))) +(defclass package-git-recipe (package-recipe) ()) (defclass package-github-recipe (package-git-recipe) ((url-format :initform "https://github.com/%s.git") @@ -87,10 +83,7 @@ ;;;; Mercurial -(defclass package-hg-recipe (package-recipe) - ((time-regexp :initform "\ -\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} \ -[0-9]\\{2\\}:[0-9]\\{2\\}\\( [+-][0-9]\\{4\\}\\)?\\)"))) +(defclass package-hg-recipe (package-recipe) ()) ;;; Interface