Skip to content

Commit 6078d29

Browse files
authored
fix(platform): make template directory NTFS-safe for Windows (#92)
1 parent 8010670 commit 6078d29

57 files changed

Lines changed: 50 additions & 89 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/etc/dictionary.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ refurb
1414
skopeo
1515
syft
1616
taskfile
17+
winget
1718
zenable
1819
zizmor

.github/workflows/ci.yml

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,10 @@ jobs:
106106
name: Windows Smoke Test
107107
runs-on: windows-latest
108108
steps:
109-
# Note: no checkout step. The cookiecutter template directory contains
110-
# characters (pipe, quotes) that are illegal on NTFS, so we cannot check
111-
# out the repo on Windows. Instead, cookiecutter fetches the template
112-
# directly from the remote branch.
113109
- name: Setup uv
114110
uses: astral-sh/setup-uv@v7
115111
with:
116112
python-version: ${{ env.python_version }}
117-
# No checkout in this job (NTFS-illegal chars in template dir), so disable cache
118113
enable-cache: false
119114
ignore-empty-workdir: true
120115
- name: Install Task
@@ -126,24 +121,30 @@ jobs:
126121
env:
127122
RUN_POST_HOOK: 'true'
128123
SKIP_GIT_PUSH: 'true'
129-
TEMPLATE_REF: ${{ github.event.pull_request.head.sha || github.sha }}
124+
TEMPLATE_REPO: ${{ github.repository }}
125+
TEMPLATE_REF: ${{ github.head_ref || github.ref_name }}
130126
run: |
131127
git config --global user.name "CI Automation"
132128
git config --global user.email "ci@zenable.io"
133129
134-
# The template directory name contains NTFS-illegal characters
135-
# (double quotes, pipe). Use extract_template_zip.py to safely
136-
# extract and rename only the top-level template dir.
137-
zipUrl="https://github.com/${{ github.repository }}/archive/${TEMPLATE_REF}.zip"
138-
scriptUrl="https://raw.githubusercontent.com/${{ github.repository }}/${TEMPLATE_REF}/scripts/extract_template_zip.py"
139-
tmpdir=$(mktemp -d)
140-
curl -fsSL "$zipUrl" -o "$tmpdir/template.zip"
141-
curl -fsSL "$scriptUrl" -o "$tmpdir/extract_template_zip.py"
142-
repoDir=$(python3 "$tmpdir/extract_template_zip.py" "$tmpdir/template.zip" "$tmpdir/src")
143-
144-
uvx --with gitpython cookiecutter "$repoDir" --no-input \
145-
project_name="ci-test-project" \
146-
--output-dir "$RUNNER_TEMP"
130+
# Use the same command as README.md (gh: syntax). On Windows, if the
131+
# default branch still has NTFS-illegal paths (pre-merge), the gh: clone
132+
# fails because cookiecutter clones main first. Fall back to a local
133+
# clone in that case. After merge, the gh: command succeeds directly.
134+
uvx --with gitpython cookiecutter \
135+
"gh:${TEMPLATE_REPO}" \
136+
--checkout "${TEMPLATE_REF}" \
137+
--no-input project_name="ci-test-project" \
138+
--output-dir "$RUNNER_TEMP" \
139+
|| {
140+
echo "::warning::gh: clone failed (expected pre-merge on Windows). Using local clone."
141+
template_dir="$RUNNER_TEMP/template-repo"
142+
git clone --no-checkout "https://github.com/${TEMPLATE_REPO}.git" "$template_dir"
143+
git -C "$template_dir" checkout "${TEMPLATE_REF}"
144+
uvx --with gitpython cookiecutter "$template_dir" \
145+
--no-input project_name="ci-test-project" \
146+
--output-dir "$RUNNER_TEMP"
147+
}
147148
- name: Verify generated project
148149
shell: pwsh
149150
run: |

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
exclude: '^{{cookiecutter\.project_name\|replace\(" ", ""\)}}/.*'
2+
exclude: '^{{cookiecutter\.project_name}}/.*'
33
repos:
44
- repo: https://github.com/astral-sh/ruff-pre-commit
55
rev: 3db93a2be6f214ed722bf7bce095ec1b1715422a # frozen: v0.14.2

README.md

Lines changed: 9 additions & 4 deletions

Taskfile.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,12 @@ tasks:
9393
update:
9494
desc: Update the project dev and runtime dependencies
9595
cmds:
96-
# This currently assumes uv was installed via uv (locally); we will want to make it more flexible in the future
97-
- '{{if ne .GITHUB_ACTIONS "true"}}brew upgrade uv{{end}}'
96+
# Upgrade uv via brew on macOS/Linux when running locally (not CI)
97+
- '{{if and (ne .GITHUB_ACTIONS "true") (ne OS "windows")}}brew upgrade uv{{end}}'
9898
- uv tool upgrade --all
9999
- pre-commit autoupdate --freeze --jobs 4
100100
# Copy the newly updated config into the project template, excluding the exclude line
101-
- cat .pre-commit-config.yaml | grep -v ^exclude > '{{`{{cookiecutter.project_name|replace(" ", "")}}`}}/.pre-commit-config.yaml'
101+
- cat .pre-commit-config.yaml | grep -v ^exclude > '{{`{{cookiecutter.project_name}}`}}/.pre-commit-config.yaml'
102102
- uv lock --upgrade
103103
# This can take a while but it's required for the following step to update BuildKit in the docker driver
104104
- '{{if eq .CLI_ARGS "all"}}docker buildx rm multiplatform || true{{end}}'

docs/hooks.md

Lines changed: 1 addition & 1 deletion

hooks/pre_gen_project.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,16 @@
1616

1717

1818
def validate_project_name() -> None:
19-
"""Validate that project_name starts with an alphabetical character."""
20-
# Check if project_name starts with an alphabetical character
19+
"""Validate that project_name starts with an alphabetical character and contains no spaces."""
2120
if not re.match(r"^[a-zA-Z]", PROJECT_NAME):
2221
LOG.error(
23-
"Invalid project name '%s': Python project names must start with an alphabetical character (a-z or A-Z).",
22+
"Invalid project name '%s': must start with an alphabetical character (a-z or A-Z).",
23+
PROJECT_NAME,
24+
)
25+
sys.exit(1)
26+
if " " in PROJECT_NAME:
27+
LOG.error(
28+
"Invalid project name '%s': must not contain spaces. Use hyphens instead (e.g. 'my-project').",
2429
PROJECT_NAME,
2530
)
2631
sys.exit(1)

scripts/extract_template_zip.py

Lines changed: 0 additions & 53 deletions
This file was deleted.

tests/test_cookiecutter.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,11 +220,13 @@ def test_autofix_hook(cookies, context):
220220
"-invalid", # starts with dash
221221
"9project", # starts with number
222222
"!invalid", # starts with special character
223+
"My Project", # contains space
224+
"has space", # contains space
223225
],
224226
)
225227
def test_invalid_project_name_validation(cookies, invalid_name):
226228
"""
227-
Test that project names starting with non-alphabetical characters are rejected
229+
Test that project names with invalid characters are rejected
228230
"""
229231
result = cookies.bake(extra_context={"project_name": invalid_name})
230232

@@ -238,7 +240,7 @@ def test_invalid_project_name_validation(cookies, invalid_name):
238240
[
239241
"ValidProject", # starts with uppercase
240242
"validproject", # starts with lowercase
241-
"My Project", # starts with uppercase, has space
243+
"My-Project", # starts with uppercase, has hyphen
242244
"a1234", # starts with lowercase, has numbers
243245
"Z_project", # starts with uppercase, has underscore
244246
],

{{cookiecutter.project_name|replace(" ", "")}}/.cursor/rules/project.mdc renamed to {{cookiecutter.project_name}}/.cursor/rules/project.mdc

File renamed without changes.

0 commit comments

Comments
 (0)