Skip to content

feat(ws-changed): workspace dependency graph + affected-package selection - #106

Merged
pyramation merged 1 commit into
mainfrom
feat/ws-changed
Aug 5, 2026
Merged

feat(ws-changed): workspace dependency graph + affected-package selection#106
pyramation merged 1 commit into
mainfrom
feat/ws-changed

Conversation

@pyramation

Copy link
Copy Markdown
Contributor

Summary

New first-party package packages/ws-changed that answers "which workspace packages does this changeset affect?" — the input for change-aware CI (run only the tests a feature branch can reach) without depending on pnpm --filter / lerna / nx.

Two concerns are deliberately separated:

  1. What is a "package" and what depends on what? — pluggable WorkspaceProviders, because the same repo has different dependency graphs depending on the question:

    • pnpm — JS workspace packages from pnpm-workspace.yaml (or package.json workspaces) + each manifest's dep maps. A dep is an internal edge whenever its name is a workspace package — every workspace: protocol variant (workspace:*, workspace:^, workspace:~, workspace:^1.2.3, workspace:1.2.3) and a bare semver range when a workspace package publishes under that name (how this dist-publishing monorepo references itself). Edge kinds (prod/dev/peer/optional) are selectable.
    • pgpm — Postgres modules from each dir's <name>.control requires + pgpm.plan %project; out-of-workspace requires (plpgsql, extensions) recorded as external.
    • glob — plain directories, no edges.
    • registerProvider() for custom graphs — the domain-agnostic extension point.
      Multiple providers compose: provider: ['pnpm','pgpm'] unions the node sets by name and merges edges, so you get JS and SQL edges on the same nodes.
  2. What's affected?affected = changed ∪ transitive dependents:

const { workspace, config } = loadWorkspace();          // provider(s) from config
const r = affected(workspace, {
  changed: changedPaths({ base: 'origin/main' }),        // from git-changed
  global: config.global                                  // lockfile / CI trigger patterns
});
r.packages;    // changed ∪ dependents (sorted)
r.changed;     // packages that directly own a changed file
r.rootChanged; // changed paths owned by no package
r.global;      // a global-trigger path changed → treat everything as affected
r.why;         // [{ package, kind: 'changed'|'dependent', via }]

Configuration is via confstash (ws-changed.config.{ts,js,json}, .ws-changedrc*, or a ws-changed key in package.json), overridable per-call and per CLI flag (--provider pnpm,pgpm, --global, --include/--exclude, --config, WS_CHANGED_GLOBAL). Changed files come from git-changed. Zero third-party runtime deps (only the two workspace packages).

CLI: ws-changed --base origin/main [--json] [--why] [--dirs] [--changed] [--list] [--graph]. WorkspaceGraph also exposes direct/transitive dependencies & dependents, deterministic topological sort, and cycle detection (throws a readable cycle path).

This is M1 of the CI/CD affected-test plan (constructive-planning#1435); the pgpm edge provider wiring into constructive-db's shard planner is later milestones.

Testing

  • packages/ws-changed: 60 Jest tests across 10 suites (glob expansion & matching, workspace-file parsing, both providers incl. every workspace: variant, graph traversal/topo/cycles, affected selection incl. root/global/--why, provider composition, confstash loading + overrides, CLI parse + run). All green.
  • pnpm lint (eslint) and makage build (CJS+ESM) clean.
  • Added ws-changed to the CI test matrix in .github/workflows/ci.yml.
  • Lockfile: single packages/ws-changed importer entry (deps already in the workspace); pnpm install --frozen-lockfile verified with pnpm 10.12.2 (CI's version).
  • Dogfooded against constructive-db: --provider pgpm resolves 87 modules with correct real requires edges.

Link to Devin session: https://app.devin.ai/sessions/b4c06a1e03c840e680ba2e99a1385532
Requested by: @pyramation

@pyramation pyramation self-assigned this Aug 4, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@pyramation
pyramation merged commit 0120b4b into main Aug 5, 2026
57 checks passed
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.

1 participant