Skip to content

Commit c985fce

Browse files
JonZeollaclaude
andcommitted
fix: use extended-length path prefix to bypass NTFS char restrictions
Python's zipfile.extractall() strips double-quote characters from paths on Windows since NTFS rejects them. Use the \\?\ extended-length path prefix when extracting, which bypasses NTFS filename validation and allows the template directory with quotes to be created. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f0fb118 commit c985fce

1 file changed

Lines changed: 22 additions & 5 deletions

File tree

.github/workflows/ci.yml

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,16 +129,33 @@ jobs:
129129
git config --global user.email "ci@zenable.io"
130130
131131
# The template directory name contains double-quote characters which
132-
# are fundamentally illegal on NTFS — git clone cannot work. Download
133-
# the archive and extract it with Python's zipfile (which can write
134-
# entries with NTFS-illegal chars because it bypasses Win32 APIs for
135-
# internal temp paths). Then run cookiecutter on the extracted tree.
132+
# are fundamentally illegal on NTFS — git clone and normal extraction
133+
# both fail. Extract using Python with the \\?\ extended-length path
134+
# prefix, which bypasses NTFS name validation on Windows.
136135
$zipUrl = "https://github.com/${{ github.repository }}/archive/$env:TEMPLATE_REF.zip"
137136
$zipPath = Join-Path $env:RUNNER_TEMP "template.zip"
138137
$extractDir = Join-Path $env:RUNNER_TEMP "template-src"
139138
Invoke-WebRequest -Uri $zipUrl -OutFile $zipPath
140139
141-
python -c "import zipfile,os,sys; z=zipfile.ZipFile(sys.argv[1]); z.extractall(sys.argv[2]); print(os.path.join(sys.argv[2],os.listdir(sys.argv[2])[0]))" $zipPath $extractDir | Set-Variable templateDir
140+
python -c @"
141+
import zipfile, os, sys
142+
zf = zipfile.ZipFile(sys.argv[1])
143+
dest = sys.argv[2]
144+
# Use extended-length path prefix to bypass NTFS char restrictions
145+
if os.name == 'nt':
146+
dest = '\\\\?\\' + os.path.abspath(dest)
147+
for info in zf.infolist():
148+
target = os.path.join(dest, info.filename)
149+
if info.is_dir():
150+
os.makedirs(target, exist_ok=True)
151+
else:
152+
os.makedirs(os.path.dirname(target), exist_ok=True)
153+
with zf.open(info) as src, open(target, 'wb') as dst:
154+
dst.write(src.read())
155+
# Print the top-level directory (without the \\?\ prefix)
156+
raw_dest = sys.argv[2]
157+
print(os.path.join(raw_dest, os.listdir(dest)[0]))
158+
"@ $zipPath $extractDir | Set-Variable templateDir
142159
143160
uvx --with gitpython cookiecutter $templateDir.Trim() --no-input --output-dir "$env:RUNNER_TEMP"
144161
- name: Verify generated project

0 commit comments

Comments
 (0)