feat: experimental kernel porting tool - #776
Conversation
Coverage report —
|
| Name | Stmts | Miss | Cover | Missing |
|---|---|---|---|---|
| src/kernels/__init__.py | 14 | 0 | 100% | |
| src/kernels/_system.py | 6 | 1 | 83% | 10 |
| src/kernels/_versions.py | 130 | 14 | 89% | 53, 59-60, 63-64, 102, 165-170, 199, 219 |
| src/kernels/archs.py | 56 | 1 | 98% | 94 |
| src/kernels/backends.py | 213 | 62 | 71% | 42, 46, 50-53, 70, 92, 110, 119, 123, 127-129, 150, 159, 163, 167-169, 190, 201, 203, 210-213, 226, 230, 234-254, 262, 285-305 |
| src/kernels/compat.py | 9 | 1 | 89% | 5 |
| src/kernels/deps.py | 70 | 1 | 99% | 56 |
| src/kernels/hf_hub.py | 63 | 2 | 97% | 21, 23 |
| src/kernels/importer.py | 44 | 5 | 89% | 80, 84, 87, 101-102 |
| src/kernels/install.py | 21 | 7 | 67% | 76-100 |
| src/kernels/layer/__init__.py | 6 | 0 | 100% | |
| src/kernels/layer/_interval_tree.py | 103 | 4 | 96% | 23, 52, 147, 150 |
| src/kernels/layer/device.py | 48 | 14 | 71% | 42, 47-49, 91, 96-98, 101, 149, 152, 155-157 |
| src/kernels/layer/func.py | 85 | 6 | 93% | 90, 115, 191, 311, 338, 368 |
| src/kernels/layer/globals.py | 5 | 0 | 100% | |
| src/kernels/layer/kernelize.py | 80 | 8 | 90% | 258, 293, 301-302, 308, 312, 328-330 |
| src/kernels/layer/layer.py | 215 | 14 | 93% | 182, 229, 256, 390, 470-471, 492, 500, 511, 540, 544, 557, 610, 640 |
| src/kernels/layer/mode.py | 14 | 0 | 100% | |
| src/kernels/layer/repos.py | 144 | 42 | 71% | 27, 33, 36-43, 63-64, 70, 73-76, 90, 94, 103-104, 110, 113-116, 123-124, 130, 133-136, 143-144, 150, 153-156, 163-164, 170, 173-176, 257 |
| src/kernels/load.py | 71 | 2 | 97% | 338, 378 |
| src/kernels/locking.py | 89 | 64 | 28% | 35-83, 91-98, 102-125, 137, 152-159, 165-175, 179-186 |
| src/kernels/python_deps.py | 58 | 6 | 90% | 59-60, 64-65, 101, 104 |
| src/kernels/resolver.py | 156 | 2 | 99% | 220, 226 |
| src/kernels/status.py | 50 | 2 | 96% | 25, 79 |
| src/kernels/validate.py | 88 | 5 | 94% | 9, 100, 167, 190-191 |
| src/kernels/variants.py | 278 | 19 | 93% | 65, 96, 117, 147, 256-257, 299-302, 304, 388-394, 400-406, 437-443, 455-461 |
| src/kernels/verify.py | 127 | 6 | 95% | 46, 202-204, 318-319 |
| TOTAL | 2243 | 288 | 87% |
Updated by the Test kernels workflow on commit 247bdfbde43ec5b9866263a2d1c89257952a187d.
We might want to elaborate on what overlay means in this context.
Is the manual specification for
How are those pins derived?
Do the users have to specify More comments
|
the docs on the readme may already fill this need https://github.com/huggingface/kernels/blob/b7062eed8821f6a1235680844ad4bbc4711d7a7c/kernel-port/README.md#overlay copied for reference Note
|
Very good idea, IMO. But would we have a simple way to quickly test the correctness of its implementation? Not a blocker but I think we should strive for simplicity here.
Maybe we need to distinguish between required pins and optional pins?
Cool, that works for me!
Not sure if I fully understood it. Why would it differ for an external repo from
Oh okay. I was under the impression that without the |
| # Local packages/hooks. | ||
| kernel-builder = final.callPackage ./pkgs/kernel-builder { inherit builderProvenance; }; | ||
|
|
||
| kernel-port = final.callPackage ./pkgs/kernel-port { }; |
There was a problem hiding this comment.
Any reasoning behind making it a part of our nix-builder? For future CI?
sayakpaul
left a comment
There was a problem hiding this comment.
Thanks just left a bunch of comments. I think we are headed in a good direction. Once this takes a bit more shape, we could think about how we wire this in the CI, etc.
sayakpaul
left a comment
There was a problem hiding this comment.
Left some comments. Thanks for the further updates!
Apart from the comments, I think we should log the dirty status in the port provenance.
| @@ -0,0 +1,992 @@ | |||
| // Python edits go through libcst, so comments, quoting and layout outside the | |||
There was a problem hiding this comment.
What is the formatting situation here? Do we follow it from upstream? In some of the port PRs on kernels-community, we saw a significant amount of formatting-related changes.
There was a problem hiding this comment.
the python rewrites go through libcst so anything the tool touches keeps upstream formatting and comments byte for byte. the formatting diffs in the port prs come from overlay files and replace ops, where the content is whatever the recipe says, not from the rewriter
There was a problem hiding this comment.
Fine for now but essentially, formatting related changes should be very minimal IMO at least for existing kernels. Otherwise, it's just a lot of reviewing time that could have been spent elsewhere.
2bb25b6 to
c29a675
Compare
c29a675 to
c44f051
Compare
sayakpaul
left a comment
There was a problem hiding this comment.
Thanks a lot for the iterations. Just one comment regarding the tests.
| @@ -0,0 +1,740 @@ | |||
| use crate::{ops, python, recipe, workspace::Workspace}; | |||
There was a problem hiding this comment.
I didn't go through this fully, but on a high level, I would expect to see the tests covering both AOT and JIT-flavored kernels. If already done, no need for further modifications. If not, then let's do that.
I think tests for atomic ops that we will have in the tool are good but lightweight e2e tests also go a long way (the way we use the RELU example kernel in tests, for example).
There was a problem hiding this comment.
really good point e2e tests for the two cases is really useful! thanks
just updated to include an example for both AOT and JIT kernels. each example has a upstream, expected dir and a port.kdl, then in CI we run the porting tool on upstream and check that its identical to the valid kernel format in expected
Automated hardening of the workflow files flagged on #776. > [!WARNING] > **This narrows what the workflow can reach.** Job permissions were declared in `.github/workflows/rust.yaml`. Each job now gets only the scopes its steps were read to need — if one of them does something this could not see, it will fail on the next run. The table below says which step drove each scope. Targets `kernel-port-tool`. Files changed, and what changed them: - `.github/workflows/rust.yaml` — job permissions Fixed by this PR: - **MEDIUM** `excessive-permissions` (zizmor) — .github/workflows/rust.yaml:1 - **MEDIUM** `excessive-permissions` (zizmor) — .github/workflows/rust.yaml:15 - **MEDIUM** `excessive-permissions` (zizmor) — .github/workflows/rust.yaml:35 - **MEDIUM** `excessive-permissions` (zizmor) — .github/workflows/rust.yaml:64 **This does not fix everything.** 3 further finding(s) (3 critical) need a decision this bot should not make for you. They are in the security channel with their locations — deliberately not repeated here, since this repository may be public and they are not fixed yet. ### Permissions `.github/workflows/rust.yaml` | job | granted | why | |---|---|---| | `fmt` | `contents: read` | Only actions/checkout plus cargo fmt checks, so read access to the repository contents is all that is needed. | | `clippy` | `contents: read` | actions/checkout drives contents: read; actions/cache and cargo clippy use no token permissions. | | `test` | `contents: read` | actions/checkout drives contents: read; actions/cache and cargo test require no additional scopes. | Anything not listed above keeps the permissions it had. To measure a job this could not read, add [`GitHubSecurityLab/actions-permissions/monitor`](https://github.com/GitHubSecurityLab/actions-permissions) to it and run the workflow — it reports the minimum the run actually used. Pinning changes come from `pinact` and are mechanical. Any other change was generated by Claude — read it before merging. <!--slack ts:1790173757.620379 channel:C0AJSP0D53L--> Co-authored-by: hf-security-analysis[bot] <265538906+hf-security-analysis[bot]@users.noreply.github.com>
sayakpaul
left a comment
There was a problem hiding this comment.
One optional comment which could be addressed in a follow-up.
| run: ( cd kernel-port && cargo clippy -- -D warnings ) | ||
|
|
||
| test: | ||
| permissions: |
There was a problem hiding this comment.
Running the tests on kernel-porter for every rust-related changes doesn't sound good to me. Maybe we should have a separate workflow for the porter and trigger it on changes specific to the porter?
There was a problem hiding this comment.
good point, I'll follow up with improved ci in the next porter changes. also lets see how long the tests take to run, if its very small it might be more simple to keep in the rust workflow (will check when merged)
a0875ff to
247bdfb
Compare
Warning
This is an experiment/draft. The recipe language, the op set, and the CLI are all subject to change without notice.
this pr adds
kernel-port, an experimental tool for porting kernel repos into the kernel-builder layout by running a recipe instead of doing it by hand.the idea is that porting an existing kernel to the kernel-builder is a set of deterministic rewrites/restructuring. a recipe is a list of those operations, which has the benefit of being able to be checked into the repo and provide a way to deterministically reproduce given a specific upstream commit.
one of the difficulties of maintaining a port is that the upstream repo can drift and currently keeping the port in sync is a manual process.
recipes are a way to codify the porting process, so that if the upstream repo drifts we can simply bump the commit in the recipe and re-run the porting process. if the upstream repo has changed in a way that breaks the porting process, the recipe will fail to apply and we can fix it before continuing.
a port is a
port.kdlrecipe plus an overlay dir of checked in files. same pins + same recipe gives a byte identical tree every time, and every op hard fails on drift rather than silently porting the wrong thing.recipes are kdl 2.0 documents, one node per op:
there are 15 ops (
source,vendor,prune,delete,move,overlay,replace,strip_suffix,expect,convert_import,remap_module,relativize_imports,ensure_init,kernel,manifest). python rewrites go through libcst so comments and formatting are preserved byte for byte.build.tomlis always generated by themanifestop, never overlaid.the pins are the whole point.
count=,files=andchanges=are literals that have to match exactly, so a new file upstream cannot be rewritten without someone looking at it:you can try an op without a checkout at all,
-etakes the recipe inline and--file path=contentbuilds the input tree in memorythe readme has a cookbook with one runnable command per op, plus the full arg list and failure modes for each. every example in it was run and pasted, not written by hand.
***NEXT STEPS are to target a JIT and AOT kernel in the kernels-community and experiment using this tool