From bc6aa5df0027995161e9bf5c7185dce50a22d7ad Mon Sep 17 00:00:00 2001 From: Eli Schleifer <1265982+EliSchleifer@users.noreply.github.com> Date: Mon, 12 Feb 2024 14:01:40 -0800 Subject: [PATCH] Refresh trunk-toolbox to be better than ever (#29) --- .github/actionlint.yaml | 13 ++ .github/workflows/pr.yaml | 20 ++-- .trunk/.gitignore | 2 + .trunk/config/.shellcheckrc | 7 ++ .trunk/config/.yamllint.yaml | 10 ++ .trunk/setup-ci/action.yaml | 11 ++ .trunk/trunk.yaml | 77 +++++++++--- CONTRIBUTING.md | 31 +++++ Cargo.toml | 1 + README.md | 58 ++++++++- build.rs | 2 +- rust-toolchain.toml | 2 +- src/git.rs | 5 +- src/main.rs | 61 ++++++++-- src/rules/if_change_then_change.rs | 111 ++++++++++++------ src/rules/pls_no_land.rs | 27 ++++- tests/do_not_land_test.rs | 6 +- tests/foo.bar | 1 + tests/if_change_then_change/basic_ictc.file | 23 ++++ .../if_change_then_change/multiple_ictc.file | 23 ++++ tests/if_change_then_change/no_ictc.file | 23 ++++ tests/if_change_then_change_test.rs | 36 ++++++ tests/integration_testing.rs | 12 ++ toolbox-latest | 37 ++++++ 24 files changed, 506 insertions(+), 93 deletions(-) create mode 100644 .github/actionlint.yaml create mode 100644 .trunk/config/.shellcheckrc create mode 100644 .trunk/config/.yamllint.yaml create mode 100644 .trunk/setup-ci/action.yaml create mode 100644 CONTRIBUTING.md create mode 100644 tests/foo.bar create mode 100644 tests/if_change_then_change/basic_ictc.file create mode 100644 tests/if_change_then_change/multiple_ictc.file create mode 100644 tests/if_change_then_change/no_ictc.file create mode 100755 toolbox-latest diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml new file mode 100644 index 0000000..e5d00a7 --- /dev/null +++ b/.github/actionlint.yaml @@ -0,0 +1,13 @@ +self-hosted-runner: + labels: + - mint + - small + - 2xlarge + - prod + - playwright + - monorepo-amd64-large + - monorepo-amd64-xlarge + - monorepo-amd64-2xlarge + - monorepo-arm64-2xlarge + - public-amd64-2xlarge + - ubuntu-x64 diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 5c9e50a..2bab376 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -7,10 +7,10 @@ concurrency: jobs: build_and_test: name: Cargo Test [linux] - runs-on: ubuntu-latest + runs-on: [self-hosted, ubuntu-x64] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - name: Cargo Test @@ -18,18 +18,12 @@ jobs: trunk_check_runner: name: Trunk Check runner [linux] - runs-on: ubuntu-latest + runs-on: [self-hosted, ubuntu-x64] steps: - - name: Checkout - uses: actions/checkout@v2 - - # Caching is only needed when using ephemeral CI runners - - name: Cache Linters/Formatters - uses: actions/cache@v2 - with: - path: ~/.cache/trunk - key: trunk-${{ runner.os }} + - uses: actions/checkout@v4 - name: Trunk Check - uses: trunk-io/trunk-action@v1.0.4 + uses: trunk-io/trunk-action@v1 + with: + cache: false diff --git a/.trunk/.gitignore b/.trunk/.gitignore index cf2f254..15966d0 100644 --- a/.trunk/.gitignore +++ b/.trunk/.gitignore @@ -2,6 +2,8 @@ *logs *actions *notifications +*tools plugins user_trunk.yaml user.yaml +tmp diff --git a/.trunk/config/.shellcheckrc b/.trunk/config/.shellcheckrc new file mode 100644 index 0000000..8c7b1ad --- /dev/null +++ b/.trunk/config/.shellcheckrc @@ -0,0 +1,7 @@ +enable=all +source-path=SCRIPTDIR +disable=SC2154 + +# If you're having issues with shellcheck following source, disable the errors via: +# disable=SC1090 +# disable=SC1091 diff --git a/.trunk/config/.yamllint.yaml b/.trunk/config/.yamllint.yaml new file mode 100644 index 0000000..4d44466 --- /dev/null +++ b/.trunk/config/.yamllint.yaml @@ -0,0 +1,10 @@ +rules: + quoted-strings: + required: only-when-needed + extra-allowed: ["{|}"] + empty-values: + forbid-in-block-mappings: true + forbid-in-flow-mappings: true + key-duplicates: {} + octal-values: + forbid-implicit-octal: true diff --git a/.trunk/setup-ci/action.yaml b/.trunk/setup-ci/action.yaml new file mode 100644 index 0000000..60cdbe2 --- /dev/null +++ b/.trunk/setup-ci/action.yaml @@ -0,0 +1,11 @@ +name: trunk-io/trunk setup for trunk check/upgrade +description: Set up + +runs: + using: composite + steps: + - uses: dtolnay/rust-toolchain@stable + + - name: Build trunk-toolbox + shell: bash + run: cargo build diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 11124a6..865ef43 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -2,34 +2,79 @@ version: 0.1 plugins: sources: - id: trunk - ref: v0.0.6 + ref: v1.4.2 uri: https://github.com/trunk-io/plugins runtimes: enabled: - - go@1.18.3 - - node@16.14.2 + - python@3.10.8 + - go@1.21.0 + - node@18.12.1 + - rust@1.76.0 cli: - version: 1.10.1-beta.25 + version: 1.19.0 api: address: api.trunk-staging.io:8443 + +downloads: + - name: trunk-toolbox + version: 0.0.1 + downloads: + - version: 0.0.1 + os: + linux: unknown-linux-gnu + macos: apple-darwin + cpu: + x86_64: x86_64 + arm_64: aarch64 + url: https://github.com/trunk-io/toolbox/releases/download/${version}/trunk-toolbox-${version}-${cpu}-${os}.tar.gz +tools: + definitions: + - name: trunk-toolbox + download: trunk-toolbox + shims: [trunk-toolbox] + known_good_version: 0.0.1 + runtimes: + - rust lint: - linters: - - name: horton - type: sarif + definitions: + - name: trunk-toolbox + tools: [trunk-toolbox] files: [ALL] - command: ["${workspace}/target/debug/horton", "--file", "${path}"] - success_codes: [0, 1] + commands: + - name: lint + run: ${workspace}/toolbox-latest --upstream=${upstream-ref} --results=${tmpfile} ${target} + output: sarif + batch: true + success_codes: [0] + read_output_from: tmp_file + disable_upstream: true + version_command: + parse_regex: ${semver} + run: trunk-toolbox --version + environment: + - name: PATH + list: ["${linter}"] + enabled: + - shellcheck@0.9.0 + - shfmt@3.6.0 + - trunk-toolbox@0.0.1 + - checkov@3.2.19 + - trivy@0.49.1 + - trufflehog@3.67.5 + - oxipng@9.0.0 + - yamllint@1.34.0 - git-diff-check - - taplo@0.7.0 - - actionlint@1.6.22 - - clippy@1.65.0 - - gitleaks@8.15.2 - - markdownlint@0.32.2 - - prettier@2.8.1 - - rustfmt@1.65.0 + - taplo@0.8.1 + - actionlint@1.6.26 + - clippy@1.76.0 + - gitleaks@8.18.2 + - markdownlint@0.39.0 + - prettier@3.2.5 + - rustfmt@1.76.0 actions: enabled: + - trunk-upgrade-available - trunk-announce - trunk-check-pre-push - trunk-fmt-pre-commit diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..cd64430 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,31 @@ +# Contribution + +Thanks for contributing to the trunk toolbox! Read on to learn more. + +- [Overview](#overview) +- [Development](#development) +- [Testing](#testing) +- [Guidelines](#guidelines) +- [Docs](https://docs.trunk.io) + +## Overview + +Trunk toolbox is a place for rules that transcend particular languages and are relevant to any code in a repo. + +## Development + +The trunk.yaml in this repo has been modified to run your local iteration of toolbox as you are building. This is managed through the `trunk-latest` script. Effectively as you run `cargo build` or `cargo build release` the script will pick up the last built binary and use that. + +If no local binary has been built then the pinned version in the trunk.yaml will be used. + +## Testing + +`cargo test` will execute the unit and integration tests for the repo + +## Guidelines + +Please follow the guidelines below when contributing: + +- After defining a rule, please add it to [`README.md`](README.md). +- If you run into any problems while defining a rule, feel free to reach out on our + [Slack](https://slack.trunk.io/). diff --git a/Cargo.toml b/Cargo.toml index 45ac1c8..4dae09e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ serde = { version = "1.0.145", features = ["derive"] } serde_json = "1.0.85" serde-sarif = "0.3.4" content_inspector = "0.2.4" +rayon = "1.5.1" [dev-dependencies] assert_cmd = "2.0" diff --git a/README.md b/README.md index 8799b99..04e1e8e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ + + +

@@ -18,7 +21,10 @@ Toolbox is our custom collection of must have tools for any large software project. We've got a backlog of small tools to built into our toolbox here and happy to take contributions as well. `toolbox` is best used through `trunk check` to keep your development on rails (not the ruby kind). -### Rules +This repo is open to contributions! See our +[contribution guidelines](https://github.com/trunk-io/toolbox/blob/main/CONTRIBUTING.md) + +### Enabling To enable the toolbox rules in your repository run: @@ -26,9 +32,53 @@ To enable the toolbox rules in your repository run: trunk check enable trunk-toolbox ``` -| rule | description | -| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| DONOTLAND | Sometimes you write some code but want to make sure it doesn't actually land. With this rule you can keep yourself from that embarrassing commit that get's pushed for review with hacks or temporary junk inside it | +### Rules + +#### do-not-land + +##### What it does + +Keeps you from accidentally commiting code to a repository that is experimental, temporary, debugging cruft. It keeps you from pushing a PR with a bunch of printf() statements you added while debugging an error. + +Valid triggers for this rule are: DONOTLAND, DO-NOT-LAND, DO_NOT_LAND, donotland, do-not-land, do_not_land + +##### Why if this bad? + +Anything you intentionally don't want in your repo should really not be there. This lets you flag the code you are writing to do testing without worrying that you'll forget you dropped it in your files before pushing your Pull Request. + +##### Example + +```typescript +// DONOTLAND +console.log("I don't think this code should execute"); +``` + +#### if-change-then-change + +##### What it does + +Allows you to enforce code synchronization. Often, we have code in one file that is reliant on code in another loosely - say an enum has 4 options and you want to make sure consumers of that enum are kept in sync as new enums are added. This rule will make sure the code is updated in both places when a modification occurs to the code block. + +##### Why if this bad? + +If code has baked-in assumptions that are not enforced through a check - then they can easily get out of sync. This rule allows you to encode that dependency and ensure all related code is updated when a modification occurs. + +##### Example + +This rule will report a violation if picker.rs is not updated when the content inside this enum block is modified: + +```rust +let x = 7; + +// IfChange +enum Flavor { + Strawberry, + Chocholate +} +// ThenChange srcs/robot/picker.rs + +x += 9; // why not +``` ### Disclaimer diff --git a/build.rs b/build.rs index a94c9f4..96982f3 100644 --- a/build.rs +++ b/build.rs @@ -9,7 +9,7 @@ fn main() { println!("cargo:rustc-env=CARGO_PKG_VERSION={}", val); } else { let output = Command::new("git") - .args(&["rev-parse", "--abbrev-ref", "HEAD"]) + .args(["rev-parse", "--abbrev-ref", "HEAD"]) .output() .unwrap(); let git_ref = String::from_utf8(output.stdout).unwrap(); diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 22048ac..624eb0e 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.70.0" +channel = "1.76.0" diff --git a/src/git.rs b/src/git.rs index 2a43819..594dfa1 100644 --- a/src/git.rs +++ b/src/git.rs @@ -34,8 +34,9 @@ fn is_lfs(repo: &Repository, path: &Path) -> bool { } } -pub fn modified_since(upstream: &str) -> anyhow::Result { - let repo = Repository::open(".")?; +pub fn modified_since(upstream: &str, repo_path: Option<&Path>) -> anyhow::Result { + let path = repo_path.unwrap_or(Path::new(".")); + let repo = Repository::open(path)?; let upstream_tree = match repo.find_reference(upstream) { Ok(reference) => reference.peel_to_tree()?, diff --git a/src/main.rs b/src/main.rs index c6e46a2..d9096f7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,32 +1,50 @@ use clap::Parser; use horton::diagnostic; -use horton::git; use horton::rules::if_change_then_change::ictc; use horton::rules::pls_no_land::pls_no_land; use serde_sarif::sarif; - +use std::collections::HashSet; +use std::path::PathBuf; +use std::time::Instant; #[derive(Parser, Debug)] #[clap(version = env!("CARGO_PKG_VERSION"), author = "Trunk Technologies Inc.")] struct Opts { + // #[arg(short, long, num_args = 1..)] + files: Vec, + #[clap(long)] - // #[arg(default_value_t = String::from("refs/heads/main"))] #[arg(default_value_t = String::from("HEAD"))] upstream: String, + + #[clap(long)] + #[arg(default_value_t = String::from(""))] + results: String, } fn run() -> anyhow::Result<()> { + let start = Instant::now(); let opts: Opts = Opts::parse(); let mut ret = diagnostic::Diagnostics::default(); - let modified = git::modified_since(&opts.upstream)?; - log::debug!("Modified stats, per libgit2:\n{:#?}", modified); + // Convert to PathBufs + let paths: HashSet = opts.files.into_iter().map(PathBuf::from).collect(); - ret.diagnostics.extend(pls_no_land(&modified.paths)?); - ret.diagnostics.extend(ictc(&modified.hunks)?); + let (pls_no_land_result, ictc_result): (Result<_, _>, Result<_, _>) = + rayon::join(|| pls_no_land(&paths), || ictc(&paths, &opts.upstream)); + + match pls_no_land_result { + Ok(result) => ret.diagnostics.extend(result), + Err(e) => return Err(e), + } + + match ictc_result { + Ok(result) => ret.diagnostics.extend(result), + Err(e) => return Err(e), + } // TODO(sam): figure out how to stop using unwrap() inside the map() calls below - let results: Vec = ret + let mut results: Vec = ret .diagnostics .iter() .map(|d| { @@ -67,12 +85,30 @@ fn run() -> anyhow::Result<()> { }) .collect(); + let r = sarif::ResultBuilder::default() + .message( + sarif::MessageBuilder::default() + .text(format!( + "{:?} files processed in {:?}", + paths.len(), + start.elapsed() + )) + .build() + .unwrap(), + ) + .rule_id("toolbox-perf") + .level("note") + .build() + .unwrap(); + + results.push(r); + let run = sarif::RunBuilder::default() .tool( sarif::ToolBuilder::default() .driver( sarif::ToolComponentBuilder::default() - .name("horton") + .name("trunk-toolbox") .build() .unwrap(), ) @@ -87,7 +123,12 @@ fn run() -> anyhow::Result<()> { .build()?; let sarif = serde_json::to_string_pretty(&sarif_built)?; - println!("{}", sarif); + + if opts.results.is_empty() { + println!("{}", sarif); + } else { + std::fs::write(opts.results, sarif)?; + } Ok(()) } diff --git a/src/rules/if_change_then_change.rs b/src/rules/if_change_then_change.rs index 19944e5..edd337b 100644 --- a/src/rules/if_change_then_change.rs +++ b/src/rules/if_change_then_change.rs @@ -8,7 +8,8 @@ use std::path::PathBuf; use regex::Regex; use crate::diagnostic; -use crate::git::Hunk; +use crate::git; +use rayon::prelude::*; #[derive(Debug)] pub struct IctcBlock { @@ -24,55 +25,87 @@ lazy_static::lazy_static! { } -pub fn ictc(hunks: &Vec) -> anyhow::Result> { +pub fn find_ictc_blocks(path: &PathBuf) -> anyhow::Result> { + let mut blocks: Vec = Vec::new(); + + let mut ifttt_begin: i64 = -1; + + let in_file = File::open(path).with_context(|| format!("failed to open: {:#?}", path))?; + let in_buf = BufReader::new(in_file); + + for (i, line) in lines_view(in_buf) + .context(format!("failed to read lines of text from: {:#?}", path))? + .iter() + .enumerate() + .map(|(i, line)| (i + 1, line)) + { + if RE_BEGIN.find(line).is_some() { + ifttt_begin = i as i64; + } else if let Some(end) = RE_END.captures(line) { + if ifttt_begin != -1 { + let block = IctcBlock { + path: path.clone(), + begin: ifttt_begin, + end: i as i64, + thenchange: PathBuf::from( + end.get(2) + .with_context(|| "expected at least 3 captures")? + .as_str(), + ), + }; + blocks.push(block); + ifttt_begin = -1; + } + } + } + + Ok(blocks) +} + +pub fn ictc( + files: &HashSet, + upstream: &str, +) -> anyhow::Result> { + // Build up list of files that actually have a ifchange block - this way we can avoid + // processing git modified chunks if none are present + let all_blocks: Vec<_> = files + .par_iter() + .filter_map(|file| find_ictc_blocks(file).ok()) + .flatten() + .collect(); + + // Fast exit if we don't have any files that have ICTC blocks - saves us calling + // into git to get more information + if all_blocks.is_empty() { + return Ok(vec![]); + } + + let modified = git::modified_since(upstream, None)?; + let hunks = &modified.hunks; + + log::debug!("Modified stats, per libgit2:\n{:#?}", modified); + // TODO(sam): this _should_ be a iter-map-collect, but unclear how to apply a reducer // between the map and collect (there can be multiple hunks with the same path) let mut modified_lines_by_path: HashMap> = HashMap::new(); for h in hunks { modified_lines_by_path .entry(h.path.clone()) - .or_insert_with(HashSet::new) + .or_default() .extend(h.begin..h.end); } let modified_lines_by_path = modified_lines_by_path; let mut blocks: Vec = Vec::new(); - for path in modified_lines_by_path.keys() { - let mut ifttt_begin: i64 = -1; - - let in_file = File::open(&path).with_context(|| format!("failed to open: {:#?}", path))?; - let in_buf = BufReader::new(in_file); - - for (i, line) in lines_view(in_buf) - .context(format!("failed to read lines of text from: {:#?}", path))? - .iter() - .enumerate() - .map(|(i, line)| (i + 1, line)) - { - if RE_BEGIN.find(line).is_some() { - ifttt_begin = i as i64; - } else if let Some(end) = RE_END.captures(line) { - if ifttt_begin != -1 { - let block = IctcBlock { - path: path.clone(), - begin: ifttt_begin, - end: i as i64, - thenchange: PathBuf::from(end.get(2).unwrap().as_str()), - }; - - let block_lines = HashSet::from_iter(block.begin..block.end); - - if !block_lines.is_disjoint( - modified_lines_by_path - .get(&block.path) - .unwrap_or(&HashSet::new()), - ) { - blocks.push(block); - } - - ifttt_begin = -1; - } - } + + for block in all_blocks { + let block_lines = HashSet::from_iter(block.begin..block.end); + if !block_lines.is_disjoint( + modified_lines_by_path + .get(&block.path) + .unwrap_or(&HashSet::new()), + ) { + blocks.push(block); } } diff --git a/src/rules/pls_no_land.rs b/src/rules/pls_no_land.rs index e8238ba..968168e 100644 --- a/src/rules/pls_no_land.rs +++ b/src/rules/pls_no_land.rs @@ -1,10 +1,13 @@ +// trunk-ignore-all(trunk-toolbox/do-not-land) extern crate regex; use crate::diagnostic; use anyhow::Context; +use rayon::prelude::*; use regex::Regex; use std::collections::HashSet; use std::fs::File; +use std::io::Read; use std::io::{BufRead, BufReader}; use std::path::PathBuf; @@ -12,20 +15,32 @@ lazy_static::lazy_static! { static ref RE: Regex = Regex::new(r"(?i)(DO[\s_-]*NOT[\s_-]*LAND)").unwrap(); } +pub fn is_binary_file(path: &PathBuf) -> std::io::Result { + let mut file = File::open(path)?; + let mut buffer = [0; 4096]; + let n = file.read(&mut buffer)?; + Ok(buffer[..n].contains(&0)) +} + // Checks for $re and other forms thereof in source code // -// Note that this is named "pls_no_land" to avoid causing DNL matches everywhere in horton. +// Note that this is named "pls_no_land" to avoid causing DNL matches everywhere in trunk-toolbox. pub fn pls_no_land(paths: &HashSet) -> anyhow::Result> { - let mut ret = Vec::new(); + // Scan files in parallel + let results: Result, _> = paths.par_iter().map(pls_no_land_impl).collect(); - for path in paths { - ret.splice(0..0, pls_no_land_impl(path)?); + match results { + Ok(v) => Ok(v.into_iter().flatten().collect()), + Err(e) => Err(e), } - - Ok(ret) } fn pls_no_land_impl(path: &PathBuf) -> anyhow::Result> { + if is_binary_file(path).unwrap_or(true) { + log::debug!("Ignoring binary file {}", path.display()); + return Ok(vec![]); + } + let in_file = File::open(path).with_context(|| format!("failed to open: {:#?}", path))?; let mut in_buf = BufReader::new(in_file); diff --git a/tests/do_not_land_test.rs b/tests/do_not_land_test.rs index 0550640..3cbc364 100644 --- a/tests/do_not_land_test.rs +++ b/tests/do_not_land_test.rs @@ -1,3 +1,4 @@ +// trunk-ignore-all(trunk-toolbox/do-not-land) use spectral::prelude::*; mod integration_testing; @@ -37,6 +38,7 @@ fn binary_files_ignored() -> anyhow::Result<()> { .unwrap(); assert_eq!(runs.len(), 1); + assert_eq!( runs.get(0) .unwrap() @@ -44,9 +46,11 @@ fn binary_files_ignored() -> anyhow::Result<()> { .unwrap() .as_array() .unwrap() + .iter() + .filter(|r| r.get("level").unwrap() != "note") + .collect::>() .is_empty(), true ); - Ok(()) } diff --git a/tests/foo.bar b/tests/foo.bar new file mode 100644 index 0000000..012e28f --- /dev/null +++ b/tests/foo.bar @@ -0,0 +1 @@ +change this file \ No newline at end of file diff --git a/tests/if_change_then_change/basic_ictc.file b/tests/if_change_then_change/basic_ictc.file new file mode 100644 index 0000000..bc11bfd --- /dev/null +++ b/tests/if_change_then_change/basic_ictc.file @@ -0,0 +1,23 @@ +Lorem ipsum odor amet, consectetuer adipiscing elit. Ornare elit nostra sit metus arcu maximus conubia. Nullam nec elit justo aenean penatibus. Vestibulum cras euismod orci morbi phasellus luctus, sit tincidunt dictum. Hendrerit fringilla augue morbi vel pharetra per fames. Nostra elit mi auctor suspendisse congue. Aptent magnis molestie dui integer turpis quam accumsan? Aenean quis lobortis per tristique mi. + +Platea platea donec duis gravida dapibus. Sodales iaculis eu pharetra finibus magna orci condimentum. Habitant nascetur praesent lobortis etiam vestibulum sollicitudin magna. Augue fames laoreet rutrum vel leo donec hendrerit senectus. Senectus malesuada efficitur suscipit; platea ipsum placerat. Bibendum platea mattis nullam feugiat lorem nibh augue. Nullam aliquet libero non mollis ridiculus blandit. Nisl lacus porta justo eu enim euismod facilisi curabitur tortor. Donec augue nunc aliquam ullamcorper faucibus arcu imperdiet suspendisse. + +Fusce sem sem volutpat phasellus congue quisque ante libero dictumst. Aposuere urna cubilia mi vitae felis velit. Aenean netus habitant molestie blandit pellentesque laoreet. Et est sed; cubilia adipiscing morbi suspendisse ac etiam. Amet facilisi ultrices vulputate arcu, inceptos taciti arcu. Mi malesuada eget sagittis morbi nisl nisi nunc platea. Ac convallis conubia, a urna auctor consectetur eu proin purus. Sollicitudin mollis natoque molestie morbi vulputate nascetur ante posuere phasellus. Neque finibus vestibulum class porttitor nullam tempor massa dis fusce. Netus ornare facilisi nisi molestie tortor. +// IfChange +Mus facilisis scelerisque quam semper metus orci gravida interdum mattis. Primis semper aliquet blandit condimentum pretium mus. Fames finibus torquent interdum vitae eu. At condimentum a eros turpis mus eros. Diam at interdum euismod conubia dapibus finibus fames adipiscing? Laoreet ut vehicula metus risus eros mollis mauris habitant. Mi elit ante; ullamcorper faucibus dictumst felis ac mattis. Nascetur arcu varius libero convallis penatibus aenean nunc. Molestie velit magnis turpis potenti massa suspendisse. + +Mollis ad litora adipiscing orci mi curae, vitae ipsum auctor. Malesuada aliquam tincidunt senectus justo volutpat elit taciti. Tortor massa malesuada pulvinar; eleifend vivamus sem enim. Nostra iaculis elit fusce dui rutrum sit nulla convallis. Semper pulvinar tellus magnis sociosqu nunc primis aliquam aptent. Nulla lobortis ridiculus dui ante libero sed. Gravida ut eros massa; hendrerit iaculis nisl. Ex dapibus ornare primis duis bibendum; in et cubilia. +// ThenChange foo.bar +Feugiat conubia sem potenti nec sed elementum torquent. Turpis consequat orci scelerisque; nunc purus arcu hac eu. Dapibus arcu nascetur mi posuere dictum adipiscing. Sed ex lobortis commodo elit facilisis fusce. Varius senectus mattis est tortor; proin ex. Ridiculus curabitur mattis senectus euismod primis ac. Felis dictum suspendisse fringilla sit varius. Nunc risus nullam magna laoreet fusce hendrerit sed sollicitudin. Maecenas eu id mollis felis id suspendisse. Posuere nisl velit egestas porttitor lectus curae. + +Lacus eleifend euismod leo mauris blandit dapibus faucibus. Neque non lectus vel class dapibus per adipiscing pulvinar. Vehicula risus vulputate commodo tincidunt vivamus pretium. Ultricies lectus neque vestibulum primis augue justo vivamus. Ridiculus maximus id mollis facilisis fermentum nulla sapien; vitae montes. Etiam tempus conubia maecenas habitant nec. Nam platea turpis inceptos sapien tristique. Libero maximus porta hac donec scelerisque tempus. Scelerisque velit blandit per tempus ipsum. + +Posuere maximus himenaeos, ut aenean gravida auctor. Nulla ullamcorper fusce sodales; iaculis ac habitasse lacus. Turpis est augue gravida; gravida proin senectus mollis inceptos. Vulputate risus risus tempus class nam aenean erat. Mattis tempor commodo leo felis class natoque egestas imperdiet dis. Vestibulum tristique class posuere nibh iaculis. Sem rhoncus elit pellentesque facilisis auctor. Sodales rhoncus faucibus adipiscing phasellus magnis volutpat placerat magna. + +Eros laoreet porttitor porta etiam metus. Tempus eget integer cras sed non vitae hendrerit scelerisque. Pharetra pulvinar ex semper donec diam dolor. Curabitur vel vitae cubilia curabitur mollis nostra himenaeos. Habitasse volutpat fusce euismod enim bibendum magna. Ac eget bibendum semper lectus class duis gravida metus dictumst. Consequat erat praesent dis netus platea condimentum gravida. Ex platea ipsum ullamcorper duis egestas ullamcorper suscipit mauris vitae. + +Semper finibus enim praesent tempus malesuada at elementum. Sem congue fusce litora, iaculis viverra aenean curabitur semper. In lectus ut libero vehicula curabitur conubia sodales. Pellentesque magna diam ridiculus; netus ad accumsan convallis mattis. Erat euismod feugiat posuere diam hendrerit. Imperdiet himenaeos varius efficitur at nibh interdum. Eros feugiat convallis gravida sodales tincidunt rutrum semper blandit. Nostra varius condimentum per suspendisse, dignissim morbi placerat ridiculus donec. Libero parturient massa mi nascetur volutpat tristique nibh. + +Porttitor magna litora tortor nibh aliquam sit ipsum. Tempor mi leo dolor egestas feugiat risus ultrices? Sit eu tellus suspendisse lacus nulla magna cras fusce. Nulla et venenatis potenti nullam felis laoreet. Nulla magna tellus platea urna laoreet. Vitae venenatis litora, turpis ridiculus cursus facilisi. Dis augue taciti faucibus fermentum a montes cras. Gravida platea vivamus luctus penatibus conubia orci adipiscing aptent enim. Parturient ligula nam etiam quisque dolor non quisque fermentum. + +Odio nascetur suspendisse convallis semper tincidunt mi. Turpis aenean vehicula taciti gravida dapibus suscipit, netus mattis. Aenean diam sagittis elit; nisl ut commodo neque ornare nam. Donec metus cubilia neque per malesuada sit per hendrerit. Ridiculus hac risus lacinia pulvinar mattis? Eleifend torquent fermentum semper nibh duis amet facilisis porta. diff --git a/tests/if_change_then_change/multiple_ictc.file b/tests/if_change_then_change/multiple_ictc.file new file mode 100644 index 0000000..8f5cf46 --- /dev/null +++ b/tests/if_change_then_change/multiple_ictc.file @@ -0,0 +1,23 @@ +Lorem ipsum odor amet, consectetuer adipiscing elit. Ornare elit nostra sit metus arcu maximus conubia. Nullam nec elit justo aenean penatibus. Vestibulum cras euismod orci morbi phasellus luctus, sit tincidunt dictum. Hendrerit fringilla augue morbi vel pharetra per fames. Nostra elit mi auctor suspendisse congue. Aptent magnis molestie dui integer turpis quam accumsan? Aenean quis lobortis per tristique mi. + +Platea platea donec duis gravida dapibus. Sodales iaculis eu pharetra finibus magna orci condimentum. Habitant nascetur praesent lobortis etiam vestibulum sollicitudin magna. Augue fames laoreet rutrum vel leo donec hendrerit senectus. Senectus malesuada efficitur suscipit; platea ipsum placerat. Bibendum platea mattis nullam feugiat lorem nibh augue. Nullam aliquet libero non mollis ridiculus blandit. Nisl lacus porta justo eu enim euismod facilisi curabitur tortor. Donec augue nunc aliquam ullamcorper faucibus arcu imperdiet suspendisse. + +Fusce sem sem volutpat phasellus congue quisque ante libero dictumst. Aposuere urna cubilia mi vitae felis velit. Aenean netus habitant molestie blandit pellentesque laoreet. Et est sed; cubilia adipiscing morbi suspendisse ac etiam. Amet facilisi ultrices vulputate arcu, inceptos taciti arcu. Mi malesuada eget sagittis morbi nisl nisi nunc platea. Ac convallis conubia, a urna auctor consectetur eu proin purus. Sollicitudin mollis natoque molestie morbi vulputate nascetur ante posuere phasellus. Neque finibus vestibulum class porttitor nullam tempor massa dis fusce. Netus ornare facilisi nisi molestie tortor. +// IfChange +Mus facilisis scelerisque quam semper metus orci gravida interdum mattis. Primis semper aliquet blandit condimentum pretium mus. Fames finibus torquent interdum vitae eu. At condimentum a eros turpis mus eros. Diam at interdum euismod conubia dapibus finibus fames adipiscing? Laoreet ut vehicula metus risus eros mollis mauris habitant. Mi elit ante; ullamcorper faucibus dictumst felis ac mattis. Nascetur arcu varius libero convallis penatibus aenean nunc. Molestie velit magnis turpis potenti massa suspendisse. + +Mollis ad litora adipiscing orci mi curae, vitae ipsum auctor. Malesuada aliquam tincidunt senectus justo volutpat elit taciti. Tortor massa malesuada pulvinar; eleifend vivamus sem enim. Nostra iaculis elit fusce dui rutrum sit nulla convallis. Semper pulvinar tellus magnis sociosqu nunc primis aliquam aptent. Nulla lobortis ridiculus dui ante libero sed. Gravida ut eros massa; hendrerit iaculis nisl. Ex dapibus ornare primis duis bibendum; in et cubilia. +// ThenChange foo.bar +Feugiat conubia sem potenti nec sed elementum torquent. Turpis consequat orci scelerisque; nunc purus arcu hac eu. Dapibus arcu nascetur mi posuere dictum adipiscing. Sed ex lobortis commodo elit facilisis fusce. Varius senectus mattis est tortor; proin ex. Ridiculus curabitur mattis senectus euismod primis ac. Felis dictum suspendisse fringilla sit varius. Nunc risus nullam magna laoreet fusce hendrerit sed sollicitudin. Maecenas eu id mollis felis id suspendisse. Posuere nisl velit egestas porttitor lectus curae. + +Lacus eleifend euismod leo mauris blandit dapibus faucibus. Neque non lectus vel class dapibus per adipiscing pulvinar. Vehicula risus vulputate commodo tincidunt vivamus pretium. Ultricies lectus neque vestibulum primis augue justo vivamus. Ridiculus maximus id mollis facilisis fermentum nulla sapien; vitae montes. Etiam tempus conubia maecenas habitant nec. Nam platea turpis inceptos sapien tristique. Libero maximus porta hac donec scelerisque tempus. Scelerisque velit blandit per tempus ipsum. + +Posuere maximus himenaeos, ut aenean gravida auctor. Nulla ullamcorper fusce sodales; iaculis ac habitasse lacus. Turpis est augue gravida; gravida proin senectus mollis inceptos. Vulputate risus risus tempus class nam aenean erat. Mattis tempor commodo leo felis class natoque egestas imperdiet dis. Vestibulum tristique class posuere nibh iaculis. Sem rhoncus elit pellentesque facilisis auctor. Sodales rhoncus faucibus adipiscing phasellus magnis volutpat placerat magna. +// IfChange +Eros laoreet porttitor porta etiam metus. Tempus eget integer cras sed non vitae hendrerit scelerisque. Pharetra pulvinar ex semper donec diam dolor. Curabitur vel vitae cubilia curabitur mollis nostra himenaeos. Habitasse volutpat fusce euismod enim bibendum magna. Ac eget bibendum semper lectus class duis gravida metus dictumst. Consequat erat praesent dis netus platea condimentum gravida. Ex platea ipsum ullamcorper duis egestas ullamcorper suscipit mauris vitae. +// ThenChange path/to/file/something.else +Semper finibus enim praesent tempus malesuada at elementum. Sem congue fusce litora, iaculis viverra aenean curabitur semper. In lectus ut libero vehicula curabitur conubia sodales. Pellentesque magna diam ridiculus; netus ad accumsan convallis mattis. Erat euismod feugiat posuere diam hendrerit. Imperdiet himenaeos varius efficitur at nibh interdum. Eros feugiat convallis gravida sodales tincidunt rutrum semper blandit. Nostra varius condimentum per suspendisse, dignissim morbi placerat ridiculus donec. Libero parturient massa mi nascetur volutpat tristique nibh. + +Porttitor magna litora tortor nibh aliquam sit ipsum. Tempor mi leo dolor egestas feugiat risus ultrices? Sit eu tellus suspendisse lacus nulla magna cras fusce. Nulla et venenatis potenti nullam felis laoreet. Nulla magna tellus platea urna laoreet. Vitae venenatis litora, turpis ridiculus cursus facilisi. Dis augue taciti faucibus fermentum a montes cras. Gravida platea vivamus luctus penatibus conubia orci adipiscing aptent enim. Parturient ligula nam etiam quisque dolor non quisque fermentum. + +Odio nascetur suspendisse convallis semper tincidunt mi. Turpis aenean vehicula taciti gravida dapibus suscipit, netus mattis. Aenean diam sagittis elit; nisl ut commodo neque ornare nam. Donec metus cubilia neque per malesuada sit per hendrerit. Ridiculus hac risus lacinia pulvinar mattis? Eleifend torquent fermentum semper nibh duis amet facilisis porta. \ No newline at end of file diff --git a/tests/if_change_then_change/no_ictc.file b/tests/if_change_then_change/no_ictc.file new file mode 100644 index 0000000..0e0c772 --- /dev/null +++ b/tests/if_change_then_change/no_ictc.file @@ -0,0 +1,23 @@ +Lorem ipsum odor amet, consectetuer adipiscing elit. Ornare elit nostra sit metus arcu maximus conubia. Nullam nec elit justo aenean penatibus. Vestibulum cras euismod orci morbi phasellus luctus, sit tincidunt dictum. Hendrerit fringilla augue morbi vel pharetra per fames. Nostra elit mi auctor suspendisse congue. Aptent magnis molestie dui integer turpis quam accumsan? Aenean quis lobortis per tristique mi. + +Platea platea donec duis gravida dapibus. Sodales iaculis eu pharetra finibus magna orci condimentum. Habitant nascetur praesent lobortis etiam vestibulum sollicitudin magna. Augue fames laoreet rutrum vel leo donec hendrerit senectus. Senectus malesuada efficitur suscipit; platea ipsum placerat. Bibendum platea mattis nullam feugiat lorem nibh augue. Nullam aliquet libero non mollis ridiculus blandit. Nisl lacus porta justo eu enim euismod facilisi curabitur tortor. Donec augue nunc aliquam ullamcorper faucibus arcu imperdiet suspendisse. + +Fusce sem sem volutpat phasellus congue quisque ante libero dictumst. Aposuere urna cubilia mi vitae felis velit. Aenean netus habitant molestie blandit pellentesque laoreet. Et est sed; cubilia adipiscing morbi suspendisse ac etiam. Amet facilisi ultrices vulputate arcu, inceptos taciti arcu. Mi malesuada eget sagittis morbi nisl nisi nunc platea. Ac convallis conubia, a urna auctor consectetur eu proin purus. Sollicitudin mollis natoque molestie morbi vulputate nascetur ante posuere phasellus. Neque finibus vestibulum class porttitor nullam tempor massa dis fusce. Netus ornare facilisi nisi molestie tortor. + +Mus facilisis scelerisque quam semper metus orci gravida interdum mattis. Primis semper aliquet blandit condimentum pretium mus. Fames finibus torquent interdum vitae eu. At condimentum a eros turpis mus eros. Diam at interdum euismod conubia dapibus finibus fames adipiscing? Laoreet ut vehicula metus risus eros mollis mauris habitant. Mi elit ante; ullamcorper faucibus dictumst felis ac mattis. Nascetur arcu varius libero convallis penatibus aenean nunc. Molestie velit magnis turpis potenti massa suspendisse. + +Mollis ad litora adipiscing orci mi curae, vitae ipsum auctor. Malesuada aliquam tincidunt senectus justo volutpat elit taciti. Tortor massa malesuada pulvinar; eleifend vivamus sem enim. Nostra iaculis elit fusce dui rutrum sit nulla convallis. Semper pulvinar tellus magnis sociosqu nunc primis aliquam aptent. Nulla lobortis ridiculus dui ante libero sed. Gravida ut eros massa; hendrerit iaculis nisl. Ex dapibus ornare primis duis bibendum; in et cubilia. + +Feugiat conubia sem potenti nec sed elementum torquent. Turpis consequat orci scelerisque; nunc purus arcu hac eu. Dapibus arcu nascetur mi posuere dictum adipiscing. Sed ex lobortis commodo elit facilisis fusce. Varius senectus mattis est tortor; proin ex. Ridiculus curabitur mattis senectus euismod primis ac. Felis dictum suspendisse fringilla sit varius. Nunc risus nullam magna laoreet fusce hendrerit sed sollicitudin. Maecenas eu id mollis felis id suspendisse. Posuere nisl velit egestas porttitor lectus curae. + +Lacus eleifend euismod leo mauris blandit dapibus faucibus. Neque non lectus vel class dapibus per adipiscing pulvinar. Vehicula risus vulputate commodo tincidunt vivamus pretium. Ultricies lectus neque vestibulum primis augue justo vivamus. Ridiculus maximus id mollis facilisis fermentum nulla sapien; vitae montes. Etiam tempus conubia maecenas habitant nec. Nam platea turpis inceptos sapien tristique. Libero maximus porta hac donec scelerisque tempus. Scelerisque velit blandit per tempus ipsum. + +Posuere maximus himenaeos, ut aenean gravida auctor. Nulla ullamcorper fusce sodales; iaculis ac habitasse lacus. Turpis est augue gravida; gravida proin senectus mollis inceptos. Vulputate risus risus tempus class nam aenean erat. Mattis tempor commodo leo felis class natoque egestas imperdiet dis. Vestibulum tristique class posuere nibh iaculis. Sem rhoncus elit pellentesque facilisis auctor. Sodales rhoncus faucibus adipiscing phasellus magnis volutpat placerat magna. + +Eros laoreet porttitor porta etiam metus. Tempus eget integer cras sed non vitae hendrerit scelerisque. Pharetra pulvinar ex semper donec diam dolor. Curabitur vel vitae cubilia curabitur mollis nostra himenaeos. Habitasse volutpat fusce euismod enim bibendum magna. Ac eget bibendum semper lectus class duis gravida metus dictumst. Consequat erat praesent dis netus platea condimentum gravida. Ex platea ipsum ullamcorper duis egestas ullamcorper suscipit mauris vitae. + +Semper finibus enim praesent tempus malesuada at elementum. Sem congue fusce litora, iaculis viverra aenean curabitur semper. In lectus ut libero vehicula curabitur conubia sodales. Pellentesque magna diam ridiculus; netus ad accumsan convallis mattis. Erat euismod feugiat posuere diam hendrerit. Imperdiet himenaeos varius efficitur at nibh interdum. Eros feugiat convallis gravida sodales tincidunt rutrum semper blandit. Nostra varius condimentum per suspendisse, dignissim morbi placerat ridiculus donec. Libero parturient massa mi nascetur volutpat tristique nibh. + +Porttitor magna litora tortor nibh aliquam sit ipsum. Tempor mi leo dolor egestas feugiat risus ultrices? Sit eu tellus suspendisse lacus nulla magna cras fusce. Nulla et venenatis potenti nullam felis laoreet. Nulla magna tellus platea urna laoreet. Vitae venenatis litora, turpis ridiculus cursus facilisi. Dis augue taciti faucibus fermentum a montes cras. Gravida platea vivamus luctus penatibus conubia orci adipiscing aptent enim. Parturient ligula nam etiam quisque dolor non quisque fermentum. + +Odio nascetur suspendisse convallis semper tincidunt mi. Turpis aenean vehicula taciti gravida dapibus suscipit, netus mattis. Aenean diam sagittis elit; nisl ut commodo neque ornare nam. Donec metus cubilia neque per malesuada sit per hendrerit. Ridiculus hac risus lacinia pulvinar mattis? Eleifend torquent fermentum semper nibh duis amet facilisis porta. \ No newline at end of file diff --git a/tests/if_change_then_change_test.rs b/tests/if_change_then_change_test.rs index 5e5eca1..1a2699f 100644 --- a/tests/if_change_then_change_test.rs +++ b/tests/if_change_then_change_test.rs @@ -2,6 +2,9 @@ use spectral::prelude::*; mod integration_testing; use integration_testing::TestRepo; +use std::path::PathBuf; + +use horton::rules::if_change_then_change::find_ictc_blocks; fn assert_no_expected_changes(revisions: [&str; 2]) -> anyhow::Result<()> { let test_repo = TestRepo::make().unwrap(); @@ -269,3 +272,36 @@ trailing lines assert_expected_change_in_constant_foo(revisions) } + +#[test] +fn verify_find_ictc_blocks() { + let result = find_ictc_blocks(&PathBuf::from( + "tests/if_change_then_change/basic_ictc.file", + )); + assert!(result.is_ok()); + assert!(result.unwrap().len() == 1, "should find 1 ictc block"); + + let result = find_ictc_blocks(&PathBuf::from("tests/if_change_then_change/no_ictc.file")); + assert!(result.is_ok()); + assert!(result.unwrap().len() == 0, "should find no ictc block"); + + let result = find_ictc_blocks(&PathBuf::from( + "tests/if_change_then_change/multiple_ictc.file", + )); + assert!(result.is_ok()); + let list = result.unwrap(); + assert!(list.len() == 2, "should find two ictc block"); + // assert!(list[0].begin == 1, "first block should point to 2"); + let first = &list[0]; + assert_eq!(first.begin, 6); + assert_eq!(first.end, 10); + assert_eq!(first.thenchange, PathBuf::from("foo.bar")); + + let second = &list[1]; + assert_eq!(second.begin, 16); + assert_eq!(second.end, 18); + assert_eq!( + second.thenchange, + PathBuf::from("path/to/file/something.else") + ); +} diff --git a/tests/integration_testing.rs b/tests/integration_testing.rs index faf01a2..21946db 100644 --- a/tests/integration_testing.rs +++ b/tests/integration_testing.rs @@ -120,10 +120,22 @@ impl TestRepo { pub fn run_horton_against(&self, upstream_ref: &str) -> anyhow::Result { let mut cmd = Command::cargo_bin("trunk-toolbox")?; + let modified_paths = + horton::git::modified_since(upstream_ref, Some(self.dir.path()))?.paths; + let strings: Result, _> = modified_paths + .into_iter() + .map(|path| path.into_os_string().into_string()) + .collect(); + cmd.env("RUST_LOG", "debug"); cmd.arg("--upstream") .arg(upstream_ref) .current_dir(self.dir.path()); + for path in strings.unwrap() { + cmd.arg(path); + } + + log::debug!("Command: {}", format!("{:?}", cmd)); let output = cmd.output()?; diff --git a/toolbox-latest b/toolbox-latest new file mode 100755 index 0000000..fe8c080 --- /dev/null +++ b/toolbox-latest @@ -0,0 +1,37 @@ +#!/bin/bash + +# Define the paths +release_path="./target/release/trunk-toolbox" +debug_path="./target/debug/trunk-toolbox" +fallback_path="trunk-toolbox" + +# Check if the release and debug files exist +if [[ -e ${release_path} && -e ${debug_path} ]]; then + # If both files exist, check which one is more recent + if [[ ${release_path} -nt ${debug_path} ]]; then + # If the release file is more recent, execute it + echo "Executing ${release_path}" + ${release_path} "$@" + exit $? + else + # If the debug file is more recent, execute it + echo "Executing ${debug_path}" + ${debug_path} "$@" + exit $? + fi +elif [[ -e ${release_path} ]]; then + # If only the release file exists, execute it + echo "Executing ${release_path}" + ${release_path} "$@" + exit $? +elif [[ -e ${debug_path} ]]; then + # If only the debug file exists, execute it + echo "Executing ${debug_path}" + ${debug_path} "$@" + exit $? +else + # If neither file exists, execute the fallback path + echo "Executing ${fallback_path}" + ${fallback_path} "$@" + exit $? +fi