Skip to content

refactor(framework): Extract project file utility for flwr build #6822

Open
chongshenng wants to merge 37 commits intomainfrom
feat/refactor-collect-project-file
Open

refactor(framework): Extract project file utility for flwr build #6822
chongshenng wants to merge 37 commits intomainfrom
feat/refactor-collect-project-file

Conversation

@chongshenng
Copy link
Member

This PR extracts the common functions from flwr app publish to reuse in flwr build.

Merge after

chongshenng and others added 30 commits March 20, 2026 13:58
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@chongshenng chongshenng changed the title Feat/refactor collect project file refactor(framework): Extract project file utility for flwr build Mar 23, 2026
@chongshenng chongshenng changed the base branch from main to feat/include-based-fab-build March 23, 2026 22:56
@github-actions github-actions bot added the Maintainer Used to determine what PRs (mainly) come from Flower maintainers. label Mar 23, 2026
@chongshenng chongshenng changed the base branch from feat/include-based-fab-build to main March 24, 2026 08:13
@chongshenng chongshenng marked this pull request as ready for review March 24, 2026 08:33
Copilot AI review requested due to automatic review settings March 24, 2026 08:33
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors Flower CLI file-collection logic by extracting reusable project file utilities from flwr app publish into flwr.cli.utils, with the intent to reuse the same logic in flwr build.

Changes:

  • Added depth_of and a new collect_project_files utility to framework/py/flwr/cli/utils.py.
  • Updated flwr app publish to use collect_project_files instead of its local helpers.
  • Moved depth_of unit test coverage from publish_test.py to utils_test.py.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
framework/py/flwr/cli/utils.py Introduces shared helpers depth_of and collect_project_files (include/exclude filtering, max depth enforcement, deterministic sorting).
framework/py/flwr/cli/utils_test.py Adds depth_of test coverage in the CLI utils test module.
framework/py/flwr/cli/app_cmd/publish.py Replaces local file-walking/depth logic with the new shared collect_project_files helper.
framework/py/flwr/cli/app_cmd/publish_test.py Removes the old _depth_of tests now that the helper is centralized.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +589 to +596
# Build include/exclude pathspecs
# Note: This should be a temporary solution until we have a complete mechanism
# for configurable inclusion and exclusion rules.
# Note: Unlike Git, we do not support nested .gitignore files in subdirectories.
gitignore_patterns = tuple(load_gitignore_patterns(root / ".gitignore"))
exclude_spec = build_pathspec(gitignore_patterns + exclude_patterns)
include_spec = build_pathspec(include_patterns)

Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

collect_project_files always merges patterns from root/.gitignore into exclude_patterns. The PR description says this utility is extracted to reuse in flwr build, but (per linked #6805) flwr build is moving away from .gitignore-based filtering. Consider parameterizing gitignore handling (e.g., gitignore_patterns: Sequence[str] | None or use_gitignore: bool = True) so callers like build can explicitly disable it while publish keeps current behavior.

Copilot uses AI. Check for mistakes.
Comment on lines +599 to +606
for path in root.rglob("*"):
if not path.is_file():
continue

# Skip excluded or not included files
# Note: pathspec requires POSIX style relative paths
relative_path = path.relative_to(root)
posix = relative_path.as_posix()
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

path.is_file() returns true for symlinks to files, so collect_project_files can include symlink targets outside root (potentially uploading/packaging unintended files). Consider skipping symlinks (e.g., if path.is_symlink(): continue) or enforcing that path.resolve() stays within root.resolve() before including/reading files.

Copilot uses AI. Check for mistakes.
Comment on lines +547 to +567
def collect_project_files(
root: Path,
include_patterns: tuple[str, ...],
exclude_patterns: tuple[str, ...],
max_depth: int | None = None,
on_skip: Callable[[Path], None] | None = None,
) -> list[Path]:
"""Walk a project directory and return filtered file paths.

Files are included or excluded based on gitignore-style patterns.
Patterns from the project's ``.gitignore`` are merged with the
caller-supplied ``exclude_patterns`` before matching.

Parameters
----------
root : Path
Absolute path to the project root directory.
include_patterns : tuple[str, ...]
Gitignore-style patterns for files to include. A file must
match at least one include pattern to be accepted.
exclude_patterns : tuple[str, ...]
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

collect_project_files is newly introduced as a shared utility, but there are no direct unit tests covering its include/exclude behavior, deterministic sorting, or on_skip callback semantics (current coverage is only indirect via publish tests). Adding focused tests in cli/utils_test.py would help lock down behavior as it gets reused by multiple commands.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Maintainer Used to determine what PRs (mainly) come from Flower maintainers.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants