diff --git a/Cargo.lock b/Cargo.lock index 1296645..dbaf994 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "ahash" version = "0.7.6" @@ -55,6 +70,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.13.0" @@ -100,6 +130,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + [[package]] name = "cfg-if" version = "1.0.0" @@ -264,6 +300,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" + [[package]] name = "h2" version = "0.3.13" @@ -398,6 +440,12 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" +[[package]] +name = "is_ci" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" + [[package]] name = "iso8601" version = "0.4.2" @@ -457,6 +505,7 @@ version = "0.1.0" dependencies = [ "clap 3.2.14", "jsonschema", + "miette", "serde", "serde_json", "serde_yaml", @@ -514,6 +563,37 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "miette" +version = "5.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d67f6972a70e33dbb5551875c6a3e46ae0a7cddd4661a2811ee48be51054e9" +dependencies = [ + "atty", + "backtrace", + "miette-derive", + "once_cell", + "owo-colors", + "supports-color", + "supports-hyperlinks", + "supports-unicode", + "terminal_size", + "textwrap 0.15.0", + "thiserror", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "5.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "426594bc7266dedee4d687cdaebc121c74c52a667e4ce933c83694ad035990a6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "mime" version = "0.3.16" @@ -526,6 +606,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +dependencies = [ + "adler", +] + [[package]] name = "mio" version = "0.8.4" @@ -650,6 +739,15 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.12.0" @@ -662,6 +760,12 @@ version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" +[[package]] +name = "owo-colors" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "decf7381921fea4dcb2549c5667eda59b3ec297ab7e2b5fc33eac69d2e7da87b" + [[package]] name = "parking_lot" version = "0.12.1" @@ -805,6 +909,12 @@ dependencies = [ "winreg", ] +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + [[package]] name = "ryu" version = "1.0.10" @@ -893,6 +1003,12 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" +[[package]] +name = "smawk" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043" + [[package]] name = "socket2" version = "0.4.4" @@ -939,6 +1055,34 @@ dependencies = [ "syn", ] +[[package]] +name = "supports-color" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4872ced36b91d47bae8a214a683fe54e7078875b399dfa251df346c9b547d1f9" +dependencies = [ + "atty", + "is_ci", +] + +[[package]] +name = "supports-hyperlinks" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "590b34f7c5f01ecc9d78dba4b3f445f31df750a67621cf31626f3b7441ce6406" +dependencies = [ + "atty", +] + +[[package]] +name = "supports-unicode" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8b945e45b417b125a8ec51f1b7df2f8df7920367700d1f98aedd21e5735f8b2" +dependencies = [ + "atty", +] + [[package]] name = "syn" version = "1.0.98" @@ -959,6 +1103,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal_size" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -973,6 +1127,11 @@ name = "textwrap" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] [[package]] name = "thiserror" @@ -1101,6 +1260,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +[[package]] +name = "unicode-linebreak" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a52dcaab0c48d931f7cc8ef826fa51690a08e1ea55117ef26f89864f532383f" +dependencies = [ + "regex", +] + [[package]] name = "unicode-normalization" version = "0.1.19" diff --git a/data/sidebar.yml b/data/sidebar.yml index 1f37069..fa21984 100644 --- a/data/sidebar.yml +++ b/data/sidebar.yml @@ -16,9 +16,37 @@ solutions: references: breakage: - - https://github.com/webcompat/web-bugs/issues/11622 - - https://github.com/webcompat/web-bugs/issues/55685 - - https://github.com/webcompat/web-bugs/issues/67848 - - https://github.com/webcompat/web-bugs/issues/106830 + - url: https://github.com/webcompat/web-bugs/issues/11622 + site: http://www.election.gov.np + platform: + - all + intervention: https://github.com/mozilla-extensions/webcompat-addon/blob/4a3094f52d561925620ac441d5c4423766ec5c29/src/webextension/injections/js/bug1472081-election.gov.np-window.sidebar-shim.js + impact: feature_broken + affects_users: all + resolution: site_changed + - url: https://github.com/webcompat/web-bugs/issues/55685 + site: http://www.susu09.com/forum.php + platform: + - all + last_reproduced: 2020-07-27 + impact: site_broken + affects_users: all + resolution: site_fixed + - url: https://github.com/webcompat/web-bugs/issues/67848 + site: https://indichords.com/ + platform: + - all + last_reproduced: 2021-03-08 + impact: feature_broken + affects_users: all + resolution: site_fixed + - url: https://github.com/webcompat/web-bugs/issues/106830 + site: https://www.pontefractfhs.org.uk + platform: + - all + last_reproduced: 2022-07-09 + impact: site_broken + affects_users: all + resolution: site_fixed platform_issues: - https://bugzilla.mozilla.org/show_bug.cgi?id=1428302 diff --git a/schemas/entry.schema.json b/schemas/entry.schema.json index a229f8e..b4094c7 100644 --- a/schemas/entry.schema.json +++ b/schemas/entry.schema.json @@ -80,11 +80,68 @@ "additionalProperties": false, "properties": { "breakage": { - "description": "List of URLs to issues where actual site breakage is tracked. If available, point the link directly to a comment with clear STR", + "description": "List of issues tracking known site breakage.", "type": "array", "items": { - "type": "string", - "format": "uri" + "anyOf": [ + { + "type": "string", + "format": "uri", + "description": "URL of tracking issue" + }, + { + "type": "object", + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "URL of tracking issue" + }, + "site": { + "type": "string", + "format": "uri", + "description": "URL of broken site or page" + }, + "platform": { + "type": "array", + "items": { + "type": "string", + "enum": ["all", "desktop", "mobile", "windows", "macos", "linux"], + "description": "List of affected platforms. Default is 'all'" + } + }, + "last_reproduced": { + "type": "string", + "format": "date", + "description": "Most recent date the issue was successfully reproduced" + }, + "intervention": { + "type": "string", + "format": "uri", + "description": "URL of intervention that is shipping or has been shipped. Link to the code in the GitHub repository, and use the canonical URLs to ensure persistance over time" + }, + "impact": { + "type": "string", + "enum": ["site_broken", "feature_broken", "significant_visual", "minor_visual", "unsupported_message"], + "description": "Type of breakage" + }, + "affects_users": { + "type": "string", + "enum": ["all", "some", "few"], + "description": "What fraction of users are affected. 'all' where any site user is likely to run into the issue, 'some' for issues that are common but many users will not experience, and 'few' where the breakage depends on an unusual configuration or similar." + }, + "resolution": { + "type": "string", + "enum": ["site_changed", "site_fixed"], + "description": "If the issue no longer reproduces on this site, the kind of change that happened. 'site_change' if there was a general redesign or the site is no longer online, 'site_fixed' if the specific issue was patched." + }, + "notes": { + "type": "string", + "description": "Any additional notes about why the other fields for this issue are set to the given values." + } + } + } + ] } }, "platform_issues": { diff --git a/tools/kbcheck/Cargo.toml b/tools/kbcheck/Cargo.toml index 7261639..aedbaad 100644 --- a/tools/kbcheck/Cargo.toml +++ b/tools/kbcheck/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] jsonschema="0.16" +miette={ version="5", features = ["fancy"] } serde="1" serde_json="1" serde_yaml="0.8" diff --git a/tools/kbcheck/src/data.rs b/tools/kbcheck/src/data.rs index 981887f..b6cdcd7 100644 --- a/tools/kbcheck/src/data.rs +++ b/tools/kbcheck/src/data.rs @@ -1,4 +1,5 @@ use crate::entry::Entry; +use miette::Diagnostic; use std::collections::BTreeMap; use std::fs::File; use std::io; @@ -9,32 +10,16 @@ use walkdir::{self, DirEntry, WalkDir}; pub type EntriesMap = BTreeMap; -#[derive(thiserror::Error, Debug)] +#[derive(thiserror::Error, Debug, Diagnostic)] pub enum DataError { - #[error("Could not open file")] - IoError( - #[from] - #[source] - io::Error, - ), - #[error("JSON parsing failed")] - JsonParseError( - #[from] - #[source] - serde_json::Error, - ), - #[error("Loading YAML failed")] - YamlLoadError( - #[from] - #[source] - serde_yaml::Error, - ), - #[error("JSON parsing failed")] - WalkDirError( - #[from] - #[source] - walkdir::Error, - ), + #[error(transparent)] + IoError(#[from] io::Error), + #[error(transparent)] + JsonParseError(#[from] serde_json::Error), + #[error(transparent)] + YamlLoadError(#[from] serde_yaml::Error), + #[error(transparent)] + WalkDirError(#[from] walkdir::Error), } /// Read a path into a serde_json::Value diff --git a/tools/kbcheck/src/entry.rs b/tools/kbcheck/src/entry.rs index c38670a..b3d4c80 100644 --- a/tools/kbcheck/src/entry.rs +++ b/tools/kbcheck/src/entry.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum Severity { Critical, @@ -10,7 +10,7 @@ pub enum Severity { Low, } -#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum UserBaseImpact { Large, @@ -23,18 +23,18 @@ pub enum UserBaseImpact { #[serde(deny_unknown_fields)] pub struct Solutions { #[serde(default)] - interventions: Vec, + pub interventions: Vec, #[serde(default)] - notes: Vec, + pub notes: Vec, #[serde(default)] - workarounds: Vec, + pub workarounds: Vec, } #[derive(Debug, Serialize, Deserialize, Default)] #[serde(deny_unknown_fields)] pub struct References { #[serde(default)] - pub breakage: Vec, + pub breakage: Vec, #[serde(default)] pub platform_issues: Vec, #[serde(default)] @@ -47,6 +47,73 @@ pub struct References { pub standards_discussions: Vec, } +#[derive(Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum BreakageItem { + Url(Url), + Breakage(Breakage), +} + +impl BreakageItem { + pub fn url(&self) -> &Url { + match self { + BreakageItem::Url(url) => url, + BreakageItem::Breakage(item) => &item.url, + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Breakage { + pub url: Url, + pub site: Url, + pub platform: Vec, + pub last_reproduced: Option, // Date + pub intervention: Option, + pub impact: Impact, + pub affects_users: AffectsUsers, + pub resolution: Option, + #[serde(default)] + pub notes: String, +} + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum Platform { + All, + Desktop, + Mobile, + Windows, + Macos, + Linux, +} + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum Impact { + SiteBroken, + FeatureBroken, + SignificantVisual, + MinorVisual, + UnsupportedMessage, +} + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum AffectsUsers { + All, + Some, + Few, +} + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum BreakageResolution { + SiteChanged, + SiteFixed, +} + #[derive(Debug, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct Entry { diff --git a/tools/kbcheck/src/main.rs b/tools/kbcheck/src/main.rs index 639fe7a..212c8f3 100644 --- a/tools/kbcheck/src/main.rs +++ b/tools/kbcheck/src/main.rs @@ -1,5 +1,6 @@ use clap::{Parser, Subcommand}; use kbcheck::{data, validate}; +use miette::{Diagnostic, Result}; use std::collections::BTreeMap; use std::path::{Path, PathBuf}; @@ -21,13 +22,7 @@ enum Commands { Validate, } -enum CommandStatus { - Ok, - Failure, - UnexpectedError, -} - -fn get_tags(root_path: &Path) -> Result, data::DataError> { +fn get_tags(root_path: &Path) -> Result> { let mut tags = BTreeMap::new(); for entry in data::load_all(root_path)?.values() { for tag in entry.tags.iter() { @@ -41,57 +36,42 @@ fn get_tags(root_path: &Path) -> Result, data::D Ok(tags) } -fn tags(root_path: &Path) -> CommandStatus { - match get_tags(root_path) { - Ok(tags) => { - let mut all_tags = tags.values().collect::>(); - all_tags.sort(); - for (name, count) in all_tags.iter() { - println!("{}:{}", name, count); - } - CommandStatus::Ok - } - Err(err) => { - println!("{}", err); - CommandStatus::UnexpectedError - } +fn tags(root_path: &Path) -> Result<()> { + let tags = get_tags(root_path)?; + let mut all_tags = tags.values().collect::>(); + all_tags.sort(); + for (name, count) in all_tags.iter() { + println!("{}:{}", name, count); } + Ok(()) } -fn validate(root_path: &Path) -> CommandStatus { - match validate::validate(root_path) { - Ok(errors) => { - if !errors.is_empty() { - println!("Validation failed"); - for err in errors { - println!("{}", err); - } - CommandStatus::Failure - } else { - CommandStatus::Ok - } - } - Err(err) => { +#[derive(thiserror::Error, Debug, Diagnostic)] +pub enum ValidateError { + #[error("Validation failed")] + ValidationFailed, + #[error(transparent)] + UnexpectedError(#[from] validate::ValidateError), +} + +fn validate(root_path: &Path) -> Result<()> { + let errors = validate::validate(root_path)?; + if !errors.is_empty() { + for err in errors { println!("{}", err); - CommandStatus::UnexpectedError } + Err(ValidateError::ValidationFailed.into()) + } else { + Ok(()) } } -fn run() -> CommandStatus { +fn main() -> Result<()> { let cli = Cli::parse(); let root_path = cli.root_path.unwrap_or_default(); match &cli.command { Commands::Tags => tags(&root_path), Commands::Validate => validate(&root_path), - } -} - -fn main() { - let exit_code = match run() { - CommandStatus::Ok => 0, - CommandStatus::Failure => 1, - CommandStatus::UnexpectedError => 2, - }; - std::process::exit(exit_code); + }?; + Ok(()) } diff --git a/tools/kbcheck/src/validate.rs b/tools/kbcheck/src/validate.rs index 6217416..2dbe144 100644 --- a/tools/kbcheck/src/validate.rs +++ b/tools/kbcheck/src/validate.rs @@ -1,6 +1,7 @@ use crate::data::{iter_data_files, read_json, EntriesMap}; use crate::entry::Entry; use jsonschema::JSONSchema; +use miette::Diagnostic; use serde::de::{Deserialize, IntoDeserializer}; use std::collections::BTreeMap; use std::fmt::Display; @@ -11,14 +12,10 @@ use url::Url; pub type Failures = Vec; -#[derive(Error, Debug)] -enum ValidateError { - #[error("Load failed")] - DataError( - #[from] - #[source] - crate::data::DataError, - ), +#[derive(Error, Debug, Diagnostic)] +pub enum ValidateError { + #[error(transparent)] + DataError(#[from] crate::data::DataError), #[error("Schema compile failed")] SchemaError, } @@ -183,7 +180,7 @@ fn global_validate(entries: EntriesMap) -> Failures { } /// Validate knowledge base entries -pub fn validate(root_path: &Path) -> Result> { +pub fn validate(root_path: &Path) -> Result { let mut errors: Failures = Vec::new(); let (entries, file_errors) = load_and_validate_files(root_path)?; errors.extend(file_errors.into_iter());