You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
createGit(fs).init() and clone() into a new directory fail on the shell filesystems (Workspace / WorkspaceFileSystem and InMemoryFs) when the installed isomorphic-git is 1.40.8 or newer:
Error: ENOENT: no such file or directory, mkdir '/repo/.git/hooks'
The git fs adapter's mkdir passes errors from the shell filesystem through unchanged. Those errors carry the code only in their message ("ENOENT: …", "EEXIST: …"), not as a code property. Since 1.40.8, isomorphic-git's mkdirp dispatches on err.code and throws anything that isn't EEXIST or ENOENT, so it can no longer create the missing parent or ignore an existing directory.
@cloudflare/shell declares "isomorphic-git": "^1.38.5", so a fresh install today resolves to an affected version (1.43.3).
To Reproduce
No Durable Object, network or provider needed:
import{InMemoryFs}from"@cloudflare/shell";import{createGit}from"@cloudflare/shell/git";awaitcreateGit(newInMemoryFs()).init({dir: "/repo"});// Error: ENOENT: no such file or directory, mkdir '/repo/.git/hooks' (err.code === undefined)
The same happens with a Workspace-backed WorkspaceFileSystem, and with clone({ dir }) into a directory that doesn't exist yet. We hit it cloning Cloudflare Artifacts repositories into a Think workspace.
The filesystems' errors directly:
constfs=newInMemoryFs();awaitfs.mkdir("/a");awaitfs.mkdir("/a");// Error: EEXIST: directory already exists, mkdir '/a' code: undefinedawaitfs.mkdir("/x/y");// Error: ENOENT: no such file or directory, mkdir '/x/y' code: undefined
Expected behavior
init and clone create the repository, including every directory under .git, on any isomorphic-git version the package's range allows.
Screenshots
N/A — error output above.
Version:
@cloudflare/shell
0.4.3 (latest)
isomorphic-git
1.43.3 (fails), bisected below
runtime
Cloudflare Workers + Durable Objects; also reproduces in Node with InMemoryFs
Additional context
Root cause
Pinned to main at 48d9c36f5f1ff18a91fbf0659b9a8e026d1cac43.
readFile, unlink, stat, lstat and readlink wrap their errors with fsError so isomorphic-git gets a code. mkdir doesn't:
So on older versions the code-less errors were never surfaced. init skipped hooks, info, objects/* and refs/* without a word, and later writes happened to work because the adapter's writeFile creates parents recursively.
1.40.8 replaced this with a mkdirp helper that rethrows anything that isn't EEXIST/ENOENT:
The repo's own tests don't catch it because pnpm-lock.yaml pins isomorphic-git@1.38.5.
Suggested fix
Route mkdir errors through fsError, and have fsError take the code from the message when the cause has none. Today it defaults every code-less error to ENOENT, which would report an existing directory to mkdirp as a missing one; it then walks up to the root and throws instead of returning:
That lets isomorphic-git's own mkdirp do its job on every version. Checked against 1.43.3 by adding codes this way around InMemoryFs.mkdir: init creates HEAD, config, hooks, info, objects and refs, and a second init succeeds. Setting code where InMemoryFs and Workspace throw would be the more thorough fix, and would help other Node-style consumers too.
A regression test fits next to the existing unlink cases in git-fs-adapter.test.ts: mkdir on an existing directory rejects with code: "EEXIST", mkdir under a missing parent rejects with code: "ENOENT", and createGit(new InMemoryFs()).init({ dir: "/repo" }) leaves /repo/.git/hooks and /repo/.git/refs/heads in place. Bumping the lockfile's isomorphic-git would let the Workspace-backed tests in git.test.ts cover it too.
We currently carry a one-line pnpm patch for @cloudflare/shell@0.4.3 that makes the adapter's mkdir always recursive (fs.mkdir(path, { recursive: true })). It works, but it masks the error codes rather than fixing them, so we'd rather drop it for an upstream fix.
Activity