From c5bb84724f789f12fec6791d3c70693b63261995 Mon Sep 17 00:00:00 2001 From: Cameron Bytheway Date: Tue, 21 Jan 2025 15:08:44 -0700 Subject: [PATCH] feat: add init command (#154) --- CHANGELOG.md | 2 +- Cargo.toml | 7 +- README.md | 69 +++-- _typos.toml | 2 + duvet-core/src/diff.rs | 3 + duvet/Cargo.toml | 1 + duvet/src/config/schema.rs | 2 + duvet/src/init.rs | 249 ++++++++++++++++++ duvet/src/lib.rs | 6 + duvet/src/specification/ietf/tests.rs | 2 +- duvet/www/src/App.js | 3 + duvet/www/src/index.js | 3 + duvet/www/src/link.js | 3 + duvet/www/src/nav.js | 3 + duvet/www/src/result.js | 3 + duvet/www/src/section.js | 3 + duvet/www/src/spec.js | 3 + integration/init-c.toml | 26 ++ integration/init-java.toml | 28 ++ integration/init-python.toml | 25 ++ integration/init-quick-start.toml | 13 + integration/init-rust.toml | 25 ++ integration/snapshots/init-c.snap | 7 + integration/snapshots/init-c_json.snap | 3 + integration/snapshots/init-java.snap | 7 + integration/snapshots/init-java_json.snap | 3 + integration/snapshots/init-python.snap | 7 + integration/snapshots/init-python_json.snap | 3 + integration/snapshots/init-quick-start.snap | 38 +++ .../snapshots/init-quick-start_json.snap | 3 + integration/snapshots/init-rust.snap | 7 + integration/snapshots/init-rust_json.snap | 3 + xtask/src/args.rs | 3 + xtask/src/build.rs | 3 + xtask/src/changelog.rs | 3 + xtask/src/checks.rs | 53 +++- xtask/src/main.rs | 3 + xtask/src/publish.rs | 3 + xtask/src/tests.rs | 3 + 39 files changed, 598 insertions(+), 35 deletions(-) create mode 100644 _typos.toml create mode 100644 duvet/src/init.rs create mode 100644 integration/init-c.toml create mode 100644 integration/init-java.toml create mode 100644 integration/init-python.toml create mode 100644 integration/init-quick-start.toml create mode 100644 integration/init-rust.toml create mode 100644 integration/snapshots/init-c.snap create mode 100644 integration/snapshots/init-c_json.snap create mode 100644 integration/snapshots/init-java.snap create mode 100644 integration/snapshots/init-java_json.snap create mode 100644 integration/snapshots/init-python.snap create mode 100644 integration/snapshots/init-python_json.snap create mode 100644 integration/snapshots/init-quick-start.snap create mode 100644 integration/snapshots/init-quick-start_json.snap create mode 100644 integration/snapshots/init-rust.snap create mode 100644 integration/snapshots/init-rust_json.snap diff --git a/CHANGELOG.md b/CHANGELOG.md index 2beea2d0..b3c5364e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ ### Bug Fixes -* remove reduntant borrows ([#89](https://github.com/awslabs/duvet/issues/89)) ([0cfc8ce](https://github.com/awslabs/duvet/commit/0cfc8ce88a8a5183a68581fd5824498dbe4e376a)) +* remove redundant borrows ([#89](https://github.com/awslabs/duvet/issues/89)) ([0cfc8ce](https://github.com/awslabs/duvet/commit/0cfc8ce88a8a5183a68581fd5824498dbe4e376a)) * handle duplicate markdown section names ([#94](https://github.com/awslabs/duvet/issues/94)) ([5d31dd2](https://github.com/awslabs/duvet/commit/5d31dd21c05f5998b8a4e6c66e18552688a3e788)) ## 0.1.1 (2022-10-07) diff --git a/Cargo.toml b/Cargo.toml index c514c0b6..56c578a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,5 @@ [workspace] -members = [ - "duvet", - "duvet-core", - "duvet-macros", - "xtask", -] +members = ["duvet", "duvet-core", "duvet-macros", "xtask"] resolver = "2" [profile.bench] diff --git a/README.md b/README.md index 33f75a0f..f192b3ce 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,56 @@ -## Duvet +# Duvet -A code quality tool to help bound correctness. -By starting from a specification Duvet extracts every RFC 2119 requirement. -Duvet can then use this information to report on a code base. -Duvet can then report on every requirement, -where it is honored in source, -as well as how that source is tested. +Duvet is a tool that establishes a bidirectional link between implementation and specification. This practice is called [requirements traceability](https://en.wikipedia.org/wiki/Requirements_traceability), which is defined as: -## Support -This tool is still in beta. -Interfaces should be considered unstable and may change before the 1.0.0 release. +> the ability to describe and follow the life of a requirement in both a forwards and backwards direction (i.e., from its origins, through its development and specification, to its subsequent deployment and use, and through periods of ongoing refinement and iteration in any of these phases) -## Test -First run `make` in the main `duvet` directory to generate the necessary files. -``` -cargo test -``` +## Quick Start + +Before getting started, Duvet requires a [rust toolchain](https://www.rust-lang.org/tools/install). + +1. Install command + + ```console + $ cargo install duvet --locked + ``` + +2. Initialize repository + + In this example, we are using Rust. However, Duvet can be used with any language. + + ```console + $ duvet init --lang-rust --specification https://www.rfc-editor.org/rfc/rfc2324 + ``` + +3. Add a implementation comment in the project -## Build + ```rust + // src/lib.rs -If there are any changes to the JS -it will also need to be built. -In the `www` directory run `make build` + //= https://www.rfc-editor.org/rfc/rfc2324#section-2.1.1 + //# A coffee pot server MUST accept both the BREW and POST method + //# equivalently. + ``` -## Install +4. Generate a report + + ```console + $ duvet report + ``` + +## Development + +### Building + +```console +$ cargo xtask build +``` + +### Testing + +```console +$ cargo xtask test ``` -cargo +stable install --force --path . -```` ## Security @@ -35,4 +59,3 @@ See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more inform ## License This project is licensed under the Apache-2.0 License. - diff --git a/_typos.toml b/_typos.toml new file mode 100644 index 00000000..9893c7ee --- /dev/null +++ b/_typos.toml @@ -0,0 +1,2 @@ +[files] +extend-exclude = ["*.snap", "integration/snapshots/**", "specs/**"] diff --git a/duvet-core/src/diff.rs b/duvet-core/src/diff.rs index 141869f6..ec9e1e58 100644 --- a/duvet-core/src/diff.rs +++ b/duvet-core/src/diff.rs @@ -1,3 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + use console::{style, Style}; use similar::{udiff::UnifiedHunkHeader, Algorithm, ChangeTag, TextDiff}; use std::{ diff --git a/duvet/Cargo.toml b/duvet/Cargo.toml index c654046d..056aeb15 100644 --- a/duvet/Cargo.toml +++ b/duvet/Cargo.toml @@ -10,6 +10,7 @@ edition = "2021" license = "Apache-2.0" repository = "https://github.com/awslabs/duvet" include = ["/src/**/*.rs", "/www/public"] +default-run = "duvet" [dependencies] clap = { version = "4", features = ["derive"] } diff --git a/duvet/src/config/schema.rs b/duvet/src/config/schema.rs index d84c3503..597348d7 100644 --- a/duvet/src/config/schema.rs +++ b/duvet/src/config/schema.rs @@ -9,6 +9,8 @@ use serde::{de, Deserialize}; pub mod v0_4_0; +pub static DEFAULT: &str = "https://awslabs.github.io/duvet/config/v0.4.0.json"; + #[derive(Debug, Deserialize)] #[serde(tag = "$schema", deny_unknown_fields)] pub enum Schema { diff --git a/duvet/src/init.rs b/duvet/src/init.rs new file mode 100644 index 00000000..fcb72a40 --- /dev/null +++ b/duvet/src/init.rs @@ -0,0 +1,249 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use crate::Result; +use clap::Parser; +use duvet_core::{progress, vfs as fs}; +use std::{fs::File, io::Write, path::Path}; + +static GITIGNORE: &str = r#" +reports/ +"#; + +#[derive(Debug, Parser)] +pub struct Init { + #[clap(long)] + /// Include the given specification in the configuration + specification: Vec, + + #[clap(flatten)] + languages: Languages, +} + +impl Init { + pub async fn exec(&self) -> Result { + let mut languages = self.languages; + + if languages.is_empty() { + languages.detect().await; + } + + let dir = Path::new(".duvet"); + + std::fs::create_dir_all(dir)?; + + macro_rules! put { + ($path:expr, $writer:expr) => {{ + let path = dir.join($path); + let progress = progress!("Writing {}", path.display()); + if path.exists() { + progress!(progress, "Skipping {} - already exists", path.display()); + } else { + let mut out = File::create_new(&path)?; + let result: Result = ($writer)(&mut out); + result?; + out.flush()?; + drop(out); + + progress!(progress, "Wrote {}", path.display()); + } + }}; + } + + put!("config.toml", |out: &mut File| { + writeln!(out, "'$schema' = {:?}", crate::config::schema::DEFAULT)?; + writeln!(out)?; + + languages.write(out)?; + + if self.specification.is_empty() { + writeln!(out, "# Include required specifications here")?; + writeln!(out, "# [[specification]]")?; + writeln!( + out, + "# source = {:?}", + "https://www.rfc-editor.org/rfc/rfc2324" + )?; + } else { + for spec in &self.specification { + writeln!(out, "[[specification]]")?; + writeln!(out, "source = {:?}", spec)?; + } + } + + writeln!(out, "[report.html]")?; + writeln!(out, "enabled = true")?; + // TODO detect git repo + writeln!(out)?; + + writeln!( + out, + "# Enable snapshots to prevent requirement coverage regressions" + )?; + writeln!(out, "[report.snapshot]")?; + writeln!(out, "enabled = true")?; + + Ok(()) + }); + + put!(".gitignore", |out: &mut File| { + write!(out, "{}", GITIGNORE.trim_start())?; + Ok(()) + }); + + Ok(()) + } +} + +#[derive(Copy, Clone, Debug, Default, PartialEq, Parser)] +struct Languages { + /// Include rules for the C language + #[clap(long = "lang-c")] + c: bool, + /// Include rules for the Go language + #[clap(long = "lang-go")] + go: bool, + /// Include rules for the Java language + #[clap(long = "lang-java")] + java: bool, + /// Include rules for the JavaScript language + #[clap(long = "lang-javascript")] + javascript: bool, + /// Include rules for the Python language + #[clap(long = "lang-python")] + python: bool, + /// Include rules for the TypeScript language + #[clap(long = "lang-typescript")] + typescript: bool, + /// Include rules for the Ruby language + #[clap(long = "lang-ruby")] + ruby: bool, + /// Include rules for the Rust language + #[clap(long = "lang-rust")] + rust: bool, +} + +impl Languages { + fn is_empty(&self) -> bool { + Self::default().eq(self) + } + + fn write(&self, out: &mut O) -> Result { + if self.is_empty() { + writeln!(out, "# Specify source code patterns here")?; + writeln!(out, "# [[source]]")?; + writeln!(out, "# pattern = {:?}", "src/**/*.rs")?; + writeln!(out)?; + return Ok(()); + } + + macro_rules! write { + ($lang:ident) => { + if self.$lang { + lang::$lang(out)?; + } + }; + } + + write!(c); + write!(go); + write!(java); + write!(javascript); + write!(python); + write!(typescript); + write!(ruby); + write!(rust); + + Ok(()) + } + + async fn detect(&mut self) { + async fn check(path: &str) -> bool { + fs::read_metadata(path).await.is_ok() + } + + if check("CMakeLists.txt").await { + self.c = true; + } + + if check("go.mod").await { + self.go = true; + } + + if check("pom.xml").await || check("build.gradle").await || check("build.gradle.kts").await + { + self.java = true; + } + + if check("package.json").await { + self.javascript = true; + } + + if check("requirements.txt").await + || check("pyproject.toml").await + || check("setup.py").await + { + self.python = true; + } + + if check("tsconfig.json").await { + self.typescript = true; + } + + if check("Gemfile").await { + self.ruby = true; + } + + if check("Cargo.toml").await { + self.rust = true; + } + } +} + +mod lang { + use super::*; + + macro_rules! lang { + ($name:ident, $pattern:expr $(, $extra:expr)?) => { + pub fn $name(out: &mut O) -> Result { + writeln!(out, "[[source]]")?; + writeln!(out, "pattern = {:?}", $pattern)?; + $( + writeln!(out, "{}", $extra)?; + )? + writeln!(out)?; + Ok(()) + } + }; + } + + lang!( + c, + "src/**/*.c", + format_args!( + "comment-style = {{ meta = {:?}, content = {:?} }}", + "*=", "*#" + ) + ); + lang!(go, "src/**/*.go"); + lang!(java, "src/**/*.java"); + lang!(javascript, "src/**/*.js"); + lang!( + python, + "src/**/*.py", + format_args!( + "comment-style = {{ meta = {:?}, content = {:?} }}", + "##=", "##%" + ) + ); + lang!(typescript, "src/**/*.ts"); + lang!( + ruby, + "lib/**/*.rb", + format_args!( + "comment-style = {{ meta = {:?}, content = {:?} }}", + "##=", "##%" + ) + ); + lang!(rust, "src/**/*.rs"); +} diff --git a/duvet/src/lib.rs b/duvet/src/lib.rs index e92e314e..7203ad92 100644 --- a/duvet/src/lib.rs +++ b/duvet/src/lib.rs @@ -8,6 +8,7 @@ mod annotation; mod comment; mod config; mod extract; +mod init; mod project; mod reference; mod report; @@ -21,7 +22,11 @@ pub use duvet_core::{diagnostic::Error, Result}; #[allow(clippy::large_enum_variant)] #[derive(Debug, Parser)] pub enum Arguments { + /// Initializes a duvet project + Init(init::Init), + /// Extracts requirements out of a specification Extract(extract::Extract), + /// Generates reports for the project Report(report::Report), } @@ -33,6 +38,7 @@ pub async fn arguments() -> Arc { impl Arguments { pub async fn exec(&self) -> Result { match self { + Self::Init(args) => args.exec().await, Self::Extract(args) => args.exec().await, Self::Report(args) => args.exec().await, } diff --git a/duvet/src/specification/ietf/tests.rs b/duvet/src/specification/ietf/tests.rs index ca643986..9e40888f 100644 --- a/duvet/src/specification/ietf/tests.rs +++ b/duvet/src/specification/ietf/tests.rs @@ -208,7 +208,7 @@ async fn test_rfc(rfc: usize) -> bool { (4606, "1"), ]; - // RFCs that use roman numberals + // RFCs that use roman numerals let roman_appendix_ids = [(5357, "I")]; // RFCs that have indented sections diff --git a/duvet/www/src/App.js b/duvet/www/src/App.js index ce2566cd..a6b7fd1a 100644 --- a/duvet/www/src/App.js +++ b/duvet/www/src/App.js @@ -1,3 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + import { useState, default as React } from "react"; import { makeStyles } from "@material-ui/core/styles"; import CssBaseline from "@material-ui/core/CssBaseline"; diff --git a/duvet/www/src/index.js b/duvet/www/src/index.js index 847bcc5e..b4fd3c2f 100644 --- a/duvet/www/src/index.js +++ b/duvet/www/src/index.js @@ -1,3 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + import { default as React, useEffect } from "react"; import ReactDOM from "react-dom"; import { HashRouter, useLocation } from "react-router-dom"; diff --git a/duvet/www/src/link.js b/duvet/www/src/link.js index e81823a8..4d481be2 100644 --- a/duvet/www/src/link.js +++ b/duvet/www/src/link.js @@ -1,3 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + import { Link as A } from "react-router-dom"; import B from "@material-ui/core/Link"; diff --git a/duvet/www/src/nav.js b/duvet/www/src/nav.js index 05c17e23..bcce2431 100644 --- a/duvet/www/src/nav.js +++ b/duvet/www/src/nav.js @@ -1,3 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + import { useState, default as React, useMemo, forwardRef } from "react"; import { makeStyles, useTheme } from "@material-ui/core/styles"; import clsx from "clsx"; diff --git a/duvet/www/src/result.js b/duvet/www/src/result.js index 4fcc2a74..a0185ada 100644 --- a/duvet/www/src/result.js +++ b/duvet/www/src/result.js @@ -1,3 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + const input = process.env.NODE_ENV === "production" ? JSON.parse(document.getElementById("result").innerHTML) diff --git a/duvet/www/src/section.js b/duvet/www/src/section.js index a99d6b01..26c00f44 100644 --- a/duvet/www/src/section.js +++ b/duvet/www/src/section.js @@ -1,3 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + import { useState, useMemo, default as React } from "react"; import { useLocation } from "react-router-dom"; import { makeStyles, withStyles } from "@material-ui/core/styles"; diff --git a/duvet/www/src/spec.js b/duvet/www/src/spec.js index 53fb82f8..fa127fca 100644 --- a/duvet/www/src/spec.js +++ b/duvet/www/src/spec.js @@ -1,3 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + import { makeStyles } from "@material-ui/core/styles"; import { DataGrid } from "@mui/x-data-grid"; import Table from "@material-ui/core/Table"; diff --git a/integration/init-c.toml b/integration/init-c.toml new file mode 100644 index 00000000..4ea3fd59 --- /dev/null +++ b/integration/init-c.toml @@ -0,0 +1,26 @@ +source = { local = true } +cmd = ["duvet init", "duvet report"] + +[[file]] +path = "CMakeLists.txt" +contents = """ +cmake_minimum_required (VERSION 3.9) +project (testing C) +""" + +[[file]] +path = "src/testing.c" +contents = """ +/** + *= my-spec.md#c + *# C SHOULD be auto-detected. + */ +""" + +[[file]] +path = "my-spec.md" +contents = """ +# C + +C SHOULD be auto-detected. +""" diff --git a/integration/init-java.toml b/integration/init-java.toml new file mode 100644 index 00000000..a53ae982 --- /dev/null +++ b/integration/init-java.toml @@ -0,0 +1,28 @@ +source = { local = true } +cmd = ["duvet init", "duvet report"] + +[[file]] +path = "build.gradle" +contents = """ +plugins { + id 'java' +} + +group 'com.example' +version '1.0-SNAPSHOT' +""" + +[[file]] +path = "src/main/HelloDuvet.java" +contents = """ +//= my-spec.md#java +//# Java SHOULD be auto-detected. +""" + +[[file]] +path = "my-spec.md" +contents = """ +# Java + +Java SHOULD be auto-detected. +""" diff --git a/integration/init-python.toml b/integration/init-python.toml new file mode 100644 index 00000000..7fc00d82 --- /dev/null +++ b/integration/init-python.toml @@ -0,0 +1,25 @@ +source = { local = true } +cmd = ["duvet init", "duvet report"] + +[[file]] +path = "pyproject.toml" +contents = """ +[project] +name = "testing" +version = "0.1.0" +""" + +[[file]] +path = "src/testing.py" +contents = """ +##= my-spec.md#python +##% Python SHOULD be auto-detected. +""" + +[[file]] +path = "my-spec.md" +contents = """ +# Python + +Python SHOULD be auto-detected. +""" diff --git a/integration/init-quick-start.toml b/integration/init-quick-start.toml new file mode 100644 index 00000000..b9e45b7b --- /dev/null +++ b/integration/init-quick-start.toml @@ -0,0 +1,13 @@ +source = { local = true } +cmd = [ + "duvet init --lang-rust --specification https://www.rfc-editor.org/rfc/rfc2324", + "duvet report", +] + +[[file]] +path = "src/lib.rs" +contents = """ +//= https://www.rfc-editor.org/rfc/rfc2324#section-2.1.1 +//# A coffee pot server MUST accept both the BREW and POST method +//# equivalently. +""" diff --git a/integration/init-rust.toml b/integration/init-rust.toml new file mode 100644 index 00000000..5057b774 --- /dev/null +++ b/integration/init-rust.toml @@ -0,0 +1,25 @@ +source = { local = true } +cmd = ["duvet init", "duvet report"] + +[[file]] +path = "Cargo.toml" +contents = """ +[package] +name = "testing" +version = "0.1.0" +""" + +[[file]] +path = "src/lib.rs" +contents = """ +//= my-spec.md#rust +//# Rust SHOULD be auto-detected. +""" + +[[file]] +path = "my-spec.md" +contents = """ +# Rust + +Rust SHOULD be auto-detected. +""" diff --git a/integration/snapshots/init-c.snap b/integration/snapshots/init-c.snap new file mode 100644 index 00000000..8a5a49ec --- /dev/null +++ b/integration/snapshots/init-c.snap @@ -0,0 +1,7 @@ +--- +source: xtask/src/tests.rs +expression: snapshot +--- +SPECIFICATION: [C](my-spec.md) + SECTION: [C](#c) + TEXT[implementation]: C SHOULD be auto-detected. diff --git a/integration/snapshots/init-c_json.snap b/integration/snapshots/init-c_json.snap new file mode 100644 index 00000000..78cb6753 --- /dev/null +++ b/integration/snapshots/init-c_json.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:017ff9e279ee86a2a64fefcb72d5f47f614d261fd059463449e066cf3bbd5ee1 +size 25629 diff --git a/integration/snapshots/init-java.snap b/integration/snapshots/init-java.snap new file mode 100644 index 00000000..88ebf69f --- /dev/null +++ b/integration/snapshots/init-java.snap @@ -0,0 +1,7 @@ +--- +source: xtask/src/tests.rs +expression: snapshot +--- +SPECIFICATION: [Java](my-spec.md) + SECTION: [Java](#java) + TEXT[implementation]: Java SHOULD be auto-detected. diff --git a/integration/snapshots/init-java_json.snap b/integration/snapshots/init-java_json.snap new file mode 100644 index 00000000..f09eb207 --- /dev/null +++ b/integration/snapshots/init-java_json.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:60727deb2e3ced49182c072a9ccf5191a2400b7d1ee949f5434720c4d34bb8f2 +size 25655 diff --git a/integration/snapshots/init-python.snap b/integration/snapshots/init-python.snap new file mode 100644 index 00000000..92ba235b --- /dev/null +++ b/integration/snapshots/init-python.snap @@ -0,0 +1,7 @@ +--- +source: xtask/src/tests.rs +expression: snapshot +--- +SPECIFICATION: [Python](my-spec.md) + SECTION: [Python](#python) + TEXT[implementation]: Python SHOULD be auto-detected. diff --git a/integration/snapshots/init-python_json.snap b/integration/snapshots/init-python_json.snap new file mode 100644 index 00000000..4f75612b --- /dev/null +++ b/integration/snapshots/init-python_json.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f405615de471becd49fdb82363fc00a7d3c8f15edf0a84b81cfdb194d47b42c3 +size 25655 diff --git a/integration/snapshots/init-quick-start.snap b/integration/snapshots/init-quick-start.snap new file mode 100644 index 00000000..ec316768 --- /dev/null +++ b/integration/snapshots/init-quick-start.snap @@ -0,0 +1,38 @@ +--- +source: xtask/src/tests.rs +expression: snapshot +--- +SPECIFICATION: https://www.rfc-editor.org/rfc/rfc2324 + SECTION: [The BREW method, and the use of POST](#section-2.1.1) + TEXT[!MUST,implementation]: A coffee pot server MUST accept both the BREW and POST method + TEXT[!MUST,implementation]: equivalently. + + SECTION: [406 Not Acceptable](#section-2.3.1) + TEXT[!MAY]: In HTCPCP, this response code MAY be + TEXT[!MAY]: returned if the operator of the coffee pot cannot comply with the + TEXT[!MAY]: Accept-Addition request. + TEXT[!SHOULD]: Unless the request was a HEAD request, the + TEXT[!SHOULD]: response SHOULD include an entity containing a list of available + TEXT[!SHOULD]: coffee additions. + + SECTION: [418 I'm a teapot](#section-2.3.2) + TEXT[!MAY]: The resulting entity body MAY be short and + TEXT[!MAY]: stout. + + SECTION: [The "coffee" URI scheme](#section-3) + TEXT[!MAY]: However, the use + TEXT[!MAY]: of coffee-scheme in various languages MAY be interpreted as an + TEXT[!MAY]: indication of the kind of coffee produced by the coffee pot. + + SECTION: [The "message/coffeepot" media type](#section-4) + TEXT[!MUST]: The entity body of a POST or BREW request MUST be of Content-Type + TEXT[!MUST]: "message/coffeepot". + + SECTION: [Timing Considerations](#section-5.1) + TEXT[!SHOULD]: Coffee pots SHOULD use the Network Time + TEXT[!SHOULD]: Protocol [NTP] to synchronize their clocks to a globally accurate + TEXT[!SHOULD]: time standard. + + SECTION: [Crossing firewalls](#section-5.2) + TEXT[!SHOULD]: Every home computer network SHOULD be protected by a firewall + TEXT[!SHOULD]: from sources of heat. diff --git a/integration/snapshots/init-quick-start_json.snap b/integration/snapshots/init-quick-start_json.snap new file mode 100644 index 00000000..519f602a --- /dev/null +++ b/integration/snapshots/init-quick-start_json.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7c814969aff20660fbd261127ba5cb2f5ea471176b4a6fe276d4617d2147ddc4 +size 60608 diff --git a/integration/snapshots/init-rust.snap b/integration/snapshots/init-rust.snap new file mode 100644 index 00000000..883f5ff1 --- /dev/null +++ b/integration/snapshots/init-rust.snap @@ -0,0 +1,7 @@ +--- +source: xtask/src/tests.rs +expression: snapshot +--- +SPECIFICATION: [Rust](my-spec.md) + SECTION: [Rust](#rust) + TEXT[implementation]: Rust SHOULD be auto-detected. diff --git a/integration/snapshots/init-rust_json.snap b/integration/snapshots/init-rust_json.snap new file mode 100644 index 00000000..1abe2bc1 --- /dev/null +++ b/integration/snapshots/init-rust_json.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ebdd04a504e1c6120641b68e8fa0426cbb316edd4d28d4b4820b1b8d78fff1e +size 25641 diff --git a/xtask/src/args.rs b/xtask/src/args.rs index 64f921da..458e87c4 100644 --- a/xtask/src/args.rs +++ b/xtask/src/args.rs @@ -1,3 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + use crate::Result; use clap::Parser; use xshell::Shell; diff --git a/xtask/src/build.rs b/xtask/src/build.rs index c0938906..5fa34164 100644 --- a/xtask/src/build.rs +++ b/xtask/src/build.rs @@ -1,3 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + use crate::Result; use clap::Parser; use std::path::PathBuf; diff --git a/xtask/src/changelog.rs b/xtask/src/changelog.rs index 1c291fb3..1ee8cec1 100644 --- a/xtask/src/changelog.rs +++ b/xtask/src/changelog.rs @@ -1,3 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + use crate::Result; use clap::Parser; use xshell::{cmd, Shell}; diff --git a/xtask/src/checks.rs b/xtask/src/checks.rs index 4f215a39..33c0d93b 100644 --- a/xtask/src/checks.rs +++ b/xtask/src/checks.rs @@ -1,5 +1,10 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + use crate::{args::FlagExt, Result}; +use anyhow::anyhow; use clap::Parser; +use std::path::Path; use xshell::{cmd, Shell}; #[derive(Debug, Parser)] @@ -13,16 +18,24 @@ pub struct Checks { impl Checks { pub fn run(&self, sh: &Shell) -> Result { - crate::build::Build { - profile: "dev".into(), - } - .run(sh)?; + self.copyright(sh)?; { let toolchain = format!("+{}", self.rustfmt_toolchain); cmd!(sh, "cargo {toolchain} fmt --all -- --check").run()?; } + if cmd!(sh, "which typos").quiet().run().is_err() { + cmd!(sh, "cargo install --locked typos-cli").run()?; + } + + cmd!(sh, "typos").run()?; + + crate::build::Build { + profile: "dev".into(), + } + .run(sh)?; + let mut clippy_args = vec![]; if self.enforce_warnings.is_enabled(true) { clippy_args.extend(["-D", "warnings"]); @@ -35,4 +48,36 @@ impl Checks { .run()?; Ok(()) } + + fn copyright(&self, sh: &Shell) -> Result { + let files = cmd!(sh, "git ls-tree -r --name-only HEAD").read()?; + + let mut is_ok = true; + + for file in files.lines() { + let file = Path::new(file); + let Some(ext) = file.extension().and_then(|v| v.to_str()) else { + continue; + }; + if !["rs", "js"].contains(&ext) { + continue; + } + let contents = sh.read_file(file)?; + let has_copyright = contents + .lines() + .take(3) + .any(|line| line.contains("Copyright")); + + if !has_copyright { + eprintln!("{} missing copyright header", file.display()); + is_ok = false; + } + } + + if !is_ok { + return Err(anyhow!("Failed copyright check")); + } + + Ok(()) + } } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index f474de16..bf2fe6f7 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,3 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + use clap::Parser; use xshell::Shell; diff --git a/xtask/src/publish.rs b/xtask/src/publish.rs index 4ce64153..ee4d62a8 100644 --- a/xtask/src/publish.rs +++ b/xtask/src/publish.rs @@ -1,3 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + use crate::Result; use clap::Parser; use xshell::{cmd, Shell}; diff --git a/xtask/src/tests.rs b/xtask/src/tests.rs index 23042210..297c4b82 100644 --- a/xtask/src/tests.rs +++ b/xtask/src/tests.rs @@ -1,3 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + use crate::{args::FlagExt as _, Result}; use clap::Parser; use serde::Deserialize;