-
-
Notifications
You must be signed in to change notification settings - Fork 123
Support inline script for language: script
#1384
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Codecov Report❌ Patch coverage is
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. 🚀 New features to boost your workflow:
|
📦 Cargo Bloat ComparisonBinary size change: +0.00% (22.8 MiB → 22.8 MiB) Expand for cargo-bloat outputHead Branch ResultsBase Branch Results |
64d7867 to
08ff2c1
Compare
ac29730 to
9c3a78f
Compare
There was a problem hiding this 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_shebangto accept anyBufReadreader 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 |
| #[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(()) | ||
| } |
Copilot
AI
Jan 21, 2026
There was a problem hiding this comment.
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.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Closes #431