Skip to content

fix: resolve bash sources for extensionless libs and bare names - #2192

Closed
Souptik96 wants to merge 1 commit into
Graphify-Labs:v8from
Souptik96:fix/2171-bash-source-extensionless-and-bare
Closed

fix: resolve bash sources for extensionless libs and bare names#2192
Souptik96 wants to merge 1 commit into
Graphify-Labs:v8from
Souptik96:fix/2171-bash-source-extensionless-and-bare

Conversation

@Souptik96

Copy link
Copy Markdown
Contributor

Fixes #2171

Both gaps confirmed against v8 (0.9.26) and fixed along the directions in the issue.

1. Extensionless shebang scripts

_SHEBANG_DISPATCH routes a #!/usr/bin/env bash file with no extension to
extract_bash, so its functions are indexed — but the cross-file source pass picked
participants by filename suffix:

sh_paths = [p for p in paths if p.suffix in (".sh", ".bash")]

so a sourced extensionless lib was excluded and calls into it never bound.

Now selected by shape as well: the bash extractor tags every node it emits with
metadata.language == "bash". I kept the suffix check alongside it so an empty .sh file
— which has no nodes to inspect — still participates.

2. Bare source lib.sh

Only the raw.startswith((".", "/")) branch recorded a bash_sources entry; a bare name
fell through to the opaque imports fallback, so neither the source edge nor calls into the
lib resolved. The else branch now binds a sibling of that name when one exists.

The existence gate from the ./-prefixed branch carries over, so this stays a miss-fix and
not a fabrication: a bare name resolving to no sibling keeps the old imports edge and
records no bash_sources entry. is_file() is wrapped against OSError so a name that's
invalid for the platform degrades instead of raising.

Transitive sources (a→b→c) remain unresolved, as the issue specifies.

Testing

Three new tests in tests/test_extract.py — the two the issue asks for, plus a guard that
the bare-name branch still fabricates nothing:

$ uv run pytest tests/test_extract.py -q -k "extensionless_sourced_lib or bare_source_name_resolves or bare_source_missing_file"
3 passed, 149 deselected, 1 warning in 2.82s

Confirmed they catch the bugs — same tests with graphify/extract.py and
graphify/extractors/bash.py reverted to v8:

FAILED tests/test_extract.py::test_extract_bash_call_into_extensionless_sourced_lib_resolves - AssertionError: []
FAILED tests/test_extract.py::test_extract_bash_bare_source_name_resolves_to_sibling - AssertionError: []
2 failed, 150 deselected

Affected suites and both pre-commit gates:

$ uv run pytest tests/test_extract.py tests/test_symbol_resolution.py tests/test_languages.py -q
4 failed, 521 passed, 1 warning in 24.15s

$ uv run ruff check --config pyproject.toml graphify/extractors/bash.py graphify/extract.py tests/test_extract.py
All checks passed!

$ uv run python -m tools.skillgen --check
check OK: 134 artifact(s) match committed output and expected/.

Those 4 failures are pre-existing on Windows, not from this PR — they're exactly the 4
test_extract.py failures in my baseline on unmodified v8 (66d8110), all symlink-related
(test_collect_files_*, e.g. OSError: [WinError 1314] A required privilege is not held by the client). Full-suite baseline there is 45 failed / 3578 passed / 15 skipped. No
Linux/macOS box here to confirm they're green, so please read that in context.

…hify-Labs#2171)

Two coverage gaps left by Graphify-Labs#2141 / Graphify-Labs#2157, both misses rather than fabrications.

1. Extensionless shebang scripts. _SHEBANG_DISPATCH already routes a
   `#!/usr/bin/env bash` file with no extension to extract_bash, so its functions
   are indexed, but the cross-file source-resolution pass picked participants by
   filename suffix (`p.suffix in (".sh", ".bash")`). A sourced extensionless lib
   was therefore excluded and calls into it never bound. Select by shape as well:
   the bash extractor tags every node it emits with metadata.language == "bash".
   The suffix check stays so an empty .sh file, which has no nodes to inspect,
   still participates.

2. Bare `source lib.sh` (no ./ prefix). Only the `raw.startswith((".", "/"))`
   branch recorded a bash_sources entry; a bare name fell through to the opaque
   `imports` fallback, so neither the source edge nor calls into the lib resolved
   even though the file usually sits beside the script. The else branch now binds
   a sibling of that name when one exists.

The existence gate from the ./-prefixed branch carries over: a bare name that
resolves to no sibling keeps the old `imports` edge and records no bash_sources
entry, so nothing is invented. is_file() is wrapped against OSError so a name
that is invalid for the platform degrades instead of raising.

Transitive sources (a->b->c) remain unresolved, as the issue notes.
@safishamsi

Copy link
Copy Markdown
Collaborator

Thanks @Souptik96. Shipped in v0.9.27 (cherry-picked to v8), with a follow-up marking the bare-name binding INFERRED (it resolves via $PATH at runtime). Closed-unmerged here, but it's in the release: https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.27

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.

Bash source resolution: extensionless shebang scripts and bare source lib.sh are not resolved

2 participants