Skip to content

Detect Android and Gradle project types#325

Open
woocheol-lge wants to merge 1 commit into
mainfrom
g_test
Open

Detect Android and Gradle project types#325
woocheol-lge wants to merge 1 commit into
mainfrom
g_test

Conversation

@woocheol-lge

@woocheol-lge woocheol-lge commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • Bug Fixes
    • Improved detection of Android projects in Gradle build files, including Android Gradle Plugin declarations, version-catalog aliases, and Android-specific configuration blocks.
    • Improved package-manager classification when Gradle and Android manifests are found together.
    • Reduced the risk of Gradle build files being incorrectly identified as Android or standard Gradle projects.

@woocheol-lge
woocheol-lge requested a review from dd-jy July 10, 2026 02:13
@woocheol-lge woocheol-lge self-assigned this Jul 10, 2026
@woocheol-lge woocheol-lge added the chore [PR/Issue] Refactoring, maintenance the code label Jul 10, 2026
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds centralized Gradle-versus-Android classification for Gradle build files using plugin, version-catalog, and Android DSL detection, then applies it when resolving directories containing both Gradle and Android manifests.

Changes

Gradle and Android classification

Layer / File(s) Summary
Gradle build classifier
src/fosslight_dependency/run_dependency_scanner.py
Adds regex-based Android plugin, alias, and DSL detection with brace-matched android { ... } extraction and a new classify_gradle_build_file helper.
Manifest conflict resolution
src/fosslight_dependency/run_dependency_scanner.py
Uses the classifier when both Gradle and Android manifests are detected and accumulates the returned classification flags.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: dd-jy

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: distinguishing Android and Gradle project types in dependency scanning.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch g_test

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/fosslight_dependency/run_dependency_scanner.py (1)

92-96: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Drop the redundant is_gradle branch. classify_gradle_build_file() already falls back to (True, False) for every non-Android build file, so _GRADLE_BUILD_PATTERNS never changes the outcome. If default-to-Gradle is intended, remove the unused check; otherwise the fallback needs to change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/fosslight_dependency/run_dependency_scanner.py` around lines 92 - 96,
Remove the redundant `is_gradle` calculation and conditional from
`classify_gradle_build_file()`, returning `(True, False)` directly for
non-Android build files if default-to-Gradle behavior is intended; otherwise
update the fallback return to reflect the desired classification.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/fosslight_dependency/_package_manager.py`:
- Around line 565-578: Update run_gradle_task to call the module-level
parse_dependency_tree function directly instead of self.parse_dependency_tree,
passing the same dependency output and required arguments. Use the existing
parse_dependency_tree definition and its callers to preserve the expected
parsing behavior and avoid the AttributeError.
- Around line 517-525: Update _insert_plugins_block_after_buildscript to detect
both spaced and unspaced buildscript declarations, such as “buildscript {” and
“buildscript{”, before prepending the plugin block. Reuse or extend
_find_block_end with flexible matching, and only fall back to prepending when no
buildscript block is found.

In `@src/fosslight_dependency/run_dependency_scanner.py`:
- Around line 48-67: Update _ANDROID_BUILD_PATTERNS to match
classify_gradle_build_file’s normalized lowercase, whitespace-stripped input:
lowercase all camel-case markers and remove the redundant "android {" entry,
retaining only "android{".

---

Nitpick comments:
In `@src/fosslight_dependency/run_dependency_scanner.py`:
- Around line 92-96: Remove the redundant `is_gradle` calculation and
conditional from `classify_gradle_build_file()`, returning `(True, False)`
directly for non-Android build files if default-to-Gradle behavior is intended;
otherwise update the fallback return to reflect the desired classification.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c5927ce4-79e2-452a-be91-2e449e4b5ca0

📥 Commits

Reviewing files that changed from the base of the PR and between 690d8cb and 60c122d.

📒 Files selected for processing (2)
  • src/fosslight_dependency/_package_manager.py
  • src/fosslight_dependency/run_dependency_scanner.py

Comment thread src/fosslight_dependency/_package_manager.py Outdated
Comment on lines +565 to +578
def parse_dependency_tree(self, f_name):
config = android_config if self.package_manager_name == const.ANDROID else gradle_config

try:
for stack, name in create_dep_stack(self, f_name, config):
self.total_dep_list.append(name)
if len(stack) == 0:
self.direct_dep_list.append(name)
else:
if stack[-1] not in self.relation_tree:
self.relation_tree[stack[-1]] = []
self.relation_tree[stack[-1]].append(name)
except Exception as e:
logger.warning(f'Fail to parse gradle dependency tree:{e}')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm whether the module-level functions are bound to the class or overridden.
rg -nP '(parse_dependency_tree|create_dep_stack)' src/fosslight_dependency/_package_manager.py
echo '--- class-level rebinding? ---'
rg -nP 'PackageManager\.(parse_dependency_tree|create_dep_stack)\s*=' src/fosslight_dependency
echo '--- subclass method defs? ---'
rg -nP 'def\s+parse_dependency_tree\s*\(' src/fosslight_dependency
ast-grep outline src/fosslight_dependency/_package_manager.py --items all

Repository: fosslight/fosslight_dependency_scanner

Length of output: 2833


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- PackageManager call site and class body around run_gradle_task ---'
sed -n '130,190p' src/fosslight_dependency/_package_manager.py
echo '--- class tail around parse_dependency_tree ---'
sed -n '520,585p' src/fosslight_dependency/_package_manager.py

echo '--- subclasses and whether they call run_gradle_task / override parse_dependency_tree ---'
rg -n -C 2 'class .*PackageManager|def run_gradle_task|def parse_dependency_tree' src/fosslight_dependency/package_manager src/fosslight_dependency/_package_manager.py

echo '--- any explicit binding of module-level functions to PackageManager or subclasses ---'
rg -n 'PackageManager\.(parse_dependency_tree|create_dep_stack)\s*=|self\.parse_dependency_tree|self\.create_dep_stack' src/fosslight_dependency

Repository: fosslight/fosslight_dependency_scanner

Length of output: 14365


Call parse_dependency_tree directly from run_gradle_task. In src/fosslight_dependency/_package_manager.py:156, self.parse_dependency_tree(dep_output) will raise AttributeError because parse_dependency_tree lives at module scope, not on PackageManager, unless it’s rebound or overridden in the subclass.

🧰 Tools
🪛 Ruff (0.15.20)

[warning] 577-577: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/fosslight_dependency/_package_manager.py` around lines 565 - 578, Update
run_gradle_task to call the module-level parse_dependency_tree function directly
instead of self.parse_dependency_tree, passing the same dependency output and
required arguments. Use the existing parse_dependency_tree definition and its
callers to preserve the expected parsing behavior and avoid the AttributeError.

Comment thread src/fosslight_dependency/run_dependency_scanner.py Outdated
@woocheol-lge
woocheol-lge force-pushed the g_test branch 2 times, most recently from bdab4dc to f248b02 Compare July 10, 2026 08:59

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/fosslight_dependency/_package_manager.py`:
- Around line 450-494: The existing allDeps name check in add_allDeps_in_gradle
returns early and prevents dependency collection when a user-defined task
collides. Replace the injected task name and corresponding detection checks with
a scanner-specific name such as fosslightAllDeps, and update the task
registration and execution references, including run_gradle_task(),
consistently.
- Around line 291-293: Update the Gradle detection logic in the dependency
injection method so an existing `licenseReport {` block is considered configured
only when it contains the scanner-specific renderer and output path for
`dependency-license.json`; do not return success for generic/default settings.
When the block exists but lacks these settings, augment it in place rather than
adding a duplicate `dependency-license-report` plugin declaration, ensuring
`self.input_file_name` is generated.
- Around line 710-722: Update ensure_executable to determine executability with
os.access(filepath, os.X_OK) rather than checking whether any executable bit is
set in current_mode; retain the existing mode-preservation and chmod behavior
when the current user cannot execute the file.
- Around line 172-175: Update the Gradle command constructed in the
generateLicenseReport execution block of _run_gradle_process to include the
--no-parallel flag, ensuring multi-project license report generation runs
without parallel execution.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7ab26992-d28f-4d10-8d1b-7ffdf2af22d3

📥 Commits

Reviewing files that changed from the base of the PR and between 60c122d and bdab4dc.

📒 Files selected for processing (2)
  • src/fosslight_dependency/_package_manager.py
  • src/fosslight_dependency/run_dependency_scanner.py

Comment thread src/fosslight_dependency/_package_manager.py
Comment on lines 291 to 293
if 'dependency-license-report' in data or 'licenseReport {' in data:
logger.info('gradle-license-report plugin is already configured in build.gradle. Skip injection.')
return True

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Do not treat every licenseReport block as scanner-ready.

An existing block may use default renderers or paths, yet this returns success and skips injection of dependency-license.json. The subsequent task then cannot find self.input_file_name. Only skip when the scanner-specific renderer/output is already configured; otherwise augment the existing plugin configuration without adding a duplicate plugin declaration.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/fosslight_dependency/_package_manager.py` around lines 291 - 293, Update
the Gradle detection logic in the dependency injection method so an existing
`licenseReport {` block is considered configured only when it contains the
scanner-specific renderer and output path for `dependency-license.json`; do not
return success for generic/default settings. When the block exists but lacks
these settings, augment it in place rather than adding a duplicate
`dependency-license-report` plugin declaration, ensuring `self.input_file_name`
is generated.

Comment thread src/fosslight_dependency/_package_manager.py
Comment thread src/fosslight_dependency/_package_manager.py Outdated
@woocheol-lge
woocheol-lge force-pushed the g_test branch 4 times, most recently from 1e3b2a0 to f43b087 Compare July 13, 2026 02:06

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/fosslight_dependency/_package_manager.py`:
- Around line 813-824: Update get_gradle_cmd and its caller
collect_gradle_download_urls to accept input_dir, resolve gradlew/gradlew.bat
relative to that directory, and return the resolved wrapper path. Use that path
for ensure_executable and for restoring the original mode, ensuring wrapper
detection and cleanup target the same file executed with cwd=input_dir.
- Around line 580-630: The block scanner in _find_block_end must use an explicit
while-loop index so comment handling actually advances past skipped text. Update
both // and /* ... */ branches to move the index beyond the comment before
continuing, while preserving quote and brace-depth handling so
_insert_plugins_block_after_buildscript receives the true block boundary.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bea95aee-3423-46a3-ba11-4b4ec193f0eb

📥 Commits

Reviewing files that changed from the base of the PR and between 45624fc and f43b087.

📒 Files selected for processing (2)
  • src/fosslight_dependency/_package_manager.py
  • src/fosslight_dependency/run_dependency_scanner.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/fosslight_dependency/run_dependency_scanner.py

Comment on lines +580 to +630
for idx in range(open_brace_pos, len(content)):
char = content[idx]
next_char = content[idx + 1] if idx + 1 < len(content) else ''

if quote_char is not None:
if escaped:
escaped = False
elif char == '\\':
escaped = True
elif char == quote_char:
quote_char = None
continue

if char == '/' and next_char == '/':
while idx < len(content) and content[idx] != '\n':
idx += 1
continue

if char == '/' and next_char == '*':
idx += 2
while idx + 1 < len(content) and not (content[idx] == '*' and content[idx + 1] == '/'):
idx += 1
continue

if char in {"'", '"'}:
quote_char = char
continue

if char == '{':
brace_depth += 1
elif char == '}':
brace_depth -= 1
if brace_depth == 0:
return idx

return -1


def _insert_plugins_block_after_buildscript(content, plugin_block):
if not plugin_block:
return content

buildscript_keywords = ['buildscript {', 'buildscript{']
buildscript_end = -1
for keyword in buildscript_keywords:
buildscript_end = _find_block_end(content, keyword)
if buildscript_end != -1:
break

if buildscript_end != -1:
return content[:buildscript_end + 1] + f'\n{plugin_block}\n' + content[buildscript_end + 1:]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Actually advance past comments while locating the block boundary.

Changing idx inside a for ... in range(...) loop does not advance its iterator. Braces inside // or /* ... */ comments are therefore counted and can make the plugin block get inserted inside a comment or buildscript body. Use an explicit while index and move it past each comment.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/fosslight_dependency/_package_manager.py` around lines 580 - 630, The
block scanner in _find_block_end must use an explicit while-loop index so
comment handling actually advances past skipped text. Update both // and /* ...
*/ branches to move the index beyond the comment before continuing, while
preserving quote and brace-depth handling so
_insert_plugins_block_after_buildscript receives the true block boundary.

Comment on lines +813 to +824
if os.path.isfile('gradlew') or os.path.isfile('gradlew.bat'):
if platform.system() == const.WINDOWS:
cmd_gradle = "gradlew.bat"
else:
cmd_gradle = "./gradlew"
current_mode = change_file_mode(cmd_gradle)
return cmd_gradle, current_mode
current_mode, changed_mode = ensure_executable(cmd_gradle)
return cmd_gradle, current_mode, changed_mode


def collect_gradle_download_urls(input_dir, package_manager_name, app_name=None):
download_url_map = {}
cmd_gradle, current_mode = get_gradle_cmd()
cmd_gradle, current_mode, changed_mode = get_gradle_cmd()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Resolve and restore the wrapper relative to input_dir.

get_gradle_cmd() checks and modifies a wrapper in the process working directory, but the command executes with cwd=input_dir. When these differ, scanning can miss the wrapper or modify/restore the wrong file. Pass input_dir into the resolver and retain the resolved wrapper path for ensure_executable() and restoration.

Also applies to: 840-841

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/fosslight_dependency/_package_manager.py` around lines 813 - 824, Update
get_gradle_cmd and its caller collect_gradle_download_urls to accept input_dir,
resolve gradlew/gradlew.bat relative to that directory, and return the resolved
wrapper path. Use that path for ensure_executable and for restoring the original
mode, ensuring wrapper detection and cleanup target the same file executed with
cwd=input_dir.

@woocheol-lge
woocheol-lge force-pushed the g_test branch 3 times, most recently from 2f8b789 to 3e7aa24 Compare July 15, 2026 06:37
Signed-off-by: woocheol <jayden6659@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chore [PR/Issue] Refactoring, maintenance the code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant