Skip to content

Conversation

@j178
Copy link
Owner

@j178 j178 commented Jan 17, 2026

Closes #431

@j178 j178 added the enhancement New feature or request label Jan 17, 2026
@codecov
Copy link

codecov bot commented Jan 17, 2026

Codecov Report

❌ Patch coverage is 85.84906% with 15 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.03%. Comparing base (bb8a1b4) to head (8436843).

Files with missing lines Patch % Lines
crates/prek/src/languages/script.rs 84.53% 15 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1384      +/-   ##
==========================================
- Coverage   91.05%   91.03%   -0.03%     
==========================================
  Files          87       87              
  Lines       18367    18464      +97     
==========================================
+ Hits        16724    16808      +84     
- Misses       1643     1656      +13     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions
Copy link

github-actions bot commented Jan 17, 2026

📦 Cargo Bloat Comparison

Binary size change: +0.00% (22.8 MiB → 22.8 MiB)

Expand for cargo-bloat output

Head Branch Results

 File  .text    Size        Crate Name
 0.3%   0.8% 72.9KiB        prek? <prek::cli::Command as clap_builder::derive::Subcommand>::augment_subcommands
 0.3%   0.7% 64.5KiB         prek prek::languages::<impl prek::config::Language>::run::{{closure}}::{{closure}}
 0.3%   0.6% 60.4KiB         prek prek::languages::<impl prek::config::Language>::run::{{closure}}::{{closure}}
 0.2%   0.5% 46.3KiB         prek prek::run::{{closure}}
 0.2%   0.5% 43.6KiB         prek prek::cli::run::run::run::{{closure}}
 0.2%   0.5% 42.8KiB         prek prek::languages::<impl prek::config::Language>::install::{{closure}}
 0.2%   0.5% 42.7KiB         prek prek::identify::by_extension::{{closure}}
 0.1%   0.3% 32.0KiB        prek? <prek::cli::RunArgs as clap_builder::derive::Args>::augment_args
 0.1%   0.3% 30.0KiB  serde_json? <&mut serde_json::de::Deserializer<R> as serde_core::de::Deserializer>::deserialize_struct
 0.1%   0.2% 22.2KiB         prek prek::hooks::meta_hooks::MetaHooks::run::{{closure}}
 0.1%   0.2% 22.0KiB         prek prek::hooks::meta_hooks::MetaHooks::run::{{closure}}
 0.1%   0.2% 21.6KiB         prek prek::archive::unzip::{{closure}}
 0.1%   0.2% 21.3KiB clap_builder clap_builder::parser::parser::Parser::get_matches_with
 0.1%   0.2% 20.0KiB         prek prek::cli::run::filter::collect_files_from_args::{{closure}}
 0.1%   0.2% 19.6KiB         prek prek::cli::run::filter::collect_files_from_args::{{closure}}
 0.1%   0.2% 19.6KiB         prek <prek::languages::ruby::ruby::Ruby as prek::languages::LanguageImpl>::install::{{closure}}
 0.1%   0.2% 18.8KiB         prek prek::hook::HookBuilder::build::{{closure}}
 0.1%   0.2% 18.8KiB         prek prek::hook::HookBuilder::build::{{closure}}
 0.1%   0.2% 18.8KiB         prek prek::hook::HookBuilder::build::{{closure}}
 0.1%   0.2% 18.6KiB         ring ring_core_0_17_14__x25519_ge_frombytes_vartime
36.9%  91.4%  8.4MiB              And 20082 smaller methods. Use -n N to show more.
40.4% 100.0%  9.2MiB              .text section size, the file size is 22.8MiB

Base Branch Results

 File  .text    Size        Crate Name
 0.3%   0.8% 72.2KiB        prek? <prek::cli::Command as clap_builder::derive::Subcommand>::augment_subcommands
 0.3%   0.7% 61.5KiB         prek prek::languages::<impl prek::config::Language>::run::{{closure}}::{{closure}}
 0.2%   0.6% 56.4KiB         prek prek::languages::<impl prek::config::Language>::run::{{closure}}::{{closure}}
 0.2%   0.5% 46.3KiB         prek prek::run::{{closure}}
 0.2%   0.5% 43.8KiB         prek prek::cli::run::run::run::{{closure}}
 0.2%   0.5% 42.8KiB         prek prek::languages::<impl prek::config::Language>::install::{{closure}}
 0.2%   0.5% 42.7KiB         prek prek::identify::by_extension::{{closure}}
 0.1%   0.3% 32.0KiB        prek? <prek::cli::RunArgs as clap_builder::derive::Args>::augment_args
 0.1%   0.3% 30.0KiB  serde_json? <&mut serde_json::de::Deserializer<R> as serde_core::de::Deserializer>::deserialize_struct
 0.1%   0.2% 22.2KiB         prek prek::hooks::meta_hooks::MetaHooks::run::{{closure}}
 0.1%   0.2% 22.0KiB         prek prek::hooks::meta_hooks::MetaHooks::run::{{closure}}
 0.1%   0.2% 21.5KiB         prek prek::archive::unzip::{{closure}}
 0.1%   0.2% 21.3KiB clap_builder clap_builder::parser::parser::Parser::get_matches_with
 0.1%   0.2% 20.0KiB         prek prek::cli::run::filter::collect_files_from_args::{{closure}}
 0.1%   0.2% 19.6KiB         prek prek::cli::run::filter::collect_files_from_args::{{closure}}
 0.1%   0.2% 19.6KiB         prek <prek::languages::ruby::ruby::Ruby as prek::languages::LanguageImpl>::install::{{closure}}
 0.1%   0.2% 18.8KiB         prek prek::hook::HookBuilder::build::{{closure}}
 0.1%   0.2% 18.8KiB         prek prek::hook::HookBuilder::build::{{closure}}
 0.1%   0.2% 18.8KiB         prek prek::hook::HookBuilder::build::{{closure}}
 0.1%   0.2% 18.6KiB         ring ring_core_0_17_14__x25519_ge_frombytes_vartime
36.9%  91.5%  8.4MiB              And 20101 smaller methods. Use -n N to show more.
40.3% 100.0%  9.2MiB              .text section size, the file size is 22.8MiB

@j178 j178 added this to the v0.3.0 milestone Jan 19, 2026
@j178 j178 changed the base branch from master to v03 January 20, 2026 15:35
@j178 j178 force-pushed the v03 branch 2 times, most recently from 64d7867 to 08ff2c1 Compare January 21, 2026 04:53
@j178 j178 force-pushed the inline-script branch 2 times, most recently from ac29730 to 9c3a78f Compare January 21, 2026 13:12
@j178 j178 marked this pull request as ready for review January 21, 2026 13:14
@j178 j178 requested a review from Copilot January 21, 2026 13:28
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds support for inline scripts in language: script hooks, allowing users to write small scripts directly in the entry field instead of maintaining separate script files.

Changes:

  • Adds inline script detection logic that distinguishes between script paths and inline content based on newlines and file existence
  • Implements platform-specific default shell resolution (bash/sh on Unix, pwsh/powershell/cmd on Windows)
  • Refactors parse_shebang to accept any BufRead reader for parsing shebangs from inline content
  • Adds comprehensive Unix tests for inline scripts with various configurations
  • Documents the new feature with clear examples and explanations

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
docs/languages.md Documents inline script feature with decision logic, execution details, and YAML examples
crates/prek/src/languages/script.rs Implements inline script detection, temp file creation, shell resolution, and execution
crates/prek/src/identify.rs Refactors parse_shebang to extract parse_shebang_from_reader for reuse with string content
crates/prek/tests/languages/script.rs Adds comprehensive Unix tests for inline scripts with/without shebangs, CRLF handling, and error cases

Comment on lines +175 to +398
#[test]
fn inline_script_no_shebang() {
let context = TestContext::new();
context.init_project();
context.write_pre_commit_config(indoc::indoc! {r#"
repos:
- repo: local
hooks:
- id: inline
name: inline
language: script
entry: |
echo "inline hello"
echo "$MESSAGE"
env:
MESSAGE: world
pass_filenames: false
verbose: true
"#});
context.git_add(".");

cmd_snapshot!(context.filters(), context.run(), @r"
success: true
exit_code: 0
----- stdout -----
inline...................................................................Passed
- hook id: inline
- duration: [TIME]
inline hello
world
----- stderr -----
");
}

#[test]
fn inline_script_with_env_s_shebang_and_filenames() -> Result<()> {
let context = TestContext::new();
context.init_project();

// pass_filenames defaults to true, keep it that way to exercise "$@".
context.write_pre_commit_config(indoc::indoc! {r#"
repos:
- repo: local
hooks:
- id: inline
name: inline
language: script
files: ^a\.txt$
entry: |
#!/usr/bin/env -S bash -e
printf 'args:'
printf ' %s' "$@"
printf '\n'
verbose: true
"#});

// Ensure there's at least one filename.
context.work_dir().child("a.txt").write_str("a")?;
context.git_add(".");

cmd_snapshot!(context.filters(), context.run(), @r"
success: true
exit_code: 0
----- stdout -----
inline...................................................................Passed
- hook id: inline
- duration: [TIME]
args: a.txt
----- stderr -----
");

Ok(())
}

#[test]
fn inline_script_with_sh_shebang() {
let context = TestContext::new();
context.init_project();
context.write_pre_commit_config(indoc::indoc! {r#"
repos:
- repo: local
hooks:
- id: inline
name: inline
language: script
entry: |
#!/usr/bin/env sh
echo "hello from sh"
pass_filenames: false
verbose: true
"#});
context.git_add(".");

cmd_snapshot!(context.filters(), context.run(), @r"
success: true
exit_code: 0
----- stdout -----
inline...................................................................Passed
- hook id: inline
- duration: [TIME]
hello from sh
----- stderr -----
");
}

#[test]
fn inline_script_no_shebang_crlf_line_endings() {
let context = TestContext::new();
context.init_project();

// Ensure CRLF is accepted (common on Windows checkouts) even on unix.
let config = indoc::indoc! {r#"
repos:
- repo: local
hooks:
- id: inline
name: inline
language: script
entry: "echo one\r\necho two\r\n"
pass_filenames: false
verbose: true
"#}
.replace('\n', "\r\n");

context.write_pre_commit_config(&config);
context.git_add(".");

cmd_snapshot!(context.filters(), context.run(), @r"
success: true
exit_code: 0
----- stdout -----
inline...................................................................Passed
- hook id: inline
- duration: [TIME]
one
two
----- stderr -----
");
}

#[test]
fn inline_script_no_shebang_stops_on_error() {
let context = TestContext::new();
context.init_project();
context.write_pre_commit_config(indoc::indoc! {r#"
repos:
- repo: local
hooks:
- id: inline
name: inline
language: script
entry: |
false
echo "should not print"
pass_filenames: false
verbose: true
"#});
context.git_add(".");

cmd_snapshot!(context.filters(), context.run(), @r"
success: false
exit_code: 1
----- stdout -----
inline...................................................................Failed
- hook id: inline
- duration: [TIME]
- exit code: 1
----- stderr -----
");
}

#[test]
fn multiline_entry_still_script_path() -> Result<()> {
let context = TestContext::new();
context.init_project();

// Entry is written as a YAML block scalar (contains newlines) but the first token is a
// real script path, so it should be treated as a normal `script` hook.
context.write_pre_commit_config(indoc::indoc! {r"
repos:
- repo: local
hooks:
- id: script
name: script
language: script
entry: |
./script.sh --from-entry
pass_filenames: false
verbose: true
"});

let script = context.work_dir().child("script.sh");
script.write_str(indoc::indoc! {r#"
#!/usr/bin/env bash
echo "ran script.sh $1"
"#})?;
fs_err::set_permissions(&script, std::fs::Permissions::from_mode(0o755))?;

context.git_add(".");

cmd_snapshot!(context.filters(), context.run(), @r"
success: true
exit_code: 0
----- stdout -----
script...................................................................Passed
- hook id: script
- duration: [TIME]
ran script.sh --from-entry
----- stderr -----
");

Ok(())
}
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All inline script tests are under the unix module (with #[cfg(unix)]), which means inline scripts are not tested on Windows. Given that Windows has a different default shell resolution strategy (pwsh/powershell/cmd), it's important to add tests for Windows inline scripts to ensure the feature works correctly on that platform.

Copilot uses AI. Check for mistakes.
Base automatically changed from v03 to master January 22, 2026 03:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for running multiple commands on a single “id”

2 participants