Skip to content

FEDX-7266: Support glob patterns in dependency_validator workspace resolution - #195

Open
engops-wk wants to merge 6 commits into
masterfrom
cursor/support-workspace-glob-patterns-bc5d
Open

FEDX-7266: Support glob patterns in dependency_validator workspace resolution#195
engops-wk wants to merge 6 commits into
masterfrom
cursor/support-workspace-glob-patterns-bc5d

Conversation

@engops-wk

@engops-wk engops-wk commented Sep 8, 2026

Copy link
Copy Markdown

Opened by Dustin Pauze with the ai-sdlc workflow for FEDX-7266

Motivation

Flutter/Dart pub workspaces support glob patterns for defining workspace members (e.g. packages/*), but dependency_validator only handled literal paths in workspace: entries. This meant any workspace configuration using glob patterns would fail to have its sub-packages resolved and validated correctly. (ref: #176)

Changes

  • Added resolveWorkspaceMembers and hasGlobWildcards helpers in lib/src/utils.dart to expand glob patterns in workspace: entries into concrete sub-package directories, filtering to directories that contain a pubspec.yaml (matching pub's own workspace resolution behavior).
  • Updated checkPackage in lib/src/dependency_validator.dart to resolve workspace members via the new helper before recursing into sub-packages and building subpackage globs, and to fail gracefully (return false) on invalid glob syntax.
  • Updated README.md and CHANGELOG.md to document glob pattern support in workspace configuration.
  • Extended the checkWorkspace test helper (test/utils.dart) to support multiple sub-packages with distinct paths/contents/deps/config, and added unit tests (test/utils_test.dart) and integration tests (test/workspace_test.dart) covering literal paths, glob expansion, filtering non-package directories, deduplication, sort order, invalid glob syntax, and mixed literal/glob workspace entries.

Testing/QA Instructions

  • Run dart test and confirm all tests pass, including the new resolveWorkspaceMembers/hasGlobWildcards unit tests and the glob workspace pattern integration tests.

  • Manually verify against a workspace using workspace: - packages/* that only directories with a pubspec.yaml are validated as sub-packages, that dependency issues in glob-matched sub-packages are still detected, and that invalid glob syntax produces a warning rather than a crash.

  • I have updated the CHANGELOG.md

  • I have added/updated tests for this change

  • I have verified this change manually

Intent

dependency_validator didn't support glob patterns (e.g. packages/*) in workspace: entries, even though Dart pub workspaces support them. This change adds glob expansion for workspace member resolution so dependency_validator matches pub's actual workspace resolution behavior, enabling proper validation of sub-packages defined via glob patterns.

How To QA

  1. Run the full test suite with dart test and confirm all tests pass, including new resolveWorkspaceMembers, hasGlobWildcards, and glob workspace pattern tests in test/utils_test.dart and test/workspace_test.dart
  2. Create a test workspace with workspace: - packages/* in pubspec.yaml and multiple sub-packages in packages/ directory (including one directory without pubspec.yaml). Run dependency_validator from workspace root and confirm it recurses into real sub-packages but skips non-package directories
  3. In the same workspace, introduce an unused or missing dependency in a glob-matched sub-package and verify dependency_validator reports the issue for that specific sub-package
  4. Test with a malformed glob pattern (e.g. packages/[a) and confirm the tool logs a warning about invalid syntax and does not crash
  5. Verify a workspace with mixed literal and glob entries (e.g. packages/foo and packages/*) resolves without duplicate validation of packages/foo

Co-authored-by: Dustin Pauze <dustin.pauze@workiva.com>
Comment thread lib/src/dependency_validator.dart Outdated
root,
pubspec.workspace ?? [],
))
makeGlob('$root/$subpackage**'),

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

There is no separator before **, so packages/foo also excludes sibling dirs sharing the prefix (e.g. packages/foo_helpers, which glob resolution intentionally skips when it has no pubspec.yaml) from the root package's scan — silently hiding missing/unused deps. Use makeGlob('${p.join(root, subpackage)}/**').

Comment thread test/workspace_test.dart Outdated
);

test(
'validates each glob-matched subpackage',

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Both glob tests create exactly one subpackage, so "validates each glob-matched subpackage" never proves the glob expands to multiple members — and the positive test above passes identically if the glob resolves to zero members. Please add coverage with 2+ glob-matched packages plus a non-package dir under the glob, and a mixed literal + glob workspace: list.

Comment thread lib/src/utils.dart Outdated
for (final entity in glob.listSync(root: root)) {
if (entity is! Directory) continue;
if (!File(p.join(entity.path, 'pubspec.yaml')).existsSync()) continue;
yield p.relative(entity.path, from: root);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Results are neither deduplicated nor ordered: workspace: ['packages/*', 'packages/foo'] validates packages/foo twice, and listSync order is filesystem-dependent, so recursion order and log output are nondeterministic. Please return a deduplicated, sorted List<String> (normalize to posix separators so Windows packages\foo doesn't diverge from a literal packages/foo).

Comment thread lib/src/dependency_validator.dart Outdated

final subpackageGlobs = [
for (final subpackage in pubspec.workspace ?? [])
for (final subpackage in resolveWorkspaceMembers(

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

resolveWorkspaceMembers is a lazy sync* generator called twice per checkPackage, so the filesystem is walked twice and the two call sites can disagree if a member's pubspec.yaml appears/disappears between them (recursion vs. exclusion globs). Resolve once into a local final members = resolveWorkspaceMembers(...) list near the pubspec parse and reuse it at both sites.

Comment thread lib/src/utils.dart Outdated
) sync* {
for (final workspacePath in workspacePaths) {
if (hasGlobWildcards(workspacePath)) {
final glob = makeGlob(workspacePath);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

makeGlob throws a FormatException on malformed patterns (e.g. packages/[a), so a typo'd workspace: entry now crashes the tool with a stack trace instead of a readable error. Wrap this in a try/catch that logger.shouts invalid glob syntax: "$workspacePath" and fails the run, matching how checkPackage handles invalid exclude globs.

Comment thread test/utils.dart Outdated
);
final subpackagePubspec = Pubspec(
'subpackage',
p.basename(subpackagePath),

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

test/utils.dart is imported by pubspec_to_json.dart-based tests but package:path is not declared in this package's dev_dependencies (it's a regular dependency) — fine here, but note resolveWorkspaceMembers returning p.relative output means CHANGELOG/README still document only literal workspace: entries. Please add an "Unreleased" CHANGELOG entry and a README note under "Pub Workspaces" showing workspace: ['packages/*'].

@engops-wk engops-wk left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Review round 1 — CHANGES REQUESTED (posted as a comment; GitHub blocks REQUEST_CHANGES on one's own PR)

The direction is right: mirroring pub's glob expansion in a small, testable helper (resolveWorkspaceMembers) rather than sprinkling glob logic through checkPackage is the correct architectural choice, and threading workspaceMembers/subpackagePath through the checkWorkspace test harness keeps integration coverage cheap to extend. But this cannot merge yet.

CI is red — blocking regardless of code review

CI (.github/workflows/ci.yaml, Dart SDK 3.7.2) runs three required jobs from Workiva/gha-dart-oss@v0.1.14, and the PR is failing before review:

  • unit-tests (test-unit.yaml) — failing. dart test is not green, so the PR's claim of "Passing CI (dart test)" is not accurate today.
  • checks (checks.yaml) — failing. This job runs dart analyze / dart format --set-exit-if-changed / dependency_validator on this repo itself; formatting of the new blocks in lib/src/utils.dart and test/utils_test.dart is the most likely culprit.
  • build (build.yaml) must also be green (this repo generates pubspec_config.g.dart via build_runner).

Please paste the failing job logs (or a link) with the fix so round 2 can verify the actual failure rather than guess, and run dart analyze, dart format ., and dart test locally before pushing. Also reconcile the PR checklist — every box, including "Tests pass locally," is unchecked.

Correctness / robustness issues

  1. Missing-separator glob bug in lib/src/dependency_validator.dart (makeGlob('$root/$subpackage**')) over-excludes prefix-sibling directories from the root package's scan — a silent false negative in the tool's core output. Pre-existing, but far more likely to bite now that members share a parent like packages/.
  2. No dedup / nondeterministic ordering from resolveWorkspaceMembers; overlapping literal + glob entries double-validate a package, and listSync order varies by filesystem.
  3. Unhandled FormatException from makeGlob on a malformed workspace: entry — the tool crashes instead of reporting invalid glob syntax, which checkPackage already does for exclude globs.
  4. Lazy generator invoked twice per checkPackage, doubling filesystem walks and allowing the recursion set and the exclusion-glob set to disagree.

Test strategy gap

Both new integration tests use a single subpackage, so neither proves multi-member expansion, and the positive case would pass even if the glob resolved to zero members. Add a case with 2+ matched packages, a non-package directory under the glob, and a mixed literal + glob workspace: list.

Docs

User-visible feature with no CHANGELOG.md "Unreleased" entry and no README update under "Pub Workspaces" — future users won't discover glob support.

Happy to re-review as soon as CI is green and the above are addressed.

cursoragent and others added 2 commits September 8, 2026 19:23
- Fix subpackage exclusion glob boundary (use p.join with /** suffix)
- Resolve workspace members once; return sorted deduplicated List
- Handle invalid workspace glob syntax with logger.shout
- Expand integration tests for multi-package globs and mixed entries
- Document glob support in CHANGELOG and README

Co-authored-by: Dustin Pauze <dustin.pauze@workiva.com>
@dustinlessard-wf
dustinlessard-wf marked this pull request as ready for review September 9, 2026 13:32
@dustinlessard-wf
dustinlessard-wf requested a review from a team as a code owner September 9, 2026 13:32
@dustinlessard-wf

Copy link
Copy Markdown

@cursor resolve the merge conflicts

@cursor

cursor Bot commented Sep 10, 2026

Copy link
Copy Markdown

Merge conflicts are resolved and pushed to cursor/support-workspace-glob-patterns-bc5d.

What was conflicting

Master added nested-package exclusion (listNestedPackages, nestedPackageGlobs) and language-version parsing. Our branch added workspace glob resolution (resolveWorkspaceMembers, workspaceMembers).

How it was resolved

  1. CHANGELOG.md — Combined both unreleased sections: glob workspace support plus nested-package ignore and language-version parsing.

  2. lib/src/dependency_validator.dart — Dropped the duplicate subpackageGlobs block (master’s nestedPackageGlobs replaces it) and wired workspace globs into that list using resolved workspaceMembers:

    for (final subpackage in workspaceMembers)
      makeGlob('${p.join(root, subpackage)}/**'),
  3. test/utils_test.dart — Kept both test groups: resolveWorkspaceMembers / hasGlobWildcards and listNestedPackages, with all required imports.

Verification

  • dart analyze — clean
  • dart test test/utils_test.dart test/workspace_test.dart — all passed

Commit: FEDX-7266: resolve merge conflicts with master

Open in Web Open in Cursor 

cursoragent and others added 3 commits September 10, 2026 12:00
Co-authored-by: Dustin Pauze <dustin.pauze@workiva.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants