Repository navigation
Conversation
peternhale
left a comment
There was a problem hiding this comment.
Approve in the context that this change solves the event loop perf issues while consuming STL in vscode services extension.
| ); | ||
| const ageMs = now - mtimeMs; | ||
| if (ageMs <= autoclearMs) return false; | ||
| yield* fs.remove(lockPath).pipe(Effect.catchReason('PlatformError', 'NotFound', () => Effect.void)); |
There was a problem hiding this comment.
This can delete a lock held by a healthy writer. If writer A holds index.lock longer than autoclear, writer B removes it and acquires a new lock. When A completes, releaseLock renames the path now owned by B, so A may publish B’s index alongside A’s updated ref.
The PR description notes this limitation, but this can corrupt the index/ref transaction. Can we fail with RepoLockedError after the wait timeout instead of deleting locks automatically, unless we have a reliable ownership/liveness check?
There was a problem hiding this comment.
how are you going to unlock it? how would someone get out of that bind to avoid being perma-stuck?
I chose the timeout based on "how long could an STL write take" after making it do some really, really big ones.
|
|
||
| const EMPTY_CHANGES: LocalChanges = { added: [], modified: [], deleted: [] }; | ||
|
|
||
| const FD_PERMITS_ENV = 'SF_SOURCE_TRACKING_FD_PERMITS'; |
There was a problem hiding this comment.
This introduces SF_SOURCE_TRACKING_FD_PERMITS, but no longer honors SF_SOURCE_TRACKING_BATCH_SIZE or SFDX_SOURCE_TRACKING_BATCH_SIZE. Those variables were the documented escape hatch for EMFILE in constrained developer and CI environments.
Could the new setting take precedence while the existing variables remain fallbacks? If the old settings are intentionally being retired, we should at least warn when either is set but ignored, so customers can migrate to SF_SOURCE_TRACKING_FD_PERMITS instead of silently losing their descriptor-limit mitigation.
There was a problem hiding this comment.
I centralized the config stuff; there was a bunch of nonsense kit would allow (infinity, decimals, etc) that didn't make sense here, but it does change things a tiny bit.
| dir: options.projectPath, | ||
| gitdir: getGitDir(options.orgId, options.projectPath), | ||
| roots: options.packageDirs.map((directory) => | ||
| ensurePosix(path.relative(options.projectPath, directory.fullPath)) |
There was a problem hiding this comment.
A project can validly use its root as a package directory (path: "."). In that case path.relative(options.projectPath, directory.fullPath) returns "", but RepoConfig.roots is decoded as RepoPath, which rejects empty strings. That makes the first local-tracking operation fail for root-package projects.
The directory walker already uses "" to represent the root, so this seems to be an integration-boundary mismatch. Could we model a root separately from tracked file paths, and ensure a root matches every project-relative file?
There was a problem hiding this comment.
The path: "." case appears valid, but I couldn’t find a public GitHub project that uses it. Going to see if it actually works with other stuff besides STL. If not, I'd recommend stopping that in sfdx-core rather than trying support it here.
There was a problem hiding this comment.
The path: "." case appears valid, but I couldn’t find a public GitHub project that uses it. Going to see if it actually works with other stuff besides STL. If not, I'd recommend stopping that in sfdx-core rather than trying support it here.
There was a problem hiding this comment.
ok, turns out that edge case doesn't currently work the CLI/STL. The other stuff (scratch org creation, SDR deploy) seems to so I think it's just an STL bug we could fix as part of this PR if you want to support that
Created the isolated project at rootPackageDot/ and scratch org root-package-dot-test (test-pziuoybhyxmh@example.com, expires Oct 31, 2026). The org was authenticated and reports source tracking enabled. No existing project files, global default org, or STL PR files were changed.
Creating the scratch org succeeded.
Deploying RootPackageDotProbe from the root package succeeded.
A follow-up deploy without an explicit source path said “Nothing to deploy” despite the local edit.
Deploying that edited Apex class explicitly with --source-dir succeeded.
| * → ObjectNotFoundError; truncated/malformed → ObjectCorruptError. | ||
| */ | ||
| /** @internal */ | ||
| export const readLooseObject = Effect.fnUntraced(function* (gitdir: string, oid: Oid) { |
There was a problem hiding this comment.
Two staging fibers can hash identical content, both observe that the object path does not exist, and then concurrently write the same final path. A reader can observe a partial zlib stream, and an interruption can leave a corrupt object at that OID.
Could we write to a unique temporary file in the object directory and atomically link/rename it into the final OID path, treating an already-created destination as success? That is the normal Git loose-object publication pattern.
There was a problem hiding this comment.
this came up when I was building it as a gap between isogit and real git;
I preserved isogit's behavior because I didn't want to slow it down more by doubling the amount of fs writes
not sure it has ever caused a problem before.
| head.mode === workdirMode && | ||
| index.mode === workdirMode | ||
| ? 'unmodified' | ||
| : 'modified', |
There was a problem hiding this comment.
A file that is present in the index and worktree but absent from HEAD is a staged addition. This branch classifies it as modified, because the only unmodified path requires a HEAD entry and every other tracked worktree file falls through to modified.
This matters when interpreting existing/interrupted shadow repositories. The accompanying test accepts added, modified, or unmodified for staged-add.txt, so it does not protect the prior contract. Could we classify head === undefined && index !== undefined && hasWorkdir as added and make that assertion exact?
There was a problem hiding this comment.
will do, probably a good idea for the sake of git-correctness and some weird interruption edge case.
for us, it's normally moot. STL only ever does stage-then-immediately-commit; we don't actually use staging for anything so that's never had any tests or nuts to cover it.
| const FD_PERMITS_ENV = 'SF_SOURCE_TRACKING_FD_PERMITS'; | ||
| const FD_PERMITS_FALLBACK = IS_WINDOWS ? 64 : 256; | ||
|
|
||
| const readNumberEnv = (name: string, fallback: number) => |
There was a problem hiding this comment.
We already depend on @salesforce/kit, which provides environment helpers, but this migration adds local Effect wrappers here, a separate numeric reader in lock.ts, and more inline parsing in remote tracking. The resulting behavior is inconsistent: malformed values fall back here but are fatal in fileOperations.ts.
If Effect-native parsing is the goal, could we centralize it in one shared helper and deliberately preserve the current Kit semantics and legacy aliases? Otherwise, using Kit at the boundary and lifting the result into Effect would avoid duplicating configuration policy.
There was a problem hiding this comment.
working on this one.
| @@ -691,14 +742,8 @@ export class SourceTracking extends AsyncCreatable { | |||
| ]); | |||
There was a problem hiding this comment.
SourceTracking#setIgnoreConflicts existed on main but is absent from this version. Current VS Code consumers call it when retrying deploy and retrieve operations after a conflict: salesforcedx-vscode-core/src/commands/deployExecutor.ts and salesforcedx-vscode-core/src/commands/retrieveExecutor.ts.
Updating that consumer to this release will fail TypeScript compilation; if types are bypassed, the retry path fails at runtime. Could we restore this public mutator and add a compatibility test covering the consumer flow?
There was a problem hiding this comment.
Current VS Code consumers call it when retrying deploy and retrieve operations after a conflict
no, none of that code exists anymore
https://github.com/search?q=repo%3Aforcedotcom%2Fsalesforcedx-vscode+setIgnoreConflicts&type=code
|
Could we separate the implementation-required changes from the repository/tooling changes, or document why they need to ship together? This PR adds several top-level files ( A few questions:
|
none are necessary, they're dev tooling like lint, tests, snapshots, ts types, coverage docs, etc...all unnecessary
no, that should be gitignored, fixing now
I found it useful to find nut coverage gaps; not all guardrails are CI requirements.
I'll can remove them if you like once we're done iterating on this PR. |
c9accca to
f7f686d
Compare
done, the tooling parts are on a stacked pr. hope that helps |
What does this PR do?
isomorphic-gitwith an Effect-native Git implementation while keeping the shadow repository at.sf/orgs/<orgId>/localSourceTracking.ShadowRepoclass with a factory and cached, stream-derivedadded/modified/deletedstatus; userecordSyncfor advancing the local baseline.maindependencies.Configuration behavior and compatibility
Source-tracking settings are parsed and validated once from a
ConfigProvider, then shared through a process-wideManagedRuntime. Invalid values use documented defaults and emit lifecycle warnings; provider read failures still fail. Tests inject a provider or config layer without changingprocess.env.SF_SOURCE_TRACKING_FD_PERMITS64on Windows;256elsewhereSF_SOURCE_TRACKING_LOCK_TIMEOUT_SECONDS600secondsSF_SOURCE_TRACKING_LOCK_AUTOCLEAR_SECONDS300secondsSF_SOURCE_MEMBER_POLLING_TIMEOUTMath.ceil(memberCount * 0.05) + 5seconds.SF_SOURCE_TRACKING_FILE_VERSION1selects the current format; other accepted values select the legacy format.0Numeric strings with radix prefixes (
0x,0b,0o), non-finite values, malformed text, and whitespace are rejected and defaulted. Positive fractional timeouts remain valid; permits and file version require integers. With the normal environment provider, an empty string is treated as missing.Boolean flags use Effect
Config.Booleanliterals: lowercasetrue/false,yes/no,on/off,y/n, and1/0. Unlike Kit, parsing is case-sensitive; for example,TRUEwarns and defaults tofalse, while lowercaseyesnow enables the flag. Other invalid non-empty values warn and default tofalse.Legacy
SFDX_DISABLE_SOURCE_MEMBER_POLLINGandSFDX_SOURCE_MEMBER_POLLING_TIMEOUTremain supported with deprecation warnings. Canonical settings take precedence. The unused shadow-integrity-check flag was dropped.Known limitation: stale-lock cleanup is mtime-based (five minutes by default); a competing writer could treat an unusually long live lock as stale.
Validation:
yarn build,yarn test,yarn check:effect, andyarn check:circular-deps.What issues does this PR fix or reference?
forcedotcom/salesforcedx-vscode#7174, forcedotcom/salesforcedx-vscode#7278, @W-22711959@