Move commenter to its own repo

This commit is contained in:
Adam Bergmark 2022-02-05 16:59:18 +01:00
parent 7489d67779
commit 6261b0bac1
16 changed files with 3 additions and 1313 deletions

View File

@ -1,20 +1,9 @@
#!/usr/bin/env bash
if ! command -v cargo
if ! command -v commenter &> /dev/null
then
echo "Rust doesn't seem to be installed. https://rustup.rs/"
echo "commenter is not installed, get it from https://github.com/bergmark/commenter"
exit 1
fi
if [ "$1" = "outdated" ];
then
if ! command -v latest-version &> /dev/null
then
echo "This command requires latest-version in your PATH"
echo "Install it with: "
echo "stack install --stack-yaml etc/commenter/latest-version/stack.yaml"
exit 1
fi
fi
cargo run -q --release --manifest-path etc/commenter/Cargo.toml $@
commenter $@

View File

@ -1,3 +0,0 @@
/target
/comments.txt
/out.txt

365
etc/commenter/Cargo.lock generated
View File

@ -1,365 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "commenter"
version = "0.2.0"
dependencies = [
"lazy-regex",
"regex",
"serde",
"serde_yaml",
"structopt",
]
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "indexmap"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "lazy-regex"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "919a16773ebf2de27e95fc58460110932e55bb0780e23aa51fa5a6b59c9e2b3d"
dependencies = [
"lazy-regex-proc_macros",
"once_cell",
"regex",
]
[[package]]
name = "lazy-regex-proc_macros"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fbe6bf0a04af51c07976625d5007e75ed9b8b955befc21c77b3947733496e36"
dependencies = [
"proc-macro2",
"quote",
"regex",
"syn",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.112"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
[[package]]
name = "linked-hash-map"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "once_cell"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "ryu"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[package]]
name = "serde"
version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_yaml"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0"
dependencies = [
"indexmap",
"ryu",
"serde",
"yaml-rust",
]
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "structopt"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40b9788f4202aa75c240ecc9c15c65185e6a39ccdeb0fd5d008b98825464c87c"
dependencies = [
"clap",
"lazy_static",
"structopt-derive",
]
[[package]]
name = "structopt-derive"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "unicode-segmentation"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
[[package]]
name = "unicode-width"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]

View File

@ -1,14 +0,0 @@
[package]
name = "commenter"
version = "0.2.0"
authors = ["Adam Bergmark <adam@bergmark.nl>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
lazy-regex = "2.2.1"
regex = "1.5.4"
serde = { version = "1.0.133", features = ["derive"] }
serde_yaml = "0.8.23"
structopt = "0.3.25"

View File

@ -1,3 +0,0 @@
Helps automate mass-disabling of packages in Stackage's build-constraint.yaml.
See CURATORS.md for usage instructions.

View File

@ -1,2 +0,0 @@
#!/bin/bash
cargo run -- $@

View File

@ -1,30 +0,0 @@
Copyright Adam Bergmark (c) 2021
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Author name here nor the names of other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1 +0,0 @@
# latest-version

View File

@ -1,2 +0,0 @@
import Distribution.Simple
main = defaultMain

View File

@ -1,6 +0,0 @@
source-repository-package
type: git
location: git://github.com/commercialhaskell/pantry.git
packages: ./latest-version.cabal
with-compiler: ghc-8.10.7

View File

@ -1,23 +0,0 @@
name: latest-version
version: 0.1.0.0
homepage: https://github.com/githubuser/latest-version#readme
license: BSD3
license-file: LICENSE
author: Adam Bergmark
maintainer: adam@bergmark.nl
copyright: 2021 Adam Bergmark
category: Web
build-type: Simple
cabal-version: >=1.10
extra-source-files: README.md
executable latest-version
ghc-options: -Wall -threaded
hs-source-dirs: src
main-is: Main.hs
default-language: Haskell2010
build-depends: base >= 4.7 && < 5
, pantry
, Cabal
, rio
, containers

View File

@ -1,28 +0,0 @@
module Main where
import Data.List
import Distribution.Types.PackageName
import Distribution.Types.Version
import Pantry
import RIO
import System.Environment
import qualified Data.Map as Map
main :: IO ()
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

View File

@ -1,4 +0,0 @@
resolver:
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/18.yaml
packages:
- .

View File

@ -1,13 +0,0 @@
# 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

View File

@ -1,574 +0,0 @@
use std::collections::{BTreeMap, BTreeSet};
use std::error::Error;
use std::fmt;
use std::fs::File;
use std::io::{BufRead, BufReader, LineWriter, Lines, Write};
use std::path::Path;
use std::process::Command;
use lazy_regex::regex;
use serde::{Deserialize, Deserializer};
pub fn clear() {
handle(true, |loc, _lines| match loc {
// Add empty array to keep yaml valid
Location::Lib => vec![" []".to_owned()],
Location::Test | Location::Bench => vec![],
});
}
pub fn add(lib: Vec<String>, test: Vec<String>, bench: Vec<String>) {
handle(true, |loc, mut lines| {
lines.extend(match loc {
Location::Lib => lib.clone(),
Location::Test => test.clone(),
Location::Bench => bench.clone(),
});
lines.sort();
lines
});
}
enum VersionTag {
Manual(Version),
Auto(Version),
}
impl VersionTag {
fn tag(&self) -> &'static str {
match self {
VersionTag::Manual(_) => "manual",
VersionTag::Auto(_) => "auto",
}
}
fn version(&self) -> &Version {
match self {
VersionTag::Manual(s) => s,
VersionTag::Auto(s) => s,
}
}
}
pub fn outdated() {
let mut all: Vec<String> = vec![];
let (versioned, disabled) = handle(false, |_loc, lines| {
all.extend(lines);
vec![]
});
for DisabledPackage { package } in disabled {
println!("WARN: {package} is disabled without a noted version");
}
let mut map: BTreeMap<Package, VersionTag> = BTreeMap::new();
for VersionedPackage { package, version } in versioned {
map.insert(package, VersionTag::Manual(version));
}
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 = 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 = 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 latest_versions = {
let mut packages: Vec<Package> = 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;
}
let latest = latest_versions.get(&package).unwrap();
if version.version() != latest {
println!(
"{package} mismatch, {tag}: {version}, hackage: {latest}",
tag = version.tag(),
version = version.version(),
);
}
}
for ((package, version), dependents) in support {
if is_boot(&package) {
continue;
}
let latest = latest_versions.get(&package).unwrap();
if &version != latest {
let max = 3;
let dependents_stripped = dependents.len().saturating_sub(max);
let dependents = dependents
.into_iter()
.take(max)
.map(|(p, v)| format!("{p}-{v}"))
.collect::<Vec<String>>()
.join(", ");
let dependents = if dependents_stripped > 0 {
format!("{dependents} and {dependents_stripped} more")
} else {
dependents
};
println!(
"{package} mismatch, snapshot: {version}, hackage: {latest}, dependents: {dependents}"
);
}
}
}
fn is_boot(package: &Package) -> bool {
[
"Cabal",
"base",
"bytestring",
"containers",
"containers",
"directory",
"filepath",
"deepseq",
"ghc",
"ghc-bignum",
"ghc-boot",
"ghc-boot-th",
"ghc-prim",
"ghc-lib-parser", // not a boot lib, but tied to the GHC version.
"integer-gmp",
"process",
"stm",
"template-haskell",
"text",
"time",
]
.contains(&&*package.0)
}
fn latest_version(packages: impl Iterator<Item = Package>) -> BTreeMap<Package, Version> {
String::from_utf8(
Command::new("latest-version")
.args(packages.map(|p| p.0))
.output()
.expect("Could not find latest-version in PATH")
.stdout,
)
.unwrap()
.trim()
.to_owned()
.lines()
.map(|s| {
let VersionedPackage { package, version } = parse_versioned_package_canonical(s).unwrap();
(package, version)
})
.collect()
}
enum State {
LookingForLibBounds,
ProcessingLibBounds,
LookingForTestBounds,
ProcessingTestBounds,
LookingForBenchBounds,
ProcessingBenchBounds,
Done,
}
#[derive(PartialEq, Eq, Debug)]
struct VersionedPackage {
package: Package,
version: Version,
}
fn parse_versioned_package_canonical(s: &str) -> Option<VersionedPackage> {
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 parse_versioned_package_yaml(s: &str) -> Option<VersionedPackage> {
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<DisabledPackage> {
if !regex!(r#"- *([^ ]+) < *0 *# tried"#).is_match(s) {
if let Some(caps) = regex!(r#"- *([^ ]+) < *0 *# *\d*[^\d ]"#).captures(s) {
let package = caps.get(1).unwrap().as_str().to_owned();
Some(DisabledPackage { package })
} else {
None
}
} else {
None
}
}
fn handle<F>(write: bool, mut f: F) -> (Vec<VersionedPackage>, Vec<DisabledPackage>)
where
F: FnMut(Location, Vec<String>) -> Vec<String>,
{
let path = "build-constraints.yaml";
let mut new_lines: Vec<String> = vec![];
let mut versioned_packages: Vec<VersionedPackage> = vec![];
let mut disabled_packages: Vec<DisabledPackage> = 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_yaml(&line) {
versioned_packages.push(versioned_package);
} else if let Some(disabled_package) = parse_disabled_package(&line) {
disabled_packages.push(disabled_package);
}
match state {
State::LookingForLibBounds => {
if line == r#" "Library and exe bounds failures":"# {
state = State::ProcessingLibBounds;
}
new_lines.push(line);
}
State::ProcessingLibBounds => {
if line == r#" # End of Library and exe bounds failures"# {
new_lines.extend(f(Location::Lib, buf).into_iter());
buf = vec![];
new_lines.push(line);
state = State::LookingForTestBounds;
} else {
// Remove empty section
if line != " []" {
buf.push(line);
}
}
}
State::LookingForTestBounds => {
if line == r#" # Test bounds issues"# {
state = State::ProcessingTestBounds;
}
new_lines.push(line);
}
State::ProcessingTestBounds => {
if line == r#" # End of Test bounds issues"# {
new_lines.extend(f(Location::Test, buf).into_iter());
buf = vec![];
new_lines.push(line);
state = State::LookingForBenchBounds;
} else {
buf.push(line);
}
}
State::LookingForBenchBounds => {
if line == r#" # Benchmark bounds issues"# {
state = State::ProcessingBenchBounds;
}
new_lines.push(line);
}
State::ProcessingBenchBounds => {
if line == r#" # End of Benchmark bounds issues"# {
new_lines.extend(f(Location::Bench, buf).into_iter());
buf = vec![];
new_lines.push(line);
state = State::Done;
} else {
buf.push(line);
}
}
State::Done => {
new_lines.push(line);
}
}
}
if write {
let file = File::create(path).unwrap();
let mut file = LineWriter::new(file);
for line in new_lines {
file.write_all((line + "\n").as_bytes()).unwrap();
}
file.flush().unwrap();
}
(versioned_packages, disabled_packages)
}
enum Location {
Lib,
Test,
Bench,
}
fn read_lines<P>(filename: P) -> Lines<BufReader<File>>
where
P: AsRef<Path>,
{
let file = File::open(filename).unwrap();
BufReader::new(file).lines()
}
#[derive(Deserialize)]
struct SnapshotYaml {
// flags: BTreeMap<Package, BTreeMap<PackageFlag, bool>>,
// publish_time
packages: Vec<SnapshotPackage>,
// hidden
// resolver
}
#[derive(Deserialize)]
struct SnapshotPackage {
hackage: PackageWithVersionAndSha,
// pantry-tree
}
#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Debug)]
struct Package(String);
impl fmt::Display for Package {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Debug)]
struct Version(String);
impl fmt::Display for Version {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
// zstd-0.1.3.0@sha256:4c0a372251068eb6086b8c3a0a9f347488f08b570a7705844ffeb2c720c97223,3723
struct PackageWithVersionAndSha {
name: Package,
version: Version,
}
impl<'de> serde::Deserialize<'de> for PackageWithVersionAndSha {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s: String = String::deserialize(deserializer)?;
let r = regex!(r#"^(.+?)-([.\d]+)@sha256:[\da-z]+,\d+$"#);
if let Some(caps) = r.captures(&s) {
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 {
Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Other(&s),
&"Invalid PackageVersionWithSha",
))
}
}
}
fn yaml_from_file<A, P: AsRef<Path>>(path: P) -> Result<A, Box<dyn Error>>
where
A: for<'de> Deserialize<'de>,
{
let file = File::open(path)?;
let reader = BufReader::new(file);
let u = serde_yaml::from_reader(reader)?;
Ok(u)
}
struct Snapshot {
packages: BTreeMap<Package, Diff<Version>>,
}
#[derive(Clone, Copy)]
enum Diff<A> {
Left(A),
Right(A),
Both(A, A),
}
fn to_diff(a: SnapshotYaml, b: SnapshotYaml) -> Snapshot {
let mut packages = BTreeMap::new();
for s in a.packages {
let package = s.hackage;
packages.insert(package.name, Diff::Left(package.version));
}
for s in b.packages {
let package = s.hackage;
let name = package.name;
let version = package.version;
if let Some(a) = packages.remove(&name) {
match a {
Diff::Left(a) => {
if a == version {
packages.remove(&name);
} else {
packages.insert(name, Diff::Both(a, version));
}
}
_ => unreachable!(),
}
} else {
packages.insert(name, Diff::Right(version));
}
}
Snapshot { packages }
}
pub fn diff_snapshot(a: String, b: String) {
let diff = to_diff(yaml_from_file(a).unwrap(), yaml_from_file(b).unwrap());
for (name, diff) in diff.packages {
let s = match diff {
Diff::Left(a) => format!("- {name}-{a}"),
Diff::Right(b) => format!("+ {name}-{b}"),
Diff::Both(a, b) => format!("~ {name}-{a} -> {b}"),
};
println!("{s}");
}
}
#[derive(PartialEq, Eq, Debug)]
struct DisabledTransitively {
child: VersionedPackage,
parent: Package,
}
fn parse_disabled_transitviely(s: &str) -> Option<DisabledTransitively> {
let r = regex!(
r#"- *([^ ]+) < *0 *# tried [^ ]+-([\d.]+), but its \*[^*]+\* requires the disabled package: ([^ ]+)"#
);
if let Some(caps) = r.captures(s) {
let package = Package(caps.get(1).unwrap().as_str().to_owned());
let version = Version(caps.get(2).unwrap().as_str().to_owned());
let parent = Package(caps.get(3).unwrap().as_str().to_owned());
Some(DisabledTransitively {
child: VersionedPackage { package, version },
parent,
})
} else {
None
}
}
#[test]
fn test_parse_disabled_transitviely() {
let s = "- Network-NineP < 0 # tried Network-NineP-0.4.7.1, but its *library* requires the disabled package: mstate";
assert_eq!(
parse_disabled_transitviely(s),
Some(DisabledTransitively {
child: VersionedPackage {
package: Package("Network-NineP".to_owned()),
version: Version("0.4.7.1".to_owned())
},
parent: Package("mstate".to_owned()),
})
)
}
type M = BTreeMap<Package, (Vec<VersionedPackage>, Option<usize>)>;
pub fn disabled() {
let mut disabled_transitively: Vec<DisabledTransitively> = vec![];
handle(false, |loc, lines| {
match loc {
Location::Lib => disabled_transitively.extend(
lines
.into_iter()
.map(|line| parse_disabled_transitviely(&line))
.flatten()
.collect::<Vec<_>>(),
),
Location::Test | Location::Bench => (),
}
vec![]
});
let mut packages: BTreeSet<Package> = BTreeSet::new();
let mut disabled: M = BTreeMap::new();
for DisabledTransitively { child, parent } in disabled_transitively {
packages.insert(child.package.clone());
packages.insert(parent.clone());
disabled
.entry(child.package.clone())
.or_insert_with(|| (vec![], None));
let t = disabled.entry(parent).or_insert_with(|| (vec![], None));
t.0.push(child);
}
let mut packages_len = packages.len();
while packages_len > 0 {
let mut new_packages: BTreeSet<Package> = BTreeSet::new();
for package in packages {
let (_, count) = disabled.get(&package).unwrap();
if count.is_none() && !process(&package, &mut disabled) {
new_packages.insert(package.clone());
}
}
packages = new_packages;
packages_len = packages.len();
}
let mut v: Vec<_> = disabled
.into_iter()
.map(|(package, (_, count))| (count, package))
.collect();
v.sort();
for (count, package) in v {
let count = count.unwrap();
if count != 0 {
println!("{package} is disabled with {count} dependents");
}
}
}
fn process(package: &Package, m: &mut M) -> bool {
let (children, count) = m.get(package).unwrap();
assert!(count.is_none(), "{:?}", package);
let mut count = 0;
for child in children {
let (_, child_count) = m
.get(&child.package)
.unwrap_or_else(|| panic!("{}", child.package));
match child_count {
None => return false,
Some(i) => count += 1 + i,
}
}
m.entry(package.clone())
.and_modify(|tup| tup.1 = Some(count))
.or_insert_with(|| panic!("{}", package));
true
}

View File

@ -1,231 +0,0 @@
use std::collections::HashMap;
use std::io::{self, BufRead};
use lazy_regex::regex;
use regex::Regex;
use structopt::StructOpt;
type H = HashMap<Header, Vec<(String, String, String)>>;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum Header {
Versioned { package: String, version: String },
Missing { package: String },
}
#[derive(Debug, StructOpt)]
#[structopt(
name = "commenter",
about = "Automates generation of bounds in build-constraints.yaml"
)]
enum Opt {
Add,
Clear,
DiffSnapshot { a: String, b: String },
Disabled,
Outdated,
}
fn main() {
let opt = Opt::from_args();
match opt {
Opt::Add => add(),
Opt::Clear => commenter::clear(),
Opt::DiffSnapshot { a, b } => commenter::diff_snapshot(a, b),
Opt::Disabled => commenter::disabled(),
Opt::Outdated => commenter::outdated(),
}
}
#[test]
fn test_package_with_digit_after_dash() {
let line = "- [ ] captcha-2captcha-0.1.0.0 (==0.1.*). Edward Yang <qwbarch@gmail.com> @qwbarch. @qwbarch. Used by: library";
let p = parse_package_with_component(line).unwrap();
assert_eq!(
p,
PackageWithComponent {
package: "captcha-2captcha",
version: "0.1.0.0",
component: "library",
}
);
}
#[derive(Debug, PartialEq, Eq)]
struct PackageWithComponent<'a> {
package: &'a str,
version: &'a str,
component: &'a str,
}
fn parse_package_with_component(s: &str) -> Option<PackageWithComponent> {
let package = regex!(
r#"^- \[ \] (?P<package>[0-9a-zA-z][a-zA-Z]([a-zA-z0-9.-]*?))-(?P<version>(\d+(\.\d+)*)) \(.+?Used by: (?P<component>.+)$"#
);
if let Some(cap) = package.captures(s) {
let package = cap.name("package").unwrap().as_str();
let version = cap.name("version").unwrap().as_str();
let component = cap.name("component").unwrap().as_str();
Some(PackageWithComponent {
package,
version,
component,
})
} else {
None
}
}
#[test]
fn test_parse_header_versioned() {
let s = "aeson-2.0.3.0 ([changelog](http://hackage.haskell.org/package/aeson-2.0.3.0/changelog)) (Adam Bergmark <adam@bergmark.nl> @bergmark, Stackage upper bounds) is out of bounds for:";
let p = parse_header_versioned(s).unwrap();
assert_eq!(
p,
Header::Versioned {
package: "aeson".to_owned(),
version: "2.0.3.0".to_owned()
}
)
}
fn parse_header_versioned(s: &str) -> Option<Header> {
let header_versioned = regex!(
r#"^(?P<package>[\da-zA-z][a-zA-Z]([a-zA-z0-9.-]*?))-(?P<version>(\d+(\.\d+)*)).+?is out of bounds for:$"#
);
if let Some(cap) = header_versioned.captures(s) {
let package = cap.name("package").unwrap().as_str().to_owned();
let version = cap.name("version").unwrap().as_str().to_owned();
Some(Header::Versioned { package, version })
} else {
None
}
}
fn add() {
let mut lib_exes: H = Default::default();
let mut tests: H = Default::default();
let mut benches: H = Default::default();
let mut last_header: Option<Header> = None;
let header_missing =
regex!(r#"^(?P<package>[\da-zA-z][a-zA-Z]([a-zA-z0-9.-]*)).+?depended on by:$"#);
// Ignore everything until the bounds issues show up.
let mut process_line = false;
for line in io::stdin().lock().lines().flatten() {
if is_reg_match(&line, regex!(r#"^\s*$"#)) {
// noop
} else if line == "curator: Snapshot dependency graph contains errors:" {
process_line = true;
} else if !process_line {
println!("[INFO] {line}");
} else if let Some(PackageWithComponent {
package,
version,
component,
}) = parse_package_with_component(&line)
{
let root = last_header.clone().unwrap();
match component {
"library" | "executable" => {
insert(&mut lib_exes, root, package, version, component)
}
"benchmark" => insert(&mut benches, root, package, version, "benchmarks"),
"test-suite" => insert(&mut tests, root, package, version, component),
_ => panic!("Bad component: {}", component),
}
} else if let Some(header_versioned) = parse_header_versioned(&line) {
last_header = Some(header_versioned);
} else if let Some(cap) = header_missing.captures(&line) {
let package = cap.name("package").unwrap().as_str().to_owned();
last_header = Some(Header::Missing { package });
} else {
panic!("Unhandled: {:?}", line);
}
}
let mut auto_lib_exes = vec![];
let mut auto_tests = vec![];
let mut auto_benches = vec![];
if !lib_exes.is_empty() {
println!("\nLIBS + EXES\n");
}
for (header, packages) in lib_exes {
for (package, version, component) in packages {
let s = printer(" ", &package, true, &version, &component, &header);
println!("{s}");
auto_lib_exes.push(s);
}
}
if !tests.is_empty() {
println!("\nTESTS\n");
}
for (header, packages) in tests {
for (package, version, component) in packages {
let s = printer(" ", &package, false, &version, &component, &header);
println!("{s}");
auto_tests.push(s);
}
}
if !benches.is_empty() {
println!("\nBENCHMARKS\n");
}
for (header, packages) in benches {
for (package, version, component) in packages {
let s = printer(" ", &package, false, &version, &component, &header);
println!("{s}");
auto_benches.push(s);
}
}
println!();
println!(
"Adding {lib_exes} libs, {tests} tests, {benches} benches to build-constraints.yaml",
lib_exes = auto_lib_exes.len(),
tests = auto_tests.len(),
benches = auto_benches.len()
);
commenter::add(auto_lib_exes, auto_tests, auto_benches);
}
fn printer(
indent: &str,
package: &str,
lt0: bool,
version: &str,
component: &str,
header: &Header,
) -> String {
let lt0 = if lt0 { " < 0" } else { "" };
format!(
"{indent}- {package}{lt0} # tried {package}-{version}, but its *{component}* {cause}",
cause = match header {
Header::Versioned { package, version } => format!(
"does not support: {package}-{version}",
package = package,
version = version
),
Header::Missing { package } => format!(
"requires the disabled package: {package}",
package = package
),
},
)
}
fn insert(h: &mut H, header: Header, package: &str, version: &str, component: &str) {
(*h.entry(header).or_insert_with(Vec::new)).push((
package.to_owned(),
version.to_owned(),
component.to_owned(),
));
}
fn is_reg_match(s: &str, r: &Regex) -> bool {
r.captures(s).is_some()
}