Skip to content

feat(hook): add env support to hooks#709

Merged
jdx merged 1 commit intomainfrom
feat/hook-env
Feb 28, 2026
Merged

feat(hook): add env support to hooks#709
jdx merged 1 commit intomainfrom
feat/hook-env

Conversation

@jdx
Copy link
Copy Markdown
Owner

@jdx jdx commented Feb 28, 2026

Summary

  • Add env property to the Hook class in Config.pkl and the Rust Hook struct
  • Hook-level env vars are merged into each step's env, with step-level env taking precedence
  • This fixes the docs/public/python-project.pkl example which already used env on a hook but got Cannot find property 'env' in object of type 'hk.Config#Hook'

Closes #706

Test plan

  • Unit tests: all 134 cargo tests pass
  • Integration tests: added test/hook_env.bats with 3 tests covering:
    • Hook env vars are passed to steps
    • Step-level env takes precedence over hook-level env
    • Hook env does not leak to other hooks

🤖 Generated with Claude Code


Note

Medium Risk
Changes how step environments are constructed by merging new hook-level env into every step at init time, which can alter runtime behavior for existing configs that start using hooks.<name>.env. Default is empty so unaffected hooks should behave the same.

Overview
Adds a new env field to Hook in Config.pkl and the Rust Hook struct, allowing environment variables to be defined once per hook.

During Hook::init, hook-level env is merged into each Step (and into each step inside a Group), with step-level env taking precedence.

Introduces test/hook_env.bats to validate propagation, precedence, and that hook env does not leak across hooks.

Written by Cursor Bugbot for commit 9dce10a. This will update automatically on new commits. Configure here.

Allow setting environment variables at the hook level via `env` in
hk.pkl. Hook-level env vars are merged into each step, with step-level
env taking precedence. This fixes the python-project.pkl example which
already used this syntax.

Closes #706

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the configuration system by enabling the definition of environment variables directly at the hook level. This provides greater flexibility and simplifies the management of environment-specific settings for hook execution, while also ensuring proper precedence rules when combined with step-level environment variables. The change addresses a previous limitation and improves the overall robustness of hook configurations.

Highlights

  • Hook-level Environment Variables: Introduced an env property to the Hook class in Config.pkl and the corresponding Rust Hook struct, allowing environment variables to be defined directly on hooks.
  • Environment Variable Merging Logic: Implemented logic to merge hook-level environment variables into each step's environment, ensuring that step-level environment variables take precedence over hook-level ones.
  • Bug Fix and Example Correction: Resolved an issue where the docs/public/python-project.pkl example incorrectly used env on a hook, which previously resulted in a 'Cannot find property 'env'' error.
  • Comprehensive Testing: Added new integration tests to verify the correct behavior of hook-level environment variables, covering inheritance, precedence, and isolation between hooks.
Changelog
  • pkl/Config.pkl
    • Added the env property to the Hook class, allowing environment variables to be defined at the hook level.
  • src/hook.rs
    • Introduced the env field to the Hook struct to store hook-level environment variables.
    • Implemented logic within Hook::init to merge hook-level environment variables into step-level environment variables, ensuring step-level variables take precedence.
  • test/hook_env.bats
    • Created new integration tests to verify that hook-level environment variables are correctly passed to steps.
    • Added tests to confirm that step-level environment variables override hook-level ones.
    • Included a test to ensure hook-level environment variables do not leak to other hooks.
Activity
  • All 134 cargo unit tests passed.
  • New integration tests were added in test/hook_env.bats to cover hook environment variable functionality.
  • Integration tests confirm hook env vars are passed to steps, step-level env takes precedence, and hook env does not leak to other hooks.
  • The pull request was generated with Claude Code.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request adds support for defining environment variables at the hook level, which are then inherited by the steps within that hook. The implementation is solid, with changes to the Pkl configuration, the Rust Hook struct, and the initialization logic. The new functionality is also well-tested with new integration tests. I have one suggestion to refactor the logic for merging environment variables to improve code clarity and remove duplication.

Comment thread src/hook.rs
Comment on lines +294 to +309
if !self.env.is_empty() {
match step_or_group {
StepOrGroup::Step(step) => {
for (key, value) in &self.env {
step.env.entry(key.clone()).or_insert_with(|| value.clone());
}
}
StepOrGroup::Group(group) => {
for step in group.steps.values_mut() {
for (key, value) in &self.env {
step.env.entry(key.clone()).or_insert_with(|| value.clone());
}
}
}
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

There's some code duplication in how hook-level environment variables are merged into steps and groups. You can refactor this to be more DRY and slightly more efficient by using a trait object to get an iterator over the steps, regardless of whether it's a single step or a group. This avoids creating an intermediate Vec and keeps the merging logic in one place.

            if !self.env.is_empty() {
                let steps_iter: Box<dyn Iterator<Item = &mut crate::step::Step>> = match step_or_group {
                    StepOrGroup::Step(step) => Box::new(std::iter::once(step.as_mut())),
                    StepOrGroup::Group(group) => Box::new(group.steps.values_mut()),
                };

                for step in steps_iter {
                    for (key, value) in &self.env {
                        step.env.entry(key.clone()).or_insert_with(|| value.clone());
                    }
                }
            }

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Feb 28, 2026

Greptile Summary

Added hook-level environment variable support to allow setting environment variables at the hook level that are inherited by all steps within that hook. The implementation correctly handles precedence where step-level env vars override hook-level ones using or_insert_with(). This fixes the existing docs/public/python-project.pkl example which was using this feature before it was implemented. The change includes proper schema definition in Pkl, Rust implementation with backward compatibility via #[serde(default)], and comprehensive integration tests.

Confidence Score: 5/5

  • This PR is safe to merge with no issues found
  • The implementation is straightforward, well-tested with comprehensive integration tests, uses proper Rust patterns for precedence handling, maintains backward compatibility with #[serde(default)], and fixes an existing example that was already using this feature. All 134 cargo tests pass.
  • No files require special attention

Important Files Changed

Filename Overview
pkl/Config.pkl Added hook-level env property with clear documentation and default empty mapping
src/hook.rs Implemented env merging logic with correct precedence handling for both steps and groups
test/hook_env.bats Comprehensive test coverage for hook env functionality with 3 well-structured tests

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Hook with env vars] --> B{StepOrGroup?}
    B -->|Step| C[Merge hook env into step.env]
    B -->|Group| D[Iterate through group.steps]
    D --> C
    C --> E{Key exists in step.env?}
    E -->|Yes| F[Keep step-level value<br/>step takes precedence]
    E -->|No| G[Add hook-level value]
    F --> H[Step executes with merged env]
    G --> H
Loading

Last reviewed commit: 9dce10a

@jdx jdx merged commit 15029e6 into main Feb 28, 2026
24 checks passed
@jdx jdx deleted the feat/hook-env branch February 28, 2026 11:43
@jdx jdx mentioned this pull request Feb 28, 2026
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Mar 11, 2026
This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [hk](https://github.com/jdx/hk) | minor | `1.36.0` → `1.38.0` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>jdx/hk (hk)</summary>

### [`v1.38.0`](https://github.com/jdx/hk/blob/HEAD/CHANGELOG.md#1380---2026-03-06)

[Compare Source](jdx/hk@v1.37.0...v1.38.0)

##### 🚀 Features

- **(hook)** add `fail_on_fix` option by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;725](jdx/hk#725)

##### 🐛 Bug Fixes

- **(builtins)** remove redundant check/check\_diff from builtins by [@&#8203;nkakouros](https://github.com/nkakouros) in [#&#8203;726](jdx/hk#726)

##### 📦️ Dependency Updates

- update anthropics/claude-code-action digest to [`26ec041`](jdx/hk@26ec041) by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;720](jdx/hk#720)
- update jdx/mise-action digest to [`e79ddf6`](jdx/hk@e79ddf6) by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;721](jdx/hk#721)
- update actions-rust-lang/setup-rust-toolchain digest to [`a0b538f`](jdx/hk@a0b538f) by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;719](jdx/hk#719)
- update rust crate tokio to v1.50.0 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;722](jdx/hk#722)

### [`v1.37.0`](https://github.com/jdx/hk/blob/HEAD/CHANGELOG.md#1370---2026-03-03)

[Compare Source](jdx/hk@v1.36.0...v1.37.0)

##### 🚀 Features

- **(hook)** add env support to hooks by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;709](jdx/hk#709)
- parse Go-style diffs by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;704](jdx/hk#704)

##### 🐛 Bug Fixes

- **(builtins)** strip extra trailing newlines in end-of-file-fixer by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;708](jdx/hk#708)
- **(docs)** correctly document what --all is about by [@&#8203;nkakouros](https://github.com/nkakouros) in [#&#8203;715](jdx/hk#715)
- **(git)** exclude untracked files from unstaged\_files set by [@&#8203;nkakouros](https://github.com/nkakouros) in [#&#8203;716](jdx/hk#716)
- **(hkrc)** config format and load order by [@&#8203;ivy](https://github.com/ivy) in [#&#8203;710](jdx/hk#710)
- **(release)** write release notes to file instead of capturing stdout by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;688](jdx/hk#688)
- **(release)** make release notes editorialization non-blocking by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;690](jdx/hk#690)
- **(step)** gate check\_diff forced check\_first on Fix mode only by [@&#8203;nkakouros](https://github.com/nkakouros) in [#&#8203;717](jdx/hk#717)

##### 📚 Documentation

- **(shanty)** add audio player with sea shanty recording by [@&#8203;jdx](https://github.com/jdx) in [67a25ad](jdx/hk@67a25ad)
- document config file search paths by [@&#8203;ivy](https://github.com/ivy) in [#&#8203;701](jdx/hk#701)
- require AI disclosure on GitHub comments by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;703](jdx/hk#703)

##### 🔍 Other Changes

- replace gen-release-notes script with communique by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;700](jdx/hk#700)
- add autofix.ci workflow by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;705](jdx/hk#705)

##### 📦️ Dependency Updates

- lock file maintenance by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;686](jdx/hk#686)
- update taiki-e/upload-rust-binary-action digest to [`f391289`](jdx/hk@f391289) by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;692](jdx/hk#692)
- update anthropics/claude-code-action digest to [`c22f7c3`](jdx/hk@c22f7c3) by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;691](jdx/hk#691)
- update rust crate libc to v0.2.181 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;694](jdx/hk#694)
- update rust crate clap to v4.5.58 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;693](jdx/hk#693)
- lock file maintenance by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;695](jdx/hk#695)
- update anthropics/claude-code-action digest to [`edd85d6`](jdx/hk@edd85d6) by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;698](jdx/hk#698)
- update rust crate clap to v4.5.60 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;699](jdx/hk#699)
- lock file maintenance by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;702](jdx/hk#702)
- lock file maintenance by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;711](jdx/hk#711)

##### New Contributors

- [@&#8203;ivy](https://github.com/ivy) made their first contribution in [#&#8203;710](jdx/hk#710)
- [@&#8203;nkakouros](https://github.com/nkakouros) made their first contribution in [#&#8203;715](jdx/hk#715)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My40OS4wIiwidXBkYXRlZEluVmVyIjoiNDMuNTcuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiUmVub3ZhdGUgQm90IiwiYXV0b21hdGlvbjpib3QtYXV0aG9yZWQiLCJkZXBlbmRlbmN5LXR5cGU6Om1pbm9yIl19-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant