diff --git a/.github/workflows/validate-pr.yml b/.github/workflows/validate-pr.yml index e4954e1..b801a3f 100644 --- a/.github/workflows/validate-pr.yml +++ b/.github/workflows/validate-pr.yml @@ -26,21 +26,31 @@ jobs: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} - - name: Detect changes - uses: dorny/paths-filter@v2 - id: changes - with: - filters: | - workspace: - - '.github/workflows/trunk-on-push.yml' - - 'src/**' - - 'Cargo.lock' - - 'Cargo.toml' - shelltools: - - 'tools/shell/**' + - name: Workspace changed + id: workspace-changed + run: | + bash ./tools/shell/actions/detect-changes.sh \ + --pattern '^src/.*$' \ + --compare-with origin/${{ github.base_ref }} + if [[ -f "$GITHUB_OUTPUT" ]] && grep -q "changed_files" "$GITHUB_OUTPUT"; then + echo "change=true" >> $GITHUB_OUTPUT + else + echo "change=false" >> $GITHUB_OUTPUT + fi + + - name: Workspace changed + id: shelltools-changed + run: | + bash ./tools/shell/actions/detect-changes.sh \ + --pattern '^tools/shell/.*$' \ + --compare-with origin/${{ github.base_ref }} + if [[ -f "$GITHUB_OUTPUT" ]] && grep -q "changed_files" "$GITHUB_OUTPUT"; then + echo "change=true" >> $GITHUB_OUTPUT + else + echo "change=false" >> $GITHUB_OUTPUT + fi - name: Workspace cache - id: workspace-cache uses: ./.github/actions/workspace-cache - name: Validate the commit messages @@ -55,34 +65,34 @@ jobs: deactivate - name: Install rust - if: steps.changes.outputs.workspace == 'true' + if: steps.workspace-changed.outputs.change == 'true' run: | sudo apt-get update sudo apt-get install libfontconfig1-dev curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - name: Install project dependencies - if: steps.changes.outputs.workspace == 'true' + if: steps.workspace-changed.outputs.change == 'true' run: cargo install --path . --force - name: Check formatting - if: steps.changes.outputs.workspace == 'true' + if: steps.workspace-changed.outputs.change == 'true' run: cargo fmt -- --check - name: Lint sources - if: steps.changes.outputs.workspace == 'true' + if: steps.workspace-changed.outputs.change == 'true' run: cargo clippy -- -D warnings - name: Unit test - if: steps.changes.outputs.workspace == 'true' + if: steps.workspace-changed.outputs.change == 'true' run: cargo test - name: Build test - if: steps.changes.outputs.workspace == 'true' + if: steps.workspace-changed.outputs.change == 'true' run: cargo build - name: Lint shell - if: steps.changes.outputs.workspace == 'true' + if: steps.shelltools-changed.outputs.change == 'true' run: | sudo apt install shellcheck shellcheck tools/shell/*.sh tools/shell/utils/*.sh diff --git a/Cargo.lock b/Cargo.lock index 0d7e6d2..81cf9e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -304,6 +304,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "const-random" version = "0.1.18" @@ -615,6 +625,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.10.0", + "objc2", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -659,18 +679,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -[[package]] -name = "enum-as-inner" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "equivalent" version = "1.0.2" @@ -1016,46 +1024,70 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "hickory-proto" -version = "0.25.2" +name = "hickory-net" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502" +checksum = "e2295ed2f9c31e471e1428a8f88a3f0e1f4b27c15049592138d1eebe9c35b183" dependencies = [ "async-trait", "cfg-if", "data-encoding", - "enum-as-inner", "futures-channel", "futures-io", "futures-util", + "hickory-proto", "idna", "ipnet", + "jni", + "rand 0.10.1", + "thiserror 2.0.17", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "hickory-proto" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bab31817bfb44672a252e97fe81cd0c18d1b2cf892108922f6818820df8c643" +dependencies = [ + "data-encoding", + "idna", + "ipnet", + "jni", "once_cell", - "rand 0.9.2", + "prefix-trie", + "rand 0.10.1", "ring", "thiserror 2.0.17", "tinyvec", - "tokio", "tracing", "url", ] [[package]] name = "hickory-resolver" -version = "0.25.2" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a" +checksum = "f0d58d28879ceecde6607729660c2667a081ccdc082e082675042793960f178c" dependencies = [ "cfg-if", "futures-util", + "hickory-net", "hickory-proto", "ipconfig", + "ipnet", + "jni", "moka", + "ndk-context", "once_cell", "parking_lot 0.12.5", - "rand 0.9.2", + "rand 0.10.1", "resolv-conf", "smallvec", + "system-configuration", "thiserror 2.0.17", "tokio", "tracing", @@ -1182,7 +1214,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2", "system-configuration", "tokio", "tower-layer", @@ -1373,7 +1405,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d40460c0ce33d6ce4b0630ad68ff63d6661961c48b6dba35e5a4d81cfb48222" dependencies = [ - "socket2 0.6.3", + "socket2", "widestring", "windows-registry", "windows-result", @@ -1385,6 +1417,9 @@ name = "ipnet" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +dependencies = [ + "serde", +] [[package]] name = "iri-string" @@ -1411,6 +1446,55 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys", + "log", + "simd_cesu8", + "thiserror 2.0.17", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "jpeg-decoder" version = "0.3.2" @@ -1723,9 +1807,9 @@ checksum = "224484c5d09285a7b8cb0a0c117e847ebd14cb6e4470ecf68cdb89c503b0edb9" [[package]] name = "mongodb" -version = "3.5.2" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c5941683db2ab2697f71e58dc0319024e808d3b28e7cf20f4bfb445fe54a30b" +checksum = "276ba0cd571553d1f6936c6f180964776ece6ab7507dc8765f8a9c9c49d8cd00" dependencies = [ "base64", "bitflags 2.10.0", @@ -1736,6 +1820,7 @@ dependencies = [ "futures-io", "futures-util", "hex", + "hickory-net", "hickory-proto", "hickory-resolver", "hmac", @@ -1748,13 +1833,12 @@ dependencies = [ "rand 0.9.2", "rustc_version_runtime", "rustls", - "rustversion", "serde", "serde_bytes", "serde_with", "sha1", "sha2", - "socket2 0.6.3", + "socket2", "stringprep", "strsim", "take_mut", @@ -1769,9 +1853,9 @@ dependencies = [ [[package]] name = "mongodb-internal-macros" -version = "3.5.2" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47021a12bbf0dffde9c890fa2d36ff6ae342c532016226b04a42301b2b912660" +checksum = "99ceb1a9a1018e470077ec94cf3a8c2d0e6da542b2c05ea95a59a0a627147375" dependencies = [ "macro_magic", "proc-macro2", @@ -1823,6 +1907,12 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + [[package]] name = "noisy_float" version = "0.2.0" @@ -1891,6 +1981,15 @@ dependencies = [ "libm", ] +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + [[package]] name = "objc2-core-foundation" version = "0.3.2" @@ -1898,6 +1997,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ "bitflags 2.10.0", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.10.0", + "objc2", ] [[package]] @@ -1910,6 +2027,17 @@ dependencies = [ "objc2-core-foundation", ] +[[package]] +name = "objc2-open-directory" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb82bed227edf5201dfedf072bba4015a33d3d4a98519837295a90f0a23f676d" +dependencies = [ + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + [[package]] name = "octorust" version = "0.10.0" @@ -2184,6 +2312,17 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prefix-trie" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf6e3177f0684016a5c209b00882e15f8bdd3f3bb48f0491df10cd102d0c6e7" +dependencies = [ + "either", + "ipnet", + "num-traits", +] + [[package]] name = "prettyplease" version = "0.2.37" @@ -2216,7 +2355,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.5.10", + "socket2", "thiserror 2.0.17", "tokio", "tracing", @@ -2253,7 +2392,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2", "tracing", "windows-sys 0.60.2", ] @@ -2601,7 +2740,7 @@ dependencies = [ [[package]] name = "rust-workspace" -version = "0.10.0" +version = "0.10.1" dependencies = [ "cargo-husky", "ciborium", @@ -2800,9 +2939,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "indexmap", "itoa", @@ -2890,6 +3029,22 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "simple_asn1" version = "0.6.3" @@ -2914,16 +3069,6 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "socket2" version = "0.6.3" @@ -3008,15 +3153,16 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.38.4" +version = "0.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ab6a2f8bfe508deb3c6406578252e491d299cbbf3bc0529ecc3313aee4a52f" +checksum = "14311e7e9a03114cd4b65eedd54e8fed2945e17f08586ae97ef53bc0669f9581" dependencies = [ "libc", "memchr", "ntapi", "objc2-core-foundation", "objc2-io-kit", + "objc2-open-directory", "windows", ] @@ -3166,9 +3312,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.51.1" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", @@ -3176,7 +3322,7 @@ dependencies = [ "parking_lot 0.12.5", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.3", + "socket2", "tokio-macros", "windows-sys 0.61.2", ] @@ -3212,6 +3358,7 @@ dependencies = [ "futures-core", "futures-io", "futures-sink", + "futures-util", "pin-project-lite", "tokio", ] diff --git a/Cargo.toml b/Cargo.toml index 0044605..6f9f589 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "rust-workspace" -version = "0.10.0" +version = "0.10.1" edition = "2021" -rust-version = "1.94.1" +rust-version = "1.95.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -11,10 +11,10 @@ colored = "3.1.1" hyper = { version = "1.9.0", features = ["full"] } hyper-util = { version = "0.1.20", features = ["full"] } http-body-util = "0.1.2" -tokio = { version = "1.51.0", features = ["full"] } +tokio = { version = "1.52.3", features = ["full"] } meval = "0.2" rand = "0.10.1" -sysinfo = "0.38.4" +sysinfo = "0.39.2" # ML Packages for Model Training linfa = "0.8.1" linfa-logistic = { version = "0.8.1", features = ["serde"] } @@ -25,9 +25,9 @@ ciborium = "0.2.2" plotters = "0.3.6" # GitHub data pipeline octorust = "0.10.0" -serde_json = "1.0.149" +serde_json = "1.0.150" regex = "1.12.3" -mongodb = { version = "3.5.2", features = ["sync"] } +mongodb = { version = "3.7.0", features = ["sync"] } [dev-dependencies] diff --git a/tools/shell/actions/detect-changes.md b/tools/shell/actions/detect-changes.md new file mode 100644 index 0000000..f5cd5f8 --- /dev/null +++ b/tools/shell/actions/detect-changes.md @@ -0,0 +1,103 @@ +# detect-changes.sh + +## Purpose + +Detect files changed between commits matching a regex pattern + +## Usage + +```bash +./tools/shell/actions/detect-changes.sh --pattern PATTERN [--compare-with COMPARE_WITH] +``` + +### GitHub Actions usage: + +```yaml +... +on: + pull_request: + types: [opened, synchronize, reopened] + +jobs: + detect-changes: + runs-on: ubuntu-latest + outputs: + shell: ${{ steps.shell-changed.outputs.changed }} + typescript: ${{ steps.typescript-changed.outputs.changed }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Shell files changed + id: shell-changed + run: | + bash ./tools/shell/actions/detect-changes.sh \ + --pattern '\.sh$' \ + --compare-with origin/${{ github.base_ref }} + if [[ -f "$GITHUB_OUTPUT" ]] && grep -q "changed_files" "$GITHUB_OUTPUT"; then + echo "change=true" >> $GITHUB_OUTPUT + else + echo "change=false" >> $GITHUB_OUTPUT + fi + + - name: TypeScript files changed + id: typescript-changed + run: | + bash ./tools/shell/actions/detect-changes.sh \ + --pattern '\.ts$' \ + --compare-with origin/${{ github.base_ref }} + if [[ -f "$GITHUB_OUTPUT" ]] && grep -q "changed_files" "$GITHUB_OUTPUT"; then + echo "change=true" >> $GITHUB_OUTPUT + else + echo "change=false" >> $GITHUB_OUTPUT + fi +... +``` + +```yaml +... +on: + push: + branches: [main] + +jobs: + detect-changes: + runs-on: ubuntu-latest + outputs: + backend: ${{ steps.backend-changed.outputs.changed }} + frontend: ${{ steps.frontend-changed.outputs.changed }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Backend changed + id: backend-changed + run: | + bash ./tools/shell/actions/detect-changes.sh \ + --pattern '^src/backend/.*\.(ts)$' \ + --compare-with HEAD~1 + if [[ -f "$GITHUB_OUTPUT" ]] && grep -q "changed_files" "$GITHUB_OUTPUT"; then + echo "change=true" >> $GITHUB_OUTPUT + else + echo "change=false" >> $GITHUB_OUTPUT + fi + + - name: Drontend changed + id: frontend-changed + run: | + bash ./tools/shell/actions/detect-changes.sh \ + --pattern '^src/frontend/.*\.(ts|html|css)$' \ + --compare-with HEAD~1 + if [[ -f "$GITHUB_OUTPUT" ]] && grep -q "changed_files" "$GITHUB_OUTPUT"; then + echo "change=true" >> $GITHUB_OUTPUT + else + echo "change=false" >> $GITHUB_OUTPUT + fi +... +``` diff --git a/tools/shell/actions/detect-changes.sh b/tools/shell/actions/detect-changes.sh new file mode 100644 index 0000000..dbb6293 --- /dev/null +++ b/tools/shell/actions/detect-changes.sh @@ -0,0 +1,213 @@ +#!/bin/bash + +######################################################################################## +# Script: detect-changes.sh +# Purpose: Detect files changed between commits matching a regex pattern +# Usage: ./detect-changes.sh --pattern PATTERN [--compare-with COMPARE_WITH] [--verbose] +######################################################################################## + +set -euo pipefail + +# Default values +PATTERN="" +COMPARE_WITH="origin/main" +VERBOSE=false + +# Color codes for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Print usage instructions. +print_usage() { + cat << EOF +Usage: $0 --pattern PATTERN [OPTIONS] + +Required: + --pattern PATTERN Regular expression pattern to match files + +Optional: + --compare-with COMPARE_WITH Branch or commit to compare against + (default: origin/main) + --verbose Enable verbose output + --help Display this help message + +Examples: + $0 --pattern '\.py$' + $0 --pattern '^src/.*\.ts$' --compare-with develop + $0 --pattern '(package\.json|\.lockfile)$' --compare-with HEAD~3 + +EOF +} + +# Print an informational message. +log_info() { + echo -e "${GREEN}[INFO]${NC} $*" >&2 +} + +# Print a warning message. +log_warn() { + # shellcheck disable=SC2317 + echo -e "${YELLOW}[WARN]${NC} $*" >&2 +} + +# Print an error message. +log_error() { + echo -e "${RED}[ERROR]${NC} $*" >&2 +} + +# Print a debugging message. +log_verbose() { + if [[ "$VERBOSE" == true ]]; then + echo -e "${YELLOW}[DEBUG]${NC} $*" >&2 + fi +} + +# Parse input arguments. +parse_arguments() { + while [[ $# -gt 0 ]]; do + case "$1" in + --pattern) + if [[ -z "${2:-}" ]]; then + log_error "Pattern argument requires a value" + print_usage + exit 1 + fi + PATTERN="$2" + shift 2 + ;; + --compare-with) + if [[ -z "${2:-}" ]]; then + log_error "compare-with argument requires a value" + print_usage + exit 1 + fi + COMPARE_WITH="$2" + shift 2 + ;; + --verbose) + VERBOSE=true + shift + ;; + --help) + print_usage + exit 0 + ;; + *) + log_error "Unknown option: $1" + print_usage + exit 1 + ;; + esac + done +} + +# Validate input argument values. +validate_inputs() { + if ! git rev-parse --git-dir > /dev/null 2>&1; then + log_error "Not a git repository" + exit 1 + fi + + if [[ -z "$PATTERN" ]]; then + log_error "Pattern is required" + print_usage + exit 1 + fi + + local TEST_STRING + TEST_STRING="test_file_pattern.txt" + if ! grep -E "$PATTERN" /dev/null 2>&1; then + if ! echo "$TEST_STRING" | grep -E "$PATTERN" 2>/dev/null && ! echo "" | grep -E "$PATTERN" 2>/dev/null; then + local GREP_ERROR + GREP_ERROR=$(echo "$TEST_STRING" | grep -E "$PATTERN" 2>&1 || true) + if echo "$GREP_ERROR" | grep -qi "invalid\|error"; then + log_error "Invalid regex pattern: $PATTERN" + log_error "Grep error: $GREP_ERROR" + exit 1 + fi + fi + fi + + log_verbose "Pattern: $PATTERN" + log_verbose "Compare with: $COMPARE_WITH" +} + +# Check whether a git ref exists. +check_ref_exists() { + local REF + REF="$1" + if ! git rev-parse --verify "$REF" > /dev/null 2>&1; then + log_error "Reference not found: $REF" + log_info "Available branches:" + git branch -a | sed 's/^/ /' + exit 1 + fi +} + +# Collect changed files between git refs. +get_changed_files() { + local BASE_REF + BASE_REF="$1" + local HEAD_REF + HEAD_REF="HEAD" + + log_verbose "Fetching diff between $BASE_REF and $HEAD_REF" + + git diff --name-only --diff-filter=AMDRC "$BASE_REF..$HEAD_REF" +} + +# Filter files using a pattern provided via input arguments. +filter_files_by_pattern() { + local REGX_PATTERN + REGX_PATTERN="$1" + + local FILE + while IFS= read -r FILE; do + if [[ -z "$FILE" ]]; then + continue + fi + + log_verbose "Checking file: $FILE" + + if [[ "$FILE" =~ $REGX_PATTERN ]]; then + echo "$FILE" + fi + done +} + +# Entrypoint. +main() { + parse_arguments "$@" + validate_inputs + + log_info "Detecting changes matching pattern: $PATTERN" + log_info "Comparing HEAD against: $COMPARE_WITH" + + check_ref_exists "$COMPARE_WITH" + + local CHANGED_FILES + CHANGED_FILES=$(get_changed_files "$COMPARE_WITH" | filter_files_by_pattern "$PATTERN") + + if [[ -z "$CHANGED_FILES" ]]; then + log_info "No matching files found" + exit 0 + fi + + log_info "Found matching files:" + echo "$CHANGED_FILES" | while IFS= read -r file; do + echo " - $file" + done + + if [[ -n "${GITHUB_OUTPUT:-}" ]]; then + local FILES_JSON + FILES_JSON=$(echo "$CHANGED_FILES" | jq -R -s -c 'split("\n")[:-1]') + echo "changed_files=$FILES_JSON" >> "$GITHUB_OUTPUT" + log_verbose "GitHub Actions output set: changed=$FILES_JSON" + fi + + exit 0 +} + +main "$@"