upstream.scm 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2010-2023 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2015 Alex Kost <alezost@gmail.com>
  4. ;;; Copyright © 2019, 2022 Ricardo Wurmus <rekado@elephly.net>
  5. ;;; Copyright © 2021 Sarah Morgensen <iskarian@mgsn.dev>
  6. ;;; Copyright © 2021, 2022 Maxime Devos <maximedevos@telenet.be>
  7. ;;; Copyright © 2022 Hartmut Goebel <h.goebel@crazy-compilers.com>
  8. ;;;
  9. ;;; This file is part of GNU Guix.
  10. ;;;
  11. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  12. ;;; under the terms of the GNU General Public License as published by
  13. ;;; the Free Software Foundation; either version 3 of the License, or (at
  14. ;;; your option) any later version.
  15. ;;;
  16. ;;; GNU Guix is distributed in the hope that it will be useful, but
  17. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  18. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. ;;; GNU General Public License for more details.
  20. ;;;
  21. ;;; You should have received a copy of the GNU General Public License
  22. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  23. (define-module (guix upstream)
  24. #:use-module (guix records)
  25. #:use-module (guix utils)
  26. #:use-module (guix discovery)
  27. #:use-module ((guix download)
  28. #:select (download-to-store url-fetch))
  29. #:use-module (guix git-download)
  30. #:use-module (guix gnupg)
  31. #:use-module (guix packages)
  32. #:use-module (guix diagnostics)
  33. #:use-module (guix ui)
  34. #:use-module (guix base32)
  35. #:use-module (guix gexp)
  36. #:autoload (guix git) (latest-repository-commit git-reference->git-checkout)
  37. #:use-module (guix hash)
  38. #:use-module (guix store)
  39. #:use-module ((guix derivations) #:select (built-derivations derivation->output-path))
  40. #:autoload (guix read-print) (object->string*)
  41. #:autoload (gcrypt hash) (port-sha256)
  42. #:use-module (guix monads)
  43. #:use-module (srfi srfi-1)
  44. #:use-module (srfi srfi-9)
  45. #:use-module (srfi srfi-11)
  46. #:use-module (srfi srfi-26)
  47. #:use-module (srfi srfi-34)
  48. #:use-module (srfi srfi-35)
  49. #:use-module (rnrs bytevectors)
  50. #:use-module (ice-9 match)
  51. #:export (upstream-source
  52. upstream-source?
  53. upstream-source-package
  54. upstream-source-version
  55. upstream-source-urls
  56. upstream-source-signature-urls
  57. upstream-source-archive-types
  58. upstream-source-inputs
  59. upstream-input-type-predicate
  60. upstream-source-regular-inputs
  61. upstream-source-native-inputs
  62. upstream-source-propagated-inputs
  63. upstream-input
  64. upstream-input?
  65. upstream-input-name
  66. upstream-input-downstream-name
  67. upstream-input-type
  68. upstream-input-min-version
  69. upstream-input-max-version
  70. url-predicate
  71. url-prefix-predicate
  72. coalesce-sources
  73. upstream-updater
  74. upstream-updater?
  75. upstream-updater-name
  76. upstream-updater-description
  77. upstream-updater-predicate
  78. upstream-updater-import
  79. %updaters
  80. lookup-updater
  81. download-tarball
  82. package-archive-type
  83. package-latest-release
  84. package-latest-release*
  85. package-update
  86. update-package-source))
  87. ;;; Commentary:
  88. ;;;
  89. ;;; This module provides tools to represent and manipulate a upstream source
  90. ;;; code, and to auto-update package recipes.
  91. ;;;
  92. ;;; Code:
  93. ;; Representation of upstream's source. There can be several URLs--e.g.,
  94. ;; tar.gz, tar.gz, etc. There can be correspond signature URLs, one per
  95. ;; source URL.
  96. (define-record-type* <upstream-source>
  97. upstream-source make-upstream-source
  98. upstream-source?
  99. (package upstream-source-package) ;string
  100. (version upstream-source-version) ;string
  101. (urls upstream-source-urls) ;list of strings|git-reference
  102. (signature-urls upstream-source-signature-urls ;#f | list of strings
  103. (default #f))
  104. (inputs upstream-source-inputs ;#f | list of <upstream-input>
  105. (delayed) (default #f))) ;delayed because optional and costly
  106. ;; Representation of a dependency as expressed by upstream.
  107. (define-record-type* <upstream-input>
  108. upstream-input make-upstream-input
  109. upstream-input?
  110. (name upstream-input-name) ;upstream package name
  111. (downstream-name upstream-input-downstream-name) ;Guix package name
  112. (type upstream-input-type ;'regular | 'native | 'propagated
  113. (default 'regular))
  114. (min-version upstream-input-min-version
  115. (default 'any))
  116. (max-version upstream-input-max-version
  117. (default 'any)))
  118. (define (upstream-input-type-predicate type)
  119. "Return a predicate that returns true when passed an <upstream-input> record
  120. of the given TYPE (a symbol such as 'propagated)."
  121. (lambda (source)
  122. (eq? type (upstream-input-type source))))
  123. (define (input-type-filter type)
  124. "Return a procedure that, given an <upstream-source>, returns the subset of
  125. its inputs that have the given TYPE (a symbol such as 'native)."
  126. (lambda (source)
  127. "Return the subset of inputs of SOURCE that have the given TYPE."
  128. (filter (lambda (input)
  129. (eq? type (upstream-input-type input)))
  130. (upstream-source-inputs source))))
  131. (define upstream-source-regular-inputs (input-type-filter 'regular))
  132. (define upstream-source-native-inputs (input-type-filter 'native))
  133. (define upstream-source-propagated-inputs (input-type-filter 'propagated))
  134. (define* (url-predicate matching-url?)
  135. "Return a predicate that returns true when passed a package whose source is
  136. an <origin> with the URL-FETCH method, and one of its URLs passes
  137. MATCHING-URL?."
  138. (lambda (package)
  139. (match (package-source package)
  140. ((? origin? origin)
  141. (and (eq? (origin-method origin) url-fetch)
  142. (match (origin-uri origin)
  143. ((? string? url)
  144. (matching-url? url))
  145. (((? string? urls) ...)
  146. (any matching-url? urls))
  147. (_
  148. #f))))
  149. (_ #f))))
  150. (define (url-prefix-predicate prefix)
  151. "Return a predicate that returns true when passed a package where one of its
  152. source URLs starts with PREFIX."
  153. (url-predicate (cut string-prefix? prefix <>)))
  154. (define (upstream-source-archive-types release)
  155. "Return the available types of archives for RELEASE---a list of strings such
  156. as \"gz\" or \"xz\"."
  157. (map file-extension (upstream-source-urls release)))
  158. (define (coalesce-sources sources)
  159. "Coalesce the elements of SOURCES, a list of <upstream-source>, that
  160. correspond to the same version."
  161. (define (same-version? r1 r2)
  162. (string=? (upstream-source-version r1) (upstream-source-version r2)))
  163. (define (release>? r1 r2)
  164. (version>? (upstream-source-version r1) (upstream-source-version r2)))
  165. (fold (lambda (release result)
  166. (match result
  167. ((head . tail)
  168. (if (same-version? release head)
  169. (cons (upstream-source
  170. (inherit release)
  171. (urls (append (upstream-source-urls release)
  172. (upstream-source-urls head)))
  173. (signature-urls
  174. (let ((one (upstream-source-signature-urls release))
  175. (two (upstream-source-signature-urls head)))
  176. (and one two (append one two)))))
  177. tail)
  178. (cons release result)))
  179. (()
  180. (list release))))
  181. '()
  182. (sort sources release>?)))
  183. ;;;
  184. ;;; Auto-update.
  185. ;;;
  186. (define-record-type* <upstream-updater>
  187. upstream-updater make-upstream-updater
  188. upstream-updater?
  189. (name upstream-updater-name)
  190. (description upstream-updater-description)
  191. (pred upstream-updater-predicate)
  192. (import upstream-updater-import))
  193. (define (importer-modules)
  194. "Return the list of importer modules."
  195. (cons (resolve-interface '(guix gnu-maintenance))
  196. (all-modules (map (lambda (entry)
  197. `(,entry . "guix/import"))
  198. %load-path)
  199. #:warn warn-about-load-error)))
  200. (define %updaters
  201. ;; The list of publically-known updaters, alphabetically sorted.
  202. (delay
  203. (sort (fold-module-public-variables (lambda (obj result)
  204. (if (upstream-updater? obj)
  205. (cons obj result)
  206. result))
  207. '()
  208. (importer-modules))
  209. (lambda (updater1 updater2)
  210. (string<? (symbol->string (upstream-updater-name updater1))
  211. (symbol->string (upstream-updater-name updater2)))))))
  212. ;; Tests need to mock this variable so mark it as "non-declarative".
  213. (set! %updaters %updaters)
  214. (define* (lookup-updater package
  215. #:optional (updaters (force %updaters)))
  216. "Return an updater among UPDATERS that matches PACKAGE, or #f if none of
  217. them matches."
  218. (find (match-lambda
  219. (($ <upstream-updater> name description pred import)
  220. (pred package)))
  221. updaters))
  222. (define* (package-latest-release package
  223. #:optional
  224. (updaters (force %updaters))
  225. #:key (version #f))
  226. "Return an upstream source to update PACKAGE, a <package> object, or #f if
  227. none of UPDATERS matches PACKAGE. When several updaters match PACKAGE, try
  228. them until one of them returns an upstream source. It is the caller's
  229. responsibility to ensure that the returned source is newer than the current
  230. one."
  231. (any (match-lambda
  232. (($ <upstream-updater> name description pred import)
  233. (and (pred package)
  234. (import package #:version version))))
  235. updaters))
  236. (define* (package-latest-release* package
  237. #:optional
  238. (updaters (force %updaters)))
  239. "Like 'package-latest-release', but ensure that the return source is newer
  240. than that of PACKAGE."
  241. (match (package-latest-release package updaters)
  242. ((and source ($ <upstream-source> name version))
  243. (and (version>? version (package-version package))
  244. source))
  245. (_
  246. #f)))
  247. (define (uncompressed-tarball name tarball)
  248. "Return a derivation that decompresses TARBALL."
  249. (define (ref package)
  250. (module-ref (resolve-interface '(gnu packages compression))
  251. package))
  252. (define compressor
  253. (cond ((or (string-suffix? ".gz" tarball)
  254. (string-suffix? ".tgz" tarball))
  255. (file-append (ref 'gzip) "/bin/gzip"))
  256. ((string-suffix? ".bz2" tarball)
  257. (file-append (ref 'bzip2) "/bin/bzip2"))
  258. ((string-suffix? ".xz" tarball)
  259. (file-append (ref 'xz) "/bin/xz"))
  260. ((string-suffix? ".lz" tarball)
  261. (file-append (ref 'lzip) "/bin/lzip"))
  262. (else
  263. (error "unknown archive type" tarball))))
  264. (gexp->derivation (file-sans-extension name)
  265. #~(begin
  266. (copy-file #+tarball #+name)
  267. (and (zero? (system* #+compressor "-d" #+name))
  268. (copy-file #+(file-sans-extension name)
  269. #$output)))))
  270. (define* (download-tarball store url signature-url
  271. #:key (key-download 'interactive) key-server)
  272. "Download the tarball at URL to the store; check its OpenPGP signature at
  273. SIGNATURE-URL, unless SIGNATURE-URL is false. On success, return the tarball
  274. file name; return #f on failure (network failure or authentication failure).
  275. KEY-DOWNLOAD specifies a download policy for missing OpenPGP keys; allowed
  276. values: 'interactive' (default), 'always', and 'never'; KEY-SERVER specifies
  277. the OpenPGP key server where the key should be looked up."
  278. (let ((tarball (download-to-store store url)))
  279. (if (not signature-url)
  280. tarball
  281. (let* ((sig (download-to-store store signature-url))
  282. ;; Sometimes we get a signature over the uncompressed tarball.
  283. ;; In that case, decompress the tarball in the store so that we
  284. ;; can check the signature.
  285. (data (if (string-prefix? (basename url)
  286. (basename signature-url))
  287. tarball
  288. (run-with-store store
  289. (mlet %store-monad ((drv (uncompressed-tarball
  290. (basename url) tarball)))
  291. (mbegin %store-monad
  292. (built-derivations (list drv))
  293. (return (derivation->output-path drv))))))))
  294. (let-values (((status data)
  295. (if sig
  296. (gnupg-verify* sig data
  297. #:server key-server
  298. #:key-download key-download)
  299. (values 'missing-signature data))))
  300. (match status
  301. ('valid-signature
  302. tarball)
  303. ('missing-signature
  304. (warning (G_ "failed to download detached signature from ~a~%")
  305. signature-url)
  306. #f)
  307. ('invalid-signature
  308. (warning (G_ "signature verification failed for '~a' (key: ~a)~%")
  309. url data)
  310. #f)
  311. ('missing-key
  312. (warning (G_ "missing public key ~a for '~a'~%")
  313. data url)
  314. #f)))))))
  315. (define (upstream-source-compiler/url-fetch source system)
  316. "Lower SOURCE, an <upstream-source> pointing to a tarball, as a
  317. fixed-output derivation that would fetch it, and verify its authenticity."
  318. (mlet* %store-monad ((url -> (first (upstream-source-urls source)))
  319. (signature
  320. -> (and=> (upstream-source-signature-urls source)
  321. first))
  322. (tarball ((store-lift download-tarball) url signature)))
  323. (unless tarball
  324. (raise (formatted-message (G_ "failed to fetch source from '~a'")
  325. url)))
  326. ;; Instead of returning TARBALL, return a fixed-output derivation that
  327. ;; would be able to re-download it. In practice, since TARBALL is already
  328. ;; in the store, no extra download will happen, but having the derivation
  329. ;; in store improves provenance tracking.
  330. (let ((hash (call-with-input-file tarball port-sha256)))
  331. (url-fetch url 'sha256 hash (store-path-package-name tarball)
  332. #:system system))))
  333. (define (upstream-source-compiler/git-fetch source system)
  334. "Lower SOURCE, an <upstream-source> using git, as a fixed-output
  335. derivation that would fetch it."
  336. (mlet* %store-monad ((reference -> (upstream-source-urls source))
  337. (checkout
  338. (lower-object
  339. (git-reference->git-checkout reference)
  340. system)))
  341. ;; Like in 'upstream-source-compiler/url-fetch', return a fixed-output
  342. ;; derivation instead of CHECKOUT.
  343. (git-fetch reference 'sha256
  344. (file-hash* checkout #:recursive? #true #:select? (const #true))
  345. (git-file-name (upstream-source-package source)
  346. (upstream-source-version source))
  347. #:system system)))
  348. (define-gexp-compiler (upstream-source-compiler (source <upstream-source>)
  349. system target)
  350. "Download SOURCE, lower it as a fixed-output derivation that would fetch it,
  351. and verify its authenticity if possible."
  352. (if (git-reference? (upstream-source-urls source))
  353. (upstream-source-compiler/git-fetch source system)
  354. (upstream-source-compiler/url-fetch source system)))
  355. (define (find2 pred lst1 lst2)
  356. "Like 'find', but operate on items from both LST1 and LST2. Return two
  357. values: the item from LST1 and the item from LST2 that match PRED."
  358. (let loop ((lst1 lst1) (lst2 lst2))
  359. (match lst1
  360. ((head1 . tail1)
  361. (match lst2
  362. ((head2 . tail2)
  363. (if (pred head1 head2)
  364. (values head1 head2)
  365. (loop tail1 tail2)))))
  366. (()
  367. (values #f #f)))))
  368. (define (package-archive-type package)
  369. "If PACKAGE's source is a tarball or zip archive, return its archive type--a
  370. string such as \"xz\". Otherwise return #f."
  371. (match (and=> (package-source package) origin-actual-file-name)
  372. (#f #f)
  373. (file
  374. (let ((extension (file-extension file)))
  375. ;; FILE might be "example-1.2-checkout", in which case we want to
  376. ;; ignore the extension.
  377. (and (or (string-contains extension "z")
  378. (string-contains extension "tar"))
  379. extension)))))
  380. (define* (package-update/url-fetch store package source
  381. #:key key-download key-server)
  382. "Return the version, tarball, and SOURCE, to update PACKAGE to
  383. SOURCE, an <upstream-source>."
  384. (match source
  385. (($ <upstream-source> _ version urls signature-urls)
  386. (let*-values (((archive-type)
  387. (package-archive-type package))
  388. ((url signature-url)
  389. ;; Try to find a URL that matches ARCHIVE-TYPE.
  390. (find2 (lambda (url sig-url)
  391. ;; Some URIs lack a file extension, like
  392. ;; 'https://crates.io/???/0.1/download'. In that
  393. ;; case, pick the first URL.
  394. (or (not archive-type)
  395. (string-suffix? archive-type url)))
  396. urls
  397. (or signature-urls (circular-list #f)))))
  398. ;; If none of URLS matches ARCHIVE-TYPE, then URL is #f; in that case,
  399. ;; pick up the first element of URLS.
  400. (let ((tarball (download-tarball store
  401. (or url (first urls))
  402. (and (pair? signature-urls)
  403. (or signature-url
  404. (first signature-urls)))
  405. #:key-server key-server
  406. #:key-download key-download)))
  407. (values version tarball source))))))
  408. (define* (package-update/git-fetch store package source
  409. #:key key-download key-server)
  410. "Return the version, checkout, and SOURCE, to update PACKAGE to
  411. SOURCE, an <upstream-source>."
  412. ;; TODO: it would be nice to authenticate commits, e.g. with
  413. ;; "guix git authenticate" or a list of permitted signing keys.
  414. (define ref (upstream-source-urls source)) ; a <git-reference>
  415. (values (upstream-source-version source)
  416. (latest-repository-commit
  417. store
  418. (git-reference-url ref)
  419. #:ref `(tag-or-commit . ,(git-reference-commit ref))
  420. #:recursive? (git-reference-recursive? ref))
  421. source))
  422. (define %method-updates
  423. ;; Mapping of origin methods to source update procedures.
  424. `((,url-fetch . ,package-update/url-fetch)
  425. (,git-fetch . ,package-update/git-fetch)))
  426. (define* (package-update store package
  427. #:optional (updaters (force %updaters))
  428. #:key (version #f)
  429. (key-download 'interactive) key-server)
  430. "Return the new version, the file name of the new version tarball, and input
  431. changes for PACKAGE; return #f (three values) when PACKAGE is up-to-date;
  432. raise an error when the updater could not determine available releases.
  433. KEY-DOWNLOAD specifies a download policy for missing OpenPGP keys; allowed
  434. values: 'always', 'never', and 'interactive' (default).
  435. When VERSION is specified, update PACKAGE to that version, even if that is a
  436. downgrade."
  437. (match (package-latest-release package updaters #:version version)
  438. ((? upstream-source? source)
  439. (if (or (version>? (upstream-source-version source)
  440. (package-version package))
  441. (and version
  442. (begin
  443. (warning (package-location package)
  444. (G_ "downgrading '~a' from ~a to ~a~%")
  445. (package-name package)
  446. (package-version package)
  447. (upstream-source-version source))
  448. #t)))
  449. (let ((method (match (package-source package)
  450. ((? origin? origin)
  451. (origin-method origin))
  452. (_
  453. #f))))
  454. (match (assq method %method-updates)
  455. (#f
  456. (raise (make-compound-condition
  457. (formatted-message (G_ "cannot download for \
  458. this method: ~s")
  459. method)
  460. (condition
  461. (&error-location
  462. (location (package-location package)))))))
  463. ((_ . update)
  464. (update store package source
  465. #:key-server key-server
  466. #:key-download key-download))))
  467. (values #f #f #f)))
  468. (#f
  469. ;; Warn rather than abort so that other updates can still take place.
  470. (if version
  471. (warning (G_ "updater failed to find release ~a@~a~%")
  472. (package-name package) version)
  473. (warning (G_ "updater failed to determine available releases for ~a~%")
  474. (package-name package)))
  475. (values #f #f #f))))
  476. (define (update-package-inputs package source)
  477. "Update the input fields of the definition of PACKAGE according to those
  478. specified in SOURCE, an <upstream-source>."
  479. (define (update-field field source-inputs package-inputs)
  480. (define loc
  481. (package-field-location package field))
  482. (define new
  483. (map (compose string->symbol upstream-input-downstream-name)
  484. (source-inputs source)))
  485. (define old
  486. (match (package-inputs package)
  487. (((labels (? package? packages)) ...)
  488. labels)
  489. (_
  490. '())))
  491. (define unchanged?
  492. (equal? new old))
  493. (if (and loc (not unchanged?))
  494. (edit-expression (location->source-properties
  495. (absolute-location loc))
  496. (lambda (str)
  497. (object->string* `(list ,@new)
  498. (location-column loc))))
  499. (unless unchanged?
  500. ;; XXX: Bail out when FIELD isn't already present in the source.
  501. ;; TODO: Add the field if it's missing.
  502. (warning (package-location package)
  503. (G_ "~a: '~a' field not found; leaving it unchanged~%")
  504. (package-name package) field)
  505. (warning (package-location package)
  506. (G_ "~a: expected '~a' value: ~s~%")
  507. (package-name package) field new))))
  508. (define (filtered-inputs source-inputs extra-property ignore-property)
  509. ;; Return a procedure that behaves like SOURCE-INPUTS but additionally
  510. ;; honors EXTRA-PROPERTY and IGNORE-PROPERTY from PACKAGE.
  511. (lambda (source)
  512. (let* ((inputs (source-inputs source))
  513. (properties (package-properties package))
  514. (ignore (or (assoc-ref properties ignore-property) '()))
  515. (extra (or (assoc-ref properties extra-property) '())))
  516. (append (if (null? ignore)
  517. inputs
  518. (remove (lambda (input)
  519. (member (upstream-input-downstream-name input)
  520. ignore))
  521. inputs))
  522. (map (lambda (name)
  523. (upstream-input
  524. (name name)
  525. (downstream-name name)))
  526. extra)))))
  527. (define regular-inputs
  528. (filtered-inputs upstream-source-regular-inputs
  529. 'updater-extra-inputs
  530. 'updater-ignored-inputs))
  531. (define native-inputs
  532. (filtered-inputs upstream-source-native-inputs
  533. 'updater-extra-native-inputs
  534. 'updater-ignored-native-inputs))
  535. (define propagated-inputs
  536. (filtered-inputs upstream-source-propagated-inputs
  537. 'updater-extra-propagated-inputs
  538. 'updater-ignored-propagated-inputs))
  539. (for-each update-field
  540. '(inputs native-inputs propagated-inputs)
  541. (list regular-inputs
  542. native-inputs
  543. propagated-inputs)
  544. (list package-inputs
  545. package-native-inputs
  546. package-propagated-inputs)))
  547. (define* (update-package-source package source hash)
  548. "Modify the source file that defines PACKAGE to refer to SOURCE, an
  549. <upstream-source> whose tarball has SHA256 HASH (a bytevector). Return the
  550. new version string if an update was made, and #f otherwise."
  551. (define (update-expression expr replacements)
  552. ;; Apply REPLACEMENTS to package expression EXPR, a string. REPLACEMENTS
  553. ;; must be a list of replacement pairs, either bytevectors or strings.
  554. (fold (lambda (replacement str)
  555. (match replacement
  556. (((? bytevector? old-bv) . (? bytevector? new-bv))
  557. (string-replace-substring
  558. str
  559. (bytevector->nix-base32-string old-bv)
  560. (bytevector->nix-base32-string new-bv)))
  561. ((old . new)
  562. (string-replace-substring str old new))))
  563. expr
  564. replacements))
  565. (let ((name (package-name package))
  566. (version (upstream-source-version source))
  567. (version-loc (package-field-location package 'version)))
  568. (if version-loc
  569. (let* ((loc (package-location package))
  570. (old-version (package-version package))
  571. (old-hash (content-hash-value
  572. (origin-hash (package-source package))))
  573. (old-url (match (origin-uri (package-source package))
  574. ((? string? url) url)
  575. ((? git-reference? ref)
  576. (git-reference-url ref))
  577. (_ #f)))
  578. (new-url (match (upstream-source-urls source)
  579. ((first _ ...) first)
  580. ((? git-reference? ref)
  581. (git-reference-url ref))
  582. (_ #f)))
  583. (old-commit (match (origin-uri (package-source package))
  584. ((? git-reference? ref)
  585. (git-reference-commit ref))
  586. (_ #f)))
  587. (new-commit (match (upstream-source-urls source)
  588. ((? git-reference? ref)
  589. (git-reference-commit ref))
  590. (_ #f)))
  591. (file (and=> (location-file loc)
  592. (cut search-path %load-path <>))))
  593. (if file
  594. ;; Be sure to use absolute filename. Replace the URL directory
  595. ;; when OLD-URL is available; this is useful notably for
  596. ;; mirror://cpan/ URLs where the directory may change as a
  597. ;; function of the person who uploads the package. Note that
  598. ;; package definitions usually concatenate fragments of the URL,
  599. ;; which is why we only attempt to replace a subset of the URL.
  600. (let ((replacements `((,old-version . ,version)
  601. (,old-hash . ,hash)
  602. ,@(if (and old-commit new-commit)
  603. `((,old-commit . ,new-commit))
  604. '())
  605. ,@(if (and old-url new-url)
  606. `((,(dirname old-url) .
  607. ,(dirname new-url)))
  608. '()))))
  609. (and (edit-expression (location->source-properties
  610. (absolute-location loc))
  611. (cut update-expression <> replacements))
  612. (or (not (upstream-source-inputs source))
  613. (update-package-inputs package source))
  614. version))
  615. (begin
  616. (warning (G_ "~a: could not locate source file")
  617. (location-file loc))
  618. #f)))
  619. (warning (package-location package)
  620. (G_ "~a: no `version' field in source; skipping~%")
  621. name))))
  622. ;;; upstream.scm ends here