Skip to content

Sweep the whole project into images, not only .py - #65

Open
nickhuo wants to merge 8 commits into
mainfrom
jiajunh/can-282-take-the-second-half-of-can-228-sweep-the-whole-project-not
Open

Sweep the whole project into images, not only .py#65
nickhuo wants to merge 8 commits into
mainfrom
jiajunh/can-282-take-the-second-half-of-can-228-sweep-the-whole-project-not

Conversation

@nickhuo

@nickhuo nickhuo commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Closes CAN-282. Second half of CAN-228; follows CAN-230 (#51).

The bug

The build's fallback sweep collected only .py. Anything else the source opens at runtime — PDFs, notes.txt, pyproject.toml, langgraph.json — never reached the image, and the port failed only once the agent was serving. 13 findings across 10 corpus repos trace to this.

The fix

_sweep_py_files becomes _sweep_project_files and takes every file at its relative path.

The rename matters beyond readability: the porting skill's validate.py probes hasattr(stub_generator, "_sweep_project_files") for its sweeps_all_files capability, so that probe reported no unconditionally. It now reports the truth.

The build's fallback sweep collected only `.py`, so anything else the
source opens at runtime -- PDFs, notes.txt, pyproject.toml,
langgraph.json -- never reached the image, and the port failed only once
the agent was serving. This accounts for 13 findings across 10 corpus
repos.

`_sweep_py_files` becomes `_sweep_project_files` and takes every file at
its relative path. The rename also fixes the porting skill's capability
probe, which checked `hasattr(stub_generator, "_sweep_project_files")`
and so reported `sweeps_all_files: no` unconditionally.

Broadening the sweep needs guardrails, since "copy everything" would
otherwise bake secrets and host junk into images:

- skipped: hidden files and directories (where .env and .git live), the
  generated stubs/, grpc_stubs/, docker_container/ at the root,
  __pycache__, node_modules, venv, site-packages, *.egg-info, host
  bytecode, and symlinks;
- skipped with a warning: private key material (.pem, .key, .p12,
  .pfx), and the root Dockerfile, requirements.txt, and
  workflow_launcher.py. The build context writes its requirements.txt
  before the copy runs, so a project's own file at that name would have
  overwritten the generated one and stripped the base runtime
  dependencies out of the image.
@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 76a56e56-5084-4385-82af-cf6690b7b626


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nickhuo nickhuo closed this Sep 2, 2026
@nickhuo nickhuo reopened this Sep 2, 2026
@nickhuo
nickhuo marked this pull request as draft September 2, 2026 01:09
Review of the previous commit found three ways it was wrong, plus one
hole the end-to-end run exposed.

Silent drops. Hidden paths were dropped without a word, which is the
same bug CAN-282 exists to fix -- a project keeping runtime assets in
.streamlit/ or .prompts/ still failed only once the agent was serving,
now with no trace of why. Every exclusion is reported: hidden paths as
one aggregated note, private keys and reserved names individually.

Name-based key matching was theater. It caught .pem and waved through
id_rsa, which has no extension at all. PEM material is now found by its
armor whatever the file is called, and a certificate -- public, and
sometimes needed -- ships instead of being lumped in with private keys.
The docstring says plainly that this is not a secret scanner:
credentials.json still ships, because nothing can recognize it.

No size signal. Broadening the sweep means a dataset or a virtualenv
under a name _SKIPPED_DIRS misses now lands in every image, where before
only .py did. Past 100 MB the sweep says how big it got and what the
largest file was, which is also the backstop for whatever the hardcoded
lists fail to catch.

The hole: the build context is assembled inside the project root, so the
sweep copied it into itself. `docker_container/` happens to be in
_GENERATED_DIRS, so the CLI path was covered by coincidence rather than
by construction; any other output directory nested a copy of the context
inside itself. The context is now excluded by resolved path.

Those lists are hardcoded with no way for a project to override them,
which is a consequence of assembling the context by hand instead of
letting Docker's own ignore mechanism run. The comment says so.
Restating a constant's contents in prose above it is noise. What stays is
the part the code cannot state: why the lists are hardcoded with no
override, why requirements.txt is reserved, why keys are matched by armor
and not by name, and why exclude_dir is compared by resolved path.
largest = max(largest, (size, rel_dst)) compares tuples, so two files of
equal size fall through to comparing their paths -- and the initial
(0, None) meets the first zero-byte file the sweep finds. An empty
__init__.py is in nearly every Python project, so this took the build
down with a TypeError. Every test passed because the test helper writes
one byte by default. Compare the size and nothing else.

The hidden-path note fired on .git, .gitignore, .venv and every tool
cache, which is to say on every real build. A note that always fires is
wallpaper, and it takes the one that matters -- a project keeping prompts
in .prompts/ -- down with it. Ordinary repo furniture is still held back,
just no longer announced. A wrong guess in that list costs a line of
output rather than a missing file, which is why it sits apart from the
lists that decide what ships.
The old sweep pruned only hidden directories, so a .py file under venv/,
node_modules/, site-packages/ or an .egg-info/ did reach the image.
_SKIPPED_DIRS drops those directories entirely, which is right, but doing
it silently is a behavior change nobody can see -- the same silent-drop
bug this ticket exists to fix.

They are now reported. __pycache__ stays silent because it never held
anything shippable, which leaves the invariant clean: the only quiet
drops are bytecode, symlinks, __pycache__, and the build's own output,
and not one of those could ever have shipped.
Symlinked files were already skipped. Symlinked directories were skipped
too, but only because os.walk defaults to followlinks=False -- a security
property resting on a default someone can flip, and one that cannot
report what it skipped. Both cases are now pruned by the same rule in the
same function: a link to /etc or to a home directory would copy files
from outside the project into the image.

They are reported rather than dropped quietly, because the target may be
a shared config a monorepo expects in the image. The answer there is to
copy the target in, not to follow the link.

No new constant and no new knob: one predicate, two loops, one note.
@nickhuo
nickhuo requested a review from Saaketh0 September 2, 2026 03:11
The directory loop built os.path.join(root, name) three times and the
first one ran to 89 characters, the one formatting deviation this branch
added to an already-unformatted file. One local name fixes both.
@nickhuo
nickhuo requested a review from iidsample September 3, 2026 00:41
@nickhuo nickhuo changed the title [CAN-282] Sweep the whole project into images, not only .py Sweep the whole project into images, not only .py Sep 3, 2026
Comment thread ventis/stub_generator.py Outdated
return b"-----BEGIN" in head and b"PRIVATE KEY-----" in head


def _sample(paths, limit=5):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this is a nitpick, but could the _worth_reporting and _sample functions be named better? the name was not intuitive at all and the docstring below didn't help too much in understanding the purpose of the two functions

@Saaketh0 Saaketh0 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

lgtm, made a nitpicky comment but everything else good

@nickhuo
nickhuo marked this pull request as ready for review September 3, 2026 03:09

nickhuo commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator Author

@saurabh.a this pr is good to be reviewed

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.

2 participants