Line data Source code
1 : use anyhow::{anyhow, Result};
2 : use clap::ValueEnum;
3 : use semver::Version;
4 : use std::fmt;
5 : use uuid::Uuid;
6 :
7 : /// The channel a release is published to, named by the workflow that publishes it
8 : #[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
9 : pub enum ReleaseChannel {
10 : Stable,
11 : Latest,
12 : }
13 :
14 : impl fmt::Display for ReleaseChannel {
15 1 : fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 1 : match self {
17 0 : ReleaseChannel::Stable => write!(f, "stable"),
18 1 : ReleaseChannel::Latest => write!(f, "latest"),
19 : }
20 1 : }
21 : }
22 :
23 : /// The version, id and channel of a release
24 : pub struct ReleaseVersion {
25 : /// The semantic version the artifacts were built with
26 : pub version: Version,
27 : /// The release id, a UUID v5 of the version string
28 : pub id: Uuid,
29 : /// The release channel
30 : pub channel: ReleaseChannel,
31 : }
32 :
33 : const RELEASE_ID_NAMESPACE: Uuid = uuid::uuid!("444560d1-4268-44be-8016-b09f95eadbba");
34 :
35 22 : pub fn build_release_version(version: Version, channel: ReleaseChannel) -> ReleaseVersion {
36 22 : let id = Uuid::new_v5(&RELEASE_ID_NAMESPACE, version.to_string().as_bytes());
37 22 : ReleaseVersion { version, id, channel }
38 22 : }
39 :
40 : /// Read the version of an electron-builder update manifest (latest-mac.yml)
41 21 : pub fn read_manifest_version(manifest: &str) -> Result<Version> {
42 21 : let version = manifest
43 21 : .lines()
44 21 : .find_map(|line| line.strip_prefix("version: "))
45 21 : .ok_or_else(|| anyhow!("update manifest has no version"))?;
46 20 : Version::parse(version.trim()).map_err(|e| anyhow!("invalid version in update manifest: {}: {}", version, e))
47 21 : }
48 :
49 : #[cfg(test)]
50 : mod tests {
51 : use super::*;
52 :
53 6 : fn version(text: &str) -> Version {
54 6 : Version::parse(text).unwrap()
55 6 : }
56 :
57 : #[test]
58 1 : fn keeps_the_version_and_the_channel() {
59 1 : let release = build_release_version(version("1.2.4-dev.4"), ReleaseChannel::Latest);
60 1 : assert_eq!(release.version.to_string(), "1.2.4-dev.4");
61 1 : assert_eq!(release.channel.to_string(), "latest");
62 1 : }
63 :
64 : #[test]
65 1 : fn a_prerelease_sorts_after_the_tag_and_before_the_next_tag() {
66 1 : let prerelease = version("1.2.4-dev.4");
67 1 : assert!(prerelease > Version::new(1, 2, 3));
68 1 : assert!(prerelease < Version::new(1, 2, 4));
69 1 : }
70 :
71 : #[test]
72 1 : fn the_release_id_only_depends_on_the_version() {
73 1 : let id = build_release_version(version("1.2.4-dev.4"), ReleaseChannel::Latest).id;
74 1 : assert_eq!(build_release_version(version("1.2.4-dev.4"), ReleaseChannel::Stable).id, id);
75 1 : assert_ne!(build_release_version(version("1.2.4-dev.5"), ReleaseChannel::Latest).id, id);
76 1 : }
77 :
78 : #[test]
79 1 : fn reads_the_top_level_version_of_a_manifest() {
80 1 : let manifest = "version: 1.2.4-dev.4\nfiles:\n - url: DashQL.zip\n version: 9.9.9\npath: DashQL.zip\n";
81 1 : assert_eq!(read_manifest_version(manifest).unwrap(), version("1.2.4-dev.4"));
82 1 : }
83 :
84 : #[test]
85 1 : fn refuses_a_manifest_without_a_valid_version() {
86 1 : let error = read_manifest_version("path: DashQL.zip\n").unwrap_err();
87 1 : assert!(error.to_string().contains("has no version"), "{error}");
88 1 : let error = read_manifest_version("version: nightly\n").unwrap_err();
89 1 : assert!(error.to_string().contains("invalid version"), "{error}");
90 1 : }
91 : }
|