diff --git a/.github/workflows/prerelease.yaml b/.github/workflows/prerelease.yaml new file mode 100644 index 0000000..4dfaaad --- /dev/null +++ b/.github/workflows/prerelease.yaml @@ -0,0 +1,83 @@ +name: Create Pre Release Binary + +on: + pull_request: + types: [labeled] + +jobs: + build-artifacts: + if: github.event.label.name == 'prerelease' + runs-on: ubuntu-latest + name: Build Artifacts + env: + binary_name: git-diff + + steps: + - uses: actions/checkout@v4 + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: ~/.cargo/registry + key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-registry- + + - name: Cache cargo index + uses: actions/cache@v4 + with: + path: ~/.cargo/git + key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-index- + + - name: Cache cargo build + uses: actions/cache@v4 + with: + path: target + key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-build- + + - name: Build + run: cargo build --release + + - name: Create Archive Folder + run: mkdir ${{ runner.os }} + + - name: Copy Artifact + run: cp target/release/${{ env.binary_name }} ${{ runner.os }} + + - name: Create Tar Archive + run: tar -czf ${{ runner.os }}.tgz ${{ runner.os }} + + - name: Store Archive + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }} + path: ${{ runner.os }}.tgz + + create-pre-release: + needs: [build-artifacts] + runs-on: ubuntu-latest + name: Create Pre-release + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 + - name: Get version + id: get_version + run: | + VERSION=$(cat version | tr -d '\n') + echo "Version: $VERSION" + echo "VERSION=$VERSION" >> $GITHUB_OUTPUT + - name: Create Release + uses: ncipollo/release-action@v1 + with: + artifacts: "Linux/Linux.tgz" + tag: ${{ steps.get_version.outputs.VERSION }}-prerelease + allowUpdates: true + makeLatest: false + prerelease: true + commit: ${{ github.sha }} diff --git a/Cargo.lock b/Cargo.lock index a07094a..68a8332 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,7 +30,7 @@ dependencies = [ [[package]] name = "git-diff" -version = "0.1.0" +version = "1.0.2" dependencies = [ "git2", "glob", diff --git a/Cargo.toml b/Cargo.toml index 80ca36f..e980054 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "git-diff" -version = "0.1.0" +version = "1.0.2" edition = "2021" [dependencies] diff --git a/action.yaml b/action.yaml index 2b9a743..23c919c 100644 --- a/action.yaml +++ b/action.yaml @@ -20,7 +20,7 @@ runs: - name: Get Binary shell: bash run: | - BIN_URL=$(curl https://api.github.com/repos/LuisLiraC/git-diff/releases/tags/v1.0.1 | jq -r '.assets[0].browser_download_url') + BIN_URL=$(curl https://api.github.com/repos/LuisLiraC/git-diff/releases/tags/v1.0.2 | jq -r '.assets[0].browser_download_url') curl -s -L $BIN_URL -o rust-binary.tgz tar -xzvf rust-binary.tgz mv ./Linux/git-diff ./git-diff @@ -29,8 +29,4 @@ runs: id: get-git-diff shell: bash run: | - PATTERNS="${{ inputs.patterns }}" - COMMA_SEPARATED_PATTERNS=$(echo "$PATTERNS" | tr '\n' ',' | sed -e 's/,$//' | sed 's/,$//' | sed 's/ //g') - echo "Files patterns: $COMMA_SEPARATED_PATTERNS" - - ./git-diff --patterns=$COMMA_SEPARATED_PATTERNS + ./git-diff --patterns='${{ inputs.patterns }}' diff --git a/src/main.rs b/src/main.rs index 944c7f7..2e3b9f5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,11 @@ use git2::Repository; +use std::collections::HashSet; use std::env; use glob::Pattern; use std::process::Command; use std::time::Instant; -#[derive(Clone)] +#[derive(Clone, Debug)] struct PatternFilter { pattern: String, exclude: bool, @@ -32,13 +33,18 @@ fn main() { let duration = start.elapsed(); println!("Getting changed files done in: {:?}", duration); + println!("Changed files: {:?}", changed_files); + let start = Instant::now(); - let filtered_files = filter(changed_files, include_patterns_filters, exclude_patterns_filters); + let filtered_files = filter_files(changed_files, include_patterns_filters, exclude_patterns_filters); let duration = start.elapsed(); println!("Filtering files done in: {:?}", duration); let count = get_count(filtered_files.clone()); + println!("Filtered files: {:?}", filtered_files); + println!("Count: {}", count); + Command::new("sh") .arg("-c") .arg(format!("echo \"DIFF_FILES={:?}\" >> $GITHUB_OUTPUT", filtered_files)) @@ -53,10 +59,18 @@ fn main() { } fn create_patterns_filters(arg: &str) -> Vec { - let patterns = arg + let binding = arg .split('=') .last() .expect("Failed to get patterns") + .replace(" ", "") + .replace("\n", ",") + .replace("\r", "") + .replace(",,", ",") + .trim_end_matches(',') + .to_string(); + + let patterns = binding .split(',') .collect::>(); @@ -85,6 +99,7 @@ fn get_changed_files() -> Vec { let head = repository.head().expect("Failed to get HEAD"); let head_commit = head.peel_to_commit().expect("Failed to peel HEAD to commit"); + // Refers to base branch in case of pull request. For example: main let base_ref_env = env::var("GITHUB_BASE_REF").expect("Failed to get GITHUB_BASE_REF env variable"); Command::new("sh") @@ -119,49 +134,39 @@ fn get_changed_files() -> Vec { changed_files } -fn filter(changed_files: Vec, include_patterns_filters: Vec, exclude_patterns_filters: Vec) -> Vec { - let filtered_files: Vec = include_patterns_filters - .iter() - .flat_map(|pattern| filter_files_by_pattern(pattern, &changed_files, &exclude_patterns_filters)) - .collect(); +fn filter_files(changed_files: Vec, include_patterns_filters: HashSet, exclude_patterns_filters: HashSet) -> HashSet { + let mut hash_set_filtered_files = HashSet::new(); - filtered_files -} + for changed_file in changed_files.iter() { + include_patterns_filters.iter().for_each(|pattern| { + if Pattern::new(pattern).expect("Failed to create pattern").matches(changed_file) { + hash_set_filtered_files.insert(changed_file.to_string()); + } -fn filter_files_by_pattern(pattern_filter: &PatternFilter, files: &Vec, exclude_patterns: &Vec) -> Vec { - let pattern = Pattern::new(&pattern_filter.pattern).expect("Failed to create pattern"); - - let mut filtered_files: Vec = files - .iter() - .filter(|file| pattern.matches(file)) - .filter(|_| pattern_filter.exclude == false) - .map(|file| file.to_string()) - .collect(); - - for exclude_pattern in exclude_patterns.iter() { - filtered_files = filtered_files - .iter() - .filter(|file| !Pattern::new(&exclude_pattern.pattern).expect("Failed to create pattern").matches(file)) - .map(|file| file.to_string()) - .collect(); + exclude_patterns_filters.iter().for_each(|pattern| { + if Pattern::new(pattern).expect("Failed to create pattern").matches(changed_file) { + hash_set_filtered_files.remove(changed_file); + } + }); + }); } - filtered_files + hash_set_filtered_files } -fn get_count(filtered_files: Vec) -> usize { +fn get_count(filtered_files: HashSet) -> usize { filtered_files.len() } -fn categorize_filters(filters: Vec) -> (Vec, Vec) { - let mut exclude_patterns_filters: Vec = Vec::new(); - let mut include_patterns_filters: Vec = Vec::new(); +fn categorize_filters(filters: Vec) -> (HashSet, HashSet) { + let mut exclude_patterns_filters: HashSet = HashSet::new(); + let mut include_patterns_filters: HashSet = HashSet::new(); filters.iter().for_each(|pattern_filter| { if pattern_filter.exclude { - exclude_patterns_filters.push(pattern_filter.clone()); + exclude_patterns_filters.insert(pattern_filter.clone().pattern); } else { - include_patterns_filters.push(pattern_filter.clone()); + include_patterns_filters.insert(pattern_filter.clone().pattern); } }); diff --git a/src/tests/integration.rs b/src/tests/integration.rs index 7594924..d945133 100644 --- a/src/tests/integration.rs +++ b/src/tests/integration.rs @@ -8,7 +8,7 @@ mod integration { fn test_filter() { let arg = "--patterns=*.rs,!*..txt"; let files = vec![ - String::from("main.rs"), + String::from("src/main.rs"), String::from("lib.rs"), String::from("test.txt"), ]; @@ -17,14 +17,16 @@ mod integration { let (include_patterns_filters, exclude_patterns_filters) = categorize_filters(filters); - let filtered_files = filter(files, include_patterns_filters, exclude_patterns_filters); + let filtered_files = filter_files(files, include_patterns_filters, exclude_patterns_filters); let count = get_count(filtered_files.clone()); - assert_eq!( - filtered_files, - vec![String::from("main.rs"), String::from("lib.rs")] - ); + let expected_filtered_files = HashSet::from([ + String::from("src/main.rs"), + String::from("lib.rs"), + ]); + + assert_eq!(filtered_files, expected_filtered_files); assert_eq!(count, 2); } } diff --git a/src/tests/unit.rs b/src/tests/unit.rs index ee114f7..9377ca5 100644 --- a/src/tests/unit.rs +++ b/src/tests/unit.rs @@ -5,7 +5,7 @@ mod unit { use super::*; #[test] - fn test_create_patterns_filters() { + fn test_create_patterns_filters_single_line() { let arg = "--patterns=*.rs,!test/*.rs"; let filters = create_patterns_filters(arg); assert_eq!(filters.len(), 2); @@ -18,6 +18,23 @@ mod unit { assert_eq!(filters[1].exclude, true); } + #[test] + fn test_create_patterns_filters_multiple_lines() { + let arg = "--patterns=*.rs + !test/*.rs + .gitignore + "; + let filters = create_patterns_filters(arg); + + assert_eq!(filters.len(), 3); + assert_eq!(filters[0].pattern, "*.rs"); + assert_eq!(filters[0].exclude, false); + assert_eq!(filters[1].pattern, "test/*.rs"); + assert_eq!(filters[1].exclude, true); + assert_eq!(filters[2].pattern, ".gitignore"); + assert_eq!(filters[2].exclude, false); + } + #[test] fn test_categorize_filters() { let filters = vec![ @@ -33,76 +50,43 @@ mod unit { let (include_patterns_filters, exclude_patterns_filters) = categorize_filters(filters); assert_eq!(include_patterns_filters.len(), 1); assert_eq!(exclude_patterns_filters.len(), 1); - assert_eq!(include_patterns_filters[0].pattern, "*.rs"); - assert_eq!(exclude_patterns_filters[0].pattern, "test/*.rs"); + assert_eq!(include_patterns_filters.contains("*.rs"), true); + assert_eq!(exclude_patterns_filters.contains("test/*.rs"), true); } #[test] fn test_filter() { let files = vec![ - String::from("main.rs"), + String::from("src/main.rs"), String::from("lib.rs"), String::from("test.txt"), ]; - let include_patterns_filters = vec![ - PatternFilter { - pattern: String::from("*.rs"), - exclude: false, - }, - PatternFilter { - pattern: String::from("*.txt"), - exclude: false, - }, - ]; - let exclude_patterns_filters = vec![PatternFilter { - pattern: String::from("test.txt"), - exclude: true, - }]; - let filtered_files = filter(files, include_patterns_filters, exclude_patterns_filters); - assert_eq!( - filtered_files, - vec![String::from("main.rs"), String::from("lib.rs")] - ); - } - - #[test] - fn test_filter_files_by_pattern() { - let pattern_filter = PatternFilter { - pattern: String::from("*.rs"), - exclude: false, - }; - let files = vec![ - String::from("main.rs"), + let include_patterns_filters = HashSet::from([ + String::from("*.rs"), + String::from("src/**"), + String::from("*.txt"), + ]); + let exclude_patterns_filters = HashSet::from([ + String::from("test.txt") + ]); + let filtered_files = filter_files(files, include_patterns_filters, exclude_patterns_filters); + let expected_filtered_files = HashSet::from([ + String::from("src/main.rs"), String::from("lib.rs"), - String::from("test.txt"), - ]; - let filtered = filter_files_by_pattern(&pattern_filter, &files, &Vec::new()); - assert_eq!( - filtered, - vec![String::from("main.rs"), String::from("lib.rs")] - ); + ]); + + assert_eq!(filtered_files, expected_filtered_files); } #[test] fn test_filter_exclude_files_exclusion() { - let mut filtered_files: Vec = Vec::new(); - let mut exclude_patterns_filters: Vec = Vec::new(); - let mut include_patterns_filters: Vec = Vec::new(); - - include_patterns_filters.push(PatternFilter { - pattern: String::from("*.rs"), - exclude: false, - }); - - include_patterns_filters.push(PatternFilter { - pattern: String::from("*.txt"), - exclude: false, - }); - - exclude_patterns_filters.push(PatternFilter { - pattern: String::from("test.txt"), - exclude: true, - }); + let exclude_patterns_filters = HashSet::from([ + String::from("test.txt"), + ]); + let include_patterns_filters = HashSet::from([ + String::from("*.rs"), + String::from("*.txt"), + ]); let files = vec![ String::from("main.rs"), @@ -111,23 +95,23 @@ mod unit { String::from("test.txt"), ]; - for pattern in include_patterns_filters.iter() { - filtered_files.extend(filter_files_by_pattern(&pattern, &files, &exclude_patterns_filters)); - } + let filtered_files = filter_files(files, include_patterns_filters, exclude_patterns_filters); + let expected_filtered_files = HashSet::from([ + String::from("main.rs"), + String::from("lib.rs"), + String::from("version.txt"), + ]); - assert_eq!( - filtered_files, - vec![String::from("main.rs"), String::from("lib.rs"), String::from("version.txt")] - ); + assert_eq!(filtered_files, expected_filtered_files); } #[test] fn test_get_count() { - let files = vec![ + let files = HashSet::from([ String::from("main.rs"), String::from("lib.rs"), String::from("version.txt"), - ]; + ]); let count = get_count(files); assert_eq!(count, 3); } diff --git a/version b/version new file mode 100644 index 0000000..b482243 --- /dev/null +++ b/version @@ -0,0 +1 @@ +v1.0.2 \ No newline at end of file