From a1bea1cbe25859b6425d1202b790e7cac61da694 Mon Sep 17 00:00:00 2001 From: Adam Bergmark Date: Sat, 15 Jan 2022 04:00:36 +0100 Subject: [PATCH] commenter outdated: perf & warn on unversioned packages --- .../latest-version/latest-version.cabal | 2 +- etc/commenter/latest-version/src/Main.hs | 24 +++- etc/commenter/latest-version/stack.yaml.lock | 13 ++ etc/commenter/src/lib.rs | 133 ++++++++++++------ 4 files changed, 118 insertions(+), 54 deletions(-) create mode 100644 etc/commenter/latest-version/stack.yaml.lock diff --git a/etc/commenter/latest-version/latest-version.cabal b/etc/commenter/latest-version/latest-version.cabal index c5144cae..1be4e493 100644 --- a/etc/commenter/latest-version/latest-version.cabal +++ b/etc/commenter/latest-version/latest-version.cabal @@ -12,7 +12,7 @@ cabal-version: >=1.10 extra-source-files: README.md executable latest-version - ghc-options: -Wall + ghc-options: -Wall -threaded hs-source-dirs: src main-is: Main.hs default-language: Haskell2010 diff --git a/etc/commenter/latest-version/src/Main.hs b/etc/commenter/latest-version/src/Main.hs index 47535f20..cae07be0 100644 --- a/etc/commenter/latest-version/src/Main.hs +++ b/etc/commenter/latest-version/src/Main.hs @@ -9,10 +9,20 @@ import System.Environment import qualified Data.Map as Map main :: IO () -main = - runPantryApp $ - liftIO . putStrLn - . intercalate "." . map show . versionNumbers - . fst . head . Map.toDescList - =<< getHackagePackageVersions YesRequireHackageIndex IgnorePreferredVersions - . mkPackageName =<< head <$> liftIO getArgs +main = do + args <- getArgs + (onlyVersion, packages) <- pure $ case args of + "only-version" : packages -> (True, packages) + packages -> (False, packages) + runPantryApp $ liftIO . putStrLn . unlines_ =<< mapM (latestVersion onlyVersion) packages + where + -- unlines adds an extra trailing newline which can be annoying... + unlines_ = intercalate "\n" + +latestVersion :: (HasPantryConfig env, HasLogFunc env) => Bool -> String -> RIO env String +latestVersion onlyVersion name = fmap (displayVersion onlyVersion) . getVersion . mkPackageName $ name + where + showVersion = intercalate "." . map show . versionNumbers . fst . head . Map.toDescList + getVersion = getHackagePackageVersions YesRequireHackageIndex UsePreferredVersions + displayVersion True v = showVersion v + displayVersion False v = name <> "-" <> showVersion v diff --git a/etc/commenter/latest-version/stack.yaml.lock b/etc/commenter/latest-version/stack.yaml.lock new file mode 100644 index 00000000..cc26a008 --- /dev/null +++ b/etc/commenter/latest-version/stack.yaml.lock @@ -0,0 +1,13 @@ +# This file was autogenerated by Stack. +# You should not edit this file by hand. +# For more information, please see the documentation at: +# https://docs.haskellstack.org/en/stable/lock_files + +packages: [] +snapshots: +- completed: + size: 586296 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/18.yaml + sha256: 63539429076b7ebbab6daa7656cfb079393bf644971156dc349d7c0453694ac2 + original: + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/18.yaml diff --git a/etc/commenter/src/lib.rs b/etc/commenter/src/lib.rs index 1d02896c..1f0f9431 100644 --- a/etc/commenter/src/lib.rs +++ b/etc/commenter/src/lib.rs @@ -30,8 +30,8 @@ pub fn add(lib: Vec, test: Vec, bench: Vec) { } enum VersionTag { - Manual(String), - Auto(String), + Manual(Version), + Auto(Version), } impl VersionTag { @@ -42,7 +42,7 @@ impl VersionTag { } } - fn version(&self) -> &str { + fn version(&self) -> &Version { match self { VersionTag::Manual(s) => s, VersionTag::Auto(s) => s, @@ -52,41 +52,50 @@ impl VersionTag { pub fn outdated() { let mut all: Vec = vec![]; - let versioned = handle(false, |_loc, lines| { + let (versioned, disabled) = handle(false, |_loc, lines| { all.extend(lines); vec![] }); - let mut map: BTreeMap = BTreeMap::new(); + + for DisabledPackage { package } in disabled { + println!("WARN: {package} is disabled without a noted version"); + } + + let mut map: BTreeMap = BTreeMap::new(); for VersionedPackage { package, version } in versioned { map.insert(package, VersionTag::Manual(version)); } - let mut support: BTreeMap<(String, String), BTreeSet<(String, String)>> = BTreeMap::new(); + let mut support: BTreeMap<(Package, Version), BTreeSet<(Package, Version)>> = BTreeMap::new(); for v in all.into_iter() { let caps = regex!("tried ([^ ]+)-([^,-]+),").captures(&v).unwrap(); - let package = caps.get(1).unwrap().as_str().to_owned(); - let version = caps.get(2).unwrap().as_str().to_owned(); + let package = Package(caps.get(1).unwrap().as_str().to_owned()); + let version = Version(caps.get(2).unwrap().as_str().to_owned()); map.insert(package.clone(), VersionTag::Auto(version.clone())); if let Some(caps) = regex!("does not support: ([^ ]+)-([^-]+)").captures(&v) { - let dep_package = caps.get(1).unwrap().as_str().to_owned(); - let dep_version = caps.get(2).unwrap().as_str().to_owned(); + let dep_package = Package(caps.get(1).unwrap().as_str().to_owned()); + let dep_version = Version(caps.get(2).unwrap().as_str().to_owned()); let entry = support.entry((dep_package, dep_version)).or_default(); entry.insert((package, version)); } } - let entries = map.len() + support.len(); - let mut i = 0; + let latest_versions = { + let mut packages: Vec = map.iter().map(|(package, _)| package.clone()).collect(); + packages.append( + &mut support + .iter() + .map(|((package, _), _)| package.clone()) + .collect(), + ); + latest_version(packages.into_iter()) + }; for (package, version) in map { if is_boot(&package) { continue; } - if i % 100 == 0 { - println!("{:02}%", ((i as f64 / entries as f64) * 100.0).floor()); - } - i += 1; - let latest = latest_version(&package); + let latest = latest_versions.get(&package).unwrap(); if version.version() != latest { println!( "{package} mismatch, {tag}: {version}, hackage: {latest}", @@ -101,12 +110,8 @@ pub fn outdated() { continue; } - if i % 100 == 0 { - println!("{:02}%", ((i as f64 / entries as f64) * 100.0).floor()); - } - i += 1; - let latest = latest_version(&package); - if version != latest { + let latest = latest_versions.get(&package).unwrap(); + if &version != latest { let max = 3; let dependents_stripped = dependents.len().saturating_sub(max); let dependents = dependents @@ -128,7 +133,7 @@ pub fn outdated() { } } -fn is_boot(package: &str) -> bool { +fn is_boot(package: &Package) -> bool { [ "Cabal", "base", @@ -151,13 +156,13 @@ fn is_boot(package: &str) -> bool { "text", "time", ] - .contains(&package) + .contains(&&*package.0) } -fn latest_version(pkg: &str) -> String { +fn latest_version(packages: impl Iterator) -> BTreeMap { String::from_utf8( Command::new("latest-version") - .args([pkg]) + .args(packages.map(|p| p.0)) .output() .unwrap() .stdout, @@ -165,6 +170,12 @@ fn latest_version(pkg: &str) -> String { .unwrap() .trim() .to_owned() + .lines() + .map(|s| { + let VersionedPackage { package, version } = parse_versioned_package_canonical(s).unwrap(); + (package, version) + }) + .collect() } enum State { @@ -178,37 +189,67 @@ enum State { } struct VersionedPackage { - package: String, - version: String, + package: Package, + version: Version, } -fn parse_versioned_package(s: &str) -> Option { - if let Some(caps) = regex!(r#"- *([^ ]+) < *0 *# *([\d.]+)"#).captures(s) { - let package = caps.get(1).unwrap().as_str().to_owned(); - let version = caps.get(2).unwrap().as_str().to_owned(); - Some(VersionedPackage { package, version }) - } else if let Some(caps) = regex!(r#"- *([^ ]+) *# *([\d.]+)"#).captures(s) { - let package = caps.get(1).unwrap().as_str().to_owned(); - let version = caps.get(2).unwrap().as_str().to_owned(); +fn parse_versioned_package_canonical(s: &str) -> Option { + if let Some(caps) = regex!(r#"^(.+)-([\d.]+)$"#).captures(s) { + let package = Package(caps.get(1).unwrap().as_str().to_owned()); + let version = Version(caps.get(2).unwrap().as_str().to_owned()); Some(VersionedPackage { package, version }) } else { None } } -fn handle(write: bool, mut f: F) -> Vec +fn parse_versioned_package_yaml(s: &str) -> Option { + if let Some(caps) = regex!(r#"- *([^ ]+) < *0 *# *([\d.]+)"#).captures(s) { + let package = Package(caps.get(1).unwrap().as_str().to_owned()); + let version = Version(caps.get(2).unwrap().as_str().to_owned()); + Some(VersionedPackage { package, version }) + } else if let Some(caps) = regex!(r#"- *([^ ]+) *# *([\d.]+)"#).captures(s) { + let package = Package(caps.get(1).unwrap().as_str().to_owned()); + let version = Version(caps.get(2).unwrap().as_str().to_owned()); + Some(VersionedPackage { package, version }) + } else { + None + } +} + +struct DisabledPackage { + package: String, +} + +fn parse_disabled_package(s: &str) -> Option { + if !regex!(r#"- *([^ ]+) < *0 *# tried"#).is_match(s) { + if let Some(caps) = regex!(r#"- *([^ ]+) < *0 *# *[^\d]"#).captures(s) { + let package = caps.get(1).unwrap().as_str().to_owned(); + Some(DisabledPackage { package }) + } else { + None + } + } else { + None + } +} + +fn handle(write: bool, mut f: F) -> (Vec, Vec) where F: FnMut(Location, Vec) -> Vec, { let path = "build-constraints.yaml"; let mut new_lines: Vec = vec![]; let mut versioned_packages: Vec = vec![]; + let mut disabled_packages: Vec = vec![]; let mut state = State::LookingForLibBounds; let mut buf = vec![]; for line in read_lines(path).map(|s| s.unwrap()) { - if let Some(versioned_package) = parse_versioned_package(&line) { + if let Some(versioned_package) = parse_versioned_package_yaml(&line) { versioned_packages.push(versioned_package); + } else if let Some(disabled_package) = parse_disabled_package(&line) { + disabled_packages.push(disabled_package); } match state { @@ -279,7 +320,7 @@ where file.flush().unwrap(); } - versioned_packages + (versioned_packages, disabled_packages) } enum Location { @@ -298,7 +339,7 @@ where #[derive(Deserialize)] struct SnapshotYaml { - // flags: BTreeMap>, + // flags: BTreeMap>, // publish_time packages: Vec, // hidden @@ -312,9 +353,9 @@ struct SnapshotPackage { } #[derive(PartialOrd, Ord, PartialEq, Eq, Clone)] -struct PackageName(String); +struct Package(String); -impl fmt::Display for PackageName { +impl fmt::Display for Package { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.fmt(f) } @@ -331,7 +372,7 @@ impl fmt::Display for Version { // zstd-0.1.3.0@sha256:4c0a372251068eb6086b8c3a0a9f347488f08b570a7705844ffeb2c720c97223,3723 struct PackageWithVersionAndSha { - name: PackageName, + name: Package, version: Version, } @@ -343,7 +384,7 @@ impl<'de> serde::Deserialize<'de> for PackageWithVersionAndSha { let s: String = String::deserialize(deserializer)?; let r = regex!(r#"^(.+?)-([.\d]+)@sha256:[\da-z]+,\d+$"#); if let Some(caps) = r.captures(&s) { - let name = PackageName(caps.get(1).unwrap().as_str().to_owned()); + let name = Package(caps.get(1).unwrap().as_str().to_owned()); let version = Version(caps.get(2).unwrap().as_str().to_owned()); Ok(Self { name, version }) } else { @@ -366,7 +407,7 @@ where } struct Snapshot { - packages: BTreeMap>, + packages: BTreeMap>, } #[derive(Clone, Copy)]