Submit an issue View all issues Source
MIR-1075

Deploy drops directories preserved via `.keep` + gitignore negation patterns

Done public
phinze phinze Opened Apr 28, 2026 Updated May 5, 2026

miren deploy drops directories that are kept in source control via the common pattern of "ignore the directory contents but keep the directory itself with a .keep file." This breaks Ruby/Rails-style apps that rely on tmp/pids/, log/, etc. existing at runtime.

Repro

A vanilla bridgetown new app ships with this .gitignore:

/tmp/*
!/tmp/.keep
/tmp/pids/*
!/tmp/pids/
!/tmp/pids/.keep

…and config/puma.rb writes its pidfile to tmp/pids/server.pid. Deploy succeeds, but Puma fails to boot:

puma/launcher.rb:328: No such file or directory @ rb_sysopen - tmp/pids/server.pid (Errno::ENOENT)

Originally reported by Javier Cervantes against solojavier/mysite. Reproduced end-to-end against the local dev server, plus a failing unit test (TestMakeTarBridgetownTmpPids in pkg/tarx/tarx_test.go) that pins the bug at the tar layer.

Root cause

In pkg/tarx/tarx.go:48-78, isGitignored for directories tries both forms of the path:

paths := []string{relPath}
if isDir {
    paths = append(paths, relPath+"/")
}
for _, checkPath := range paths {
    ignore, _ := pathspec.GitIgnore(patterns, checkPath)
    if ignore {
        return true, nil   // short-circuits
    }
}

For tmp/pids (no slash), /tmp/* matches and the trailing-slash negation !/tmp/pids/ does not. The loop short-circuits before checking the trailing-slash form, the walker hits filepath.SkipDir at tarx.go:313, and .keep is never visited. The directory is absent from the deploy tar.

Fix shape

For directories, only check the trailing-slash form (the canonical form for gitignore directory matching). Negation patterns ending in / are explicitly directory-only and are how you re-include directories.

Impact

Affects any app following the Rails/Bridgetown convention of preserving runtime directories with .keep plus a re-include negation. Easy to hit on first-time deploy, hard to debug from runtime logs alone.