feat(tia): support projects in git repository subdirectories (monorepos) - #1809
Open
einar-hansen wants to merge 2 commits into
Open
feat(tia): support projects in git repository subdirectories (monorepos)#1809einar-hansen wants to merge 2 commits into
einar-hansen wants to merge 2 commits into
Conversation
Tia previously refused to run when the project root was not the git repository root, telling monorepo users to "give the project its own git repository". The underlying problem was a path-space mismatch: git's plumbing (status --porcelain, diff --name-only, show SHA:path) speaks repository-root-relative paths, while the dependency graph, watch patterns and coverage edges all speak project-root-relative paths. ChangedFiles now detects the project's location inside the repository once (git rev-parse --show-prefix) and translates at every git boundary: - status/diff output is mapped to project-relative paths, and paths outside the project subtree are dropped — they are as invisible to the project's graph as files outside the repository are for a root-level project, and leaking them through would hit the unknown-file catch-all and force a full rerun on every sibling-project change; - the diff listing runs with -z (git C-quotes non-ASCII paths, which would silently fail the prefix match and drop a real change — a stale replay) and --no-renames (rename detection reports only the NEW path, so a project file renamed to a path outside the subtree would vanish from the listing; as delete+add both sides surface); - git show SHA:path (behavioural-change filtering, composer.lock drift summaries) resolves project-relative paths through the prefix. A new GitRepository helper locates the governing repository by walking ancestors, fixing three more root==project assumptions: Fingerprint's tracked-lockfile check, BaselineSync's GitHub-repo detection (baseline fetching silently never activated for subdirectory projects), and Storage's origin-based project key — which now also carries the subdirectory prefix, so `apps/api` and `services/api` in one repository never share a graph store. Root-level projects keep their exact existing behaviour: the prefix is empty and every translation is the identity. The TiaRequiresRepositoryRoot exception is removed along with the guard.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What:
Description:
Love the Tia engine — but it refuses to run when the Pest project lives in a subdirectory of its git repository (
apps/apiin a monorepo):The root cause is a path-frame mismatch: git addresses paths relative to the repo root, while Tia's dependency graph is keyed on project-relative paths. This PR teaches every git boundary to translate instead of blocking the scenario. (Written with Claude Code; reviewed by me and verified on a real monorepo, see below.)
ChangedFilesresolves the prefix once (git rev-parse --show-prefix), strips it from everything git reports, prepends it to everything Tia asks git for (git show <sha>:apps/api/composer.lock), and ignores changes outside the project — commits that only touch a sibling app replay 100%.-zand--no-renames. Without-z, git C-quotes non-ASCII filenames, the quoted path misses the prefix, and the change is silently dropped — stale results replayed as passing, the one failure Tia can never afford. Without--no-renames, a file renamed out of the subtree reports only its new (ignored) path and vanishes from Tia's view.GitRepositoryhelper fixes three more root==repo assumptions:BaselineSync(read.git/configfrom the project folder, so baseline fetching silently never activated in a subfolder),Storage(cache key now includes the subfolder, so two apps in one repo get distinct graph stores), andFingerprint(gitignore detection).Root-level projects are untouched: the prefix is empty, every translation is a no-op, and storage keys stay byte-identical — pinned by a regression test, so no existing caches are invalidated.
Verification
composer testpasses in full. The count-based visual snapshots are left untouched, matching repo convention (they're regenerated on5.xpost-merge).apps/apiof a monorepo — record ~6 min, full-suite replay ~10.5 s, sibling-only commits replay with zero reruns. (Runs include the fix from fix: skip user hooks when replaying cached skipped, incomplete, and failed tests #1806; we hit TIA cached-replay run fires before/afterEach hooks for skipped tests outside the Laravel test bootstrap → "A facade root has not been set." #1785 independently.)Known limitation: the baseline artifact name is fixed (
pest-tia-baseline), so two baselined projects in one repository would collide. Happy to follow up if that's a setup you'd want supported.Related:
ChangedFiles. This PR additionally covers non-ASCII filenames (-z), renames leaving the subtree (--no-renames),BaselineSync(explicitly deferred there), per-projectStoragekeys, andFingerprint.