Skip to content

Commit 222105d

Browse files
mishushakovclaude
andauthored
fix: include dotfiles in template file uploads (#1162)
## Summary - Enable glob patterns to match files starting with dot (e.g., `.env`, `.gitignore`) - JS SDK: Add `dot: true` to glob calls in `getAllFilesInPath` - Python SDK: Add `glob.DOTMATCH` flag to glob calls in `get_all_files_in_path` - Add comprehensive tests for dotfile handling in both SDKs Previously, the glob library defaults prevented dotfiles from being matched, preventing upload of configuration files like `.env`. This fix enables proper handling of dotfiles in template file uploads. ## Test plan - ✅ All 16 JS SDK tests pass (4 new dotfile tests) - ✅ All 17 Python SDK tests pass (4 new dotfile tests) - ✅ `pnpm run format` passes - ✅ `pnpm run lint` passes - ✅ `pnpm run typecheck` passes 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Small, well-scoped change to glob options that only broadens matched file sets; main risk is unintentionally including hidden files unless excluded via ignore patterns. > > **Overview** > Template file collection now includes dot-prefixed files and directories (e.g., `.env`, `.gitignore`, `.hidden/**`) when building/uploading templates. > > This updates globbing in the JS SDK’s `getAllFilesInPath` to set `dot: true` (including recursive directory expansion) and the Python SDK’s `get_all_files_in_path` to add `glob.DOTMATCH`, and adds targeted tests in both SDKs to verify dotfile inclusion and that ignore patterns still exclude specified dotfiles. A changeset bumps both `e2b` and `@e2b/python-sdk` as patch releases. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit fc3cbcc. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d855949 commit 222105d

5 files changed

Lines changed: 119 additions & 2 deletions

File tree

.changeset/happy-houses-own.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@e2b/python-sdk': patch
3+
'e2b': patch
4+
---
5+
6+
include .dot files in template file upload

packages/js-sdk/src/template/utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ export async function getAllFilesInPath(
145145
const globFiles = await glob(src, {
146146
ignore: ignorePatterns,
147147
withFileTypes: true,
148+
dot: true,
148149
// this is required so that the ignore pattern is relative to the file path
149150
cwd: contextPath,
150151
})
@@ -164,6 +165,7 @@ export async function getAllFilesInPath(
164165
const dirFiles = await glob(dirPattern, {
165166
ignore: ignorePatterns,
166167
withFileTypes: true,
168+
dot: true,
167169
cwd: contextPath,
168170
})
169171
dirFiles.forEach((f) => files.set(f.fullpath(), f))

packages/js-sdk/tests/template/utils/getAllFilesInPath.test.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,56 @@ describe('getAllFilesInPath', () => {
208208
expect(files).toHaveLength(0)
209209
})
210210

211+
test('should include dotfiles', async () => {
212+
// Create regular and dotfiles
213+
await writeFile(join(testDir, 'file.txt'), 'content')
214+
await writeFile(join(testDir, '.env'), 'SECRET=123')
215+
await writeFile(join(testDir, '.gitignore'), 'node_modules')
216+
217+
const files = await getAllFilesInPath('*', testDir, [])
218+
219+
expect(files).toHaveLength(3)
220+
expect(files.some((f) => f.fullpath().endsWith('file.txt'))).toBe(true)
221+
expect(files.some((f) => f.fullpath().endsWith('.env'))).toBe(true)
222+
expect(files.some((f) => f.fullpath().endsWith('.gitignore'))).toBe(true)
223+
})
224+
225+
test('should include dotfiles in subdirectories', async () => {
226+
await mkdir(join(testDir, 'src'), { recursive: true })
227+
await writeFile(join(testDir, 'src', 'index.ts'), 'content')
228+
await writeFile(join(testDir, 'src', '.env.local'), 'SECRET=123')
229+
230+
const files = await getAllFilesInPath('src', testDir, [])
231+
232+
expect(files.some((f) => f.fullpath().endsWith('index.ts'))).toBe(true)
233+
expect(files.some((f) => f.fullpath().endsWith('.env.local'))).toBe(true)
234+
})
235+
236+
test('should include dotdirectories and their contents', async () => {
237+
await mkdir(join(testDir, '.hidden'), { recursive: true })
238+
await writeFile(join(testDir, '.hidden', 'config.json'), '{}')
239+
await writeFile(join(testDir, 'visible.txt'), 'content')
240+
241+
const files = await getAllFilesInPath('*', testDir, [])
242+
243+
expect(files.some((f) => f.fullpath().endsWith('.hidden'))).toBe(true)
244+
expect(files.some((f) => f.fullpath().endsWith('config.json'))).toBe(true)
245+
expect(files.some((f) => f.fullpath().endsWith('visible.txt'))).toBe(true)
246+
})
247+
248+
test('should respect ignore patterns for dotfiles', async () => {
249+
await writeFile(join(testDir, '.env'), 'SECRET=123')
250+
await writeFile(join(testDir, '.gitignore'), 'node_modules')
251+
await writeFile(join(testDir, 'file.txt'), 'content')
252+
253+
const files = await getAllFilesInPath('*', testDir, ['.env'])
254+
255+
expect(files).toHaveLength(2)
256+
expect(files.some((f) => f.fullpath().endsWith('.env'))).toBe(false)
257+
expect(files.some((f) => f.fullpath().endsWith('.gitignore'))).toBe(true)
258+
expect(files.some((f) => f.fullpath().endsWith('file.txt'))).toBe(true)
259+
})
260+
211261
test('should handle listing all files in current directory with dot pattern', async () => {
212262
// Create a small directory tree inside the test directory
213263
await writeFile(join(testDir, 'root.txt'), 'root')

packages/python-sdk/e2b/template/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def get_all_files_in_path(
155155
abs_context_path = os.path.abspath(context_path)
156156
files_glob = glob.glob(
157157
src,
158-
flags=glob.GLOBSTAR,
158+
flags=glob.GLOBSTAR | glob.DOTMATCH,
159159
root_dir=abs_context_path,
160160
exclude=ignore_patterns,
161161
)
@@ -170,7 +170,7 @@ def get_all_files_in_path(
170170
files.add(file_path)
171171
dir_files = glob.glob(
172172
normalize_path(file) + "/**/*",
173-
flags=glob.GLOBSTAR,
173+
flags=glob.GLOBSTAR | glob.DOTMATCH,
174174
root_dir=abs_context_path,
175175
exclude=ignore_patterns,
176176
)

packages/python-sdk/tests/shared/template/utils/get_all_files_in_path.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,65 @@ def test_should_handle_complex_ignore_patterns_with_directories(self, test_dir):
223223
assert any("helper.ts" in f for f in files)
224224
assert not any("test.spec.ts" in f for f in files)
225225

226+
def test_should_include_dotfiles(self, test_dir):
227+
"""Test that function includes files starting with a dot."""
228+
with open(os.path.join(test_dir, "file.txt"), "w") as f:
229+
f.write("content")
230+
with open(os.path.join(test_dir, ".env"), "w") as f:
231+
f.write("SECRET=123")
232+
with open(os.path.join(test_dir, ".gitignore"), "w") as f:
233+
f.write("node_modules")
234+
235+
files = get_all_files_in_path("*", test_dir, [])
236+
237+
assert len(files) == 3
238+
assert any(".env" in f for f in files)
239+
assert any(".gitignore" in f for f in files)
240+
assert any("file.txt" in f for f in files)
241+
242+
def test_should_include_dotfiles_in_subdirectories(self, test_dir):
243+
"""Test that function includes dotfiles inside subdirectories."""
244+
os.makedirs(os.path.join(test_dir, "src"), exist_ok=True)
245+
with open(os.path.join(test_dir, "src", "index.ts"), "w") as f:
246+
f.write("content")
247+
with open(os.path.join(test_dir, "src", ".env.local"), "w") as f:
248+
f.write("SECRET=123")
249+
250+
files = get_all_files_in_path("src", test_dir, [])
251+
252+
assert any("index.ts" in f for f in files)
253+
assert any(".env.local" in f for f in files)
254+
255+
def test_should_include_dotdirectories_and_their_contents(self, test_dir):
256+
"""Test that function includes dot-prefixed directories and their contents."""
257+
os.makedirs(os.path.join(test_dir, ".hidden"), exist_ok=True)
258+
with open(os.path.join(test_dir, ".hidden", "config.json"), "w") as f:
259+
f.write("{}")
260+
with open(os.path.join(test_dir, "visible.txt"), "w") as f:
261+
f.write("content")
262+
263+
files = get_all_files_in_path("*", test_dir, [])
264+
265+
assert any(".hidden" in f for f in files)
266+
assert any("config.json" in f for f in files)
267+
assert any("visible.txt" in f for f in files)
268+
269+
def test_should_respect_ignore_patterns_for_dotfiles(self, test_dir):
270+
"""Test that dotfiles can be excluded via ignore patterns."""
271+
with open(os.path.join(test_dir, ".env"), "w") as f:
272+
f.write("SECRET=123")
273+
with open(os.path.join(test_dir, ".gitignore"), "w") as f:
274+
f.write("node_modules")
275+
with open(os.path.join(test_dir, "file.txt"), "w") as f:
276+
f.write("content")
277+
278+
files = get_all_files_in_path("*", test_dir, [".env"])
279+
280+
assert len(files) == 2
281+
assert not any(f.endswith(".env") for f in files)
282+
assert any(".gitignore" in f for f in files)
283+
assert any("file.txt" in f for f in files)
284+
226285
def test_should_handle_symlinks(self, test_dir):
227286
"""Test that function handles symbolic links."""
228287
# Create a file and a symlink to it

0 commit comments

Comments
 (0)