From 22924523bf7bc06781db59e8c79ab4f5ee647dc2 Mon Sep 17 00:00:00 2001 From: Jon Zeolla Date: Sun, 5 Apr 2026 12:01:28 -0400 Subject: [PATCH 1/6] fix(platform): make template directory NTFS-safe for Windows support The template directory name `{{cookiecutter.project_name|replace(" ", "")}}` contained pipe (`|`) and double-quote (`"`) characters which are illegal on NTFS. This prevented Windows users from cloning the repo via `cookiecutter gh:zenable-io/ai-native-python`. Rename to `{{cookiecutter.project_name}}` and add pre-gen hook validation to reject spaces in project names (use hyphens instead). This eliminates the need for the extract_template_zip.py workaround and lets Windows CI do a normal checkout. Also adds Windows install instructions (winget) to README and guards `brew upgrade` commands with OS checks in Taskfiles. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/etc/dictionary.txt | 1 + .github/workflows/ci.yml | 22 ++------ .pre-commit-config.yaml | 2 +- README.md | 13 +++-- Taskfile.yml | 6 +-- docs/hooks.md | 2 +- hooks/pre_gen_project.py | 11 ++-- scripts/extract_template_zip.py | 53 ------------------- tests/test_cookiecutter.py | 6 ++- .../.cursor/rules/project.mdc | 0 .../.cursor/rules/testing.mdc | 0 .../.dockerignore | 0 .../.github/.grant.yml | 0 .../.github/CODEOWNERS | 0 .../.github/ISSUE_TEMPLATE.md | 0 .../.github/PULL_REQUEST_TEMPLATE.md | 0 .../.github/actions/bootstrap/action.yml | 0 .../.github/copilot-instructions.md | 0 .../.github/dependabot.yml | 0 .../.github/etc/dictionary.txt | 0 .../.github/linters/cspell.config.js | 0 .../.github/linters/lychee.toml | 0 .../.github/workflows/ci.yml | 0 .../.github/workflows/commit.yml | 0 .../.github/workflows/release.yml | 0 .../.github/workflows/security.yml | 0 .../.github/workflows/update.yml | 0 .../.github/workflows/validate_pr_titles.yml | 0 .../.gitignore | 0 .../.pre-commit-config.yaml | 2 +- .../CLAUDE.md | 0 .../CONTRIBUTING.md | 0 .../Dockerfile | 0 .../FAQ.md | 0 .../LICENSE | 0 .../README.md | 0 .../SECURITY.md | 0 .../Taskfile.yml | 4 +- .../pyproject.toml | 0 .../renovate.json | 0 .../scripts/get_epoch.sh | 0 .../scripts/get_os.sh | 0 .../scripts/get_platform.sh | 0 .../scripts/get_rfc3339_timestamp.py | 0 .../scripts/scan_image.py | 0 .../scripts/scan_workflow_logs.sh | 0 .../scripts/validate_service_definition.py | 0 .../service_definition.yaml | 0 .../src/main.py | 0 .../{{cookiecutter.project_slug}}/__init__.py | 0 .../{{cookiecutter.project_slug}}/config.py | 0 .../constants.py | 0 .../tests/__init__.py | 0 .../tests/test_config.py | 0 .../tests/test_integration.py | 0 .../tests/test_main.py | 0 .../tests/test_package.py | 0 57 files changed, 33 insertions(+), 89 deletions(-) delete mode 100755 scripts/extract_template_zip.py rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/.cursor/rules/project.mdc" => {{cookiecutter.project_name}}/.cursor/rules/project.mdc (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/.cursor/rules/testing.mdc" => {{cookiecutter.project_name}}/.cursor/rules/testing.mdc (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/.dockerignore" => {{cookiecutter.project_name}}/.dockerignore (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/.grant.yml" => {{cookiecutter.project_name}}/.github/.grant.yml (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/CODEOWNERS" => {{cookiecutter.project_name}}/.github/CODEOWNERS (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/ISSUE_TEMPLATE.md" => {{cookiecutter.project_name}}/.github/ISSUE_TEMPLATE.md (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/PULL_REQUEST_TEMPLATE.md" => {{cookiecutter.project_name}}/.github/PULL_REQUEST_TEMPLATE.md (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/actions/bootstrap/action.yml" => {{cookiecutter.project_name}}/.github/actions/bootstrap/action.yml (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/copilot-instructions.md" => {{cookiecutter.project_name}}/.github/copilot-instructions.md (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/dependabot.yml" => {{cookiecutter.project_name}}/.github/dependabot.yml (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/etc/dictionary.txt" => {{cookiecutter.project_name}}/.github/etc/dictionary.txt (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/linters/cspell.config.js" => {{cookiecutter.project_name}}/.github/linters/cspell.config.js (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/linters/lychee.toml" => {{cookiecutter.project_name}}/.github/linters/lychee.toml (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/ci.yml" => {{cookiecutter.project_name}}/.github/workflows/ci.yml (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/commit.yml" => {{cookiecutter.project_name}}/.github/workflows/commit.yml (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/release.yml" => {{cookiecutter.project_name}}/.github/workflows/release.yml (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/security.yml" => {{cookiecutter.project_name}}/.github/workflows/security.yml (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/update.yml" => {{cookiecutter.project_name}}/.github/workflows/update.yml (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/validate_pr_titles.yml" => {{cookiecutter.project_name}}/.github/workflows/validate_pr_titles.yml (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/.gitignore" => {{cookiecutter.project_name}}/.gitignore (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/.pre-commit-config.yaml" => {{cookiecutter.project_name}}/.pre-commit-config.yaml (97%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/CLAUDE.md" => {{cookiecutter.project_name}}/CLAUDE.md (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/CONTRIBUTING.md" => {{cookiecutter.project_name}}/CONTRIBUTING.md (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/Dockerfile" => {{cookiecutter.project_name}}/Dockerfile (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/FAQ.md" => {{cookiecutter.project_name}}/FAQ.md (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/LICENSE" => {{cookiecutter.project_name}}/LICENSE (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/README.md" => {{cookiecutter.project_name}}/README.md (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/SECURITY.md" => {{cookiecutter.project_name}}/SECURITY.md (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/Taskfile.yml" => {{cookiecutter.project_name}}/Taskfile.yml (98%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/pyproject.toml" => {{cookiecutter.project_name}}/pyproject.toml (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/renovate.json" => {{cookiecutter.project_name}}/renovate.json (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/get_epoch.sh" => {{cookiecutter.project_name}}/scripts/get_epoch.sh (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/get_os.sh" => {{cookiecutter.project_name}}/scripts/get_os.sh (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/get_platform.sh" => {{cookiecutter.project_name}}/scripts/get_platform.sh (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/get_rfc3339_timestamp.py" => {{cookiecutter.project_name}}/scripts/get_rfc3339_timestamp.py (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/scan_image.py" => {{cookiecutter.project_name}}/scripts/scan_image.py (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/scan_workflow_logs.sh" => {{cookiecutter.project_name}}/scripts/scan_workflow_logs.sh (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/validate_service_definition.py" => {{cookiecutter.project_name}}/scripts/validate_service_definition.py (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/service_definition.yaml" => {{cookiecutter.project_name}}/service_definition.yaml (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/src/main.py" => {{cookiecutter.project_name}}/src/main.py (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/src/{{cookiecutter.project_slug}}/__init__.py" => {{cookiecutter.project_name}}/src/{{cookiecutter.project_slug}}/__init__.py (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/src/{{cookiecutter.project_slug}}/config.py" => {{cookiecutter.project_name}}/src/{{cookiecutter.project_slug}}/config.py (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/src/{{cookiecutter.project_slug}}/constants.py" => {{cookiecutter.project_name}}/src/{{cookiecutter.project_slug}}/constants.py (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/__init__.py" => {{cookiecutter.project_name}}/tests/__init__.py (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/test_config.py" => {{cookiecutter.project_name}}/tests/test_config.py (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/test_integration.py" => {{cookiecutter.project_name}}/tests/test_integration.py (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/test_main.py" => {{cookiecutter.project_name}}/tests/test_main.py (100%) rename "{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/test_package.py" => {{cookiecutter.project_name}}/tests/test_package.py (100%) diff --git a/.github/etc/dictionary.txt b/.github/etc/dictionary.txt index cad5dda..12daada 100644 --- a/.github/etc/dictionary.txt +++ b/.github/etc/dictionary.txt @@ -14,5 +14,6 @@ refurb skopeo syft taskfile +winget zenable zizmor diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e80f55a..d59bd80 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,17 +106,12 @@ jobs: name: Windows Smoke Test runs-on: windows-latest steps: - # Note: no checkout step. The cookiecutter template directory contains - # characters (pipe, quotes) that are illegal on NTFS, so we cannot check - # out the repo on Windows. Instead, cookiecutter fetches the template - # directly from the remote branch. + - name: Checkout the repository + uses: actions/checkout@v6 - name: Setup uv uses: astral-sh/setup-uv@v7 with: python-version: ${{ env.python_version }} - # No checkout in this job (NTFS-illegal chars in template dir), so disable cache - enable-cache: false - ignore-empty-workdir: true - name: Install Task uses: go-task/setup-task@v2 with: @@ -126,22 +121,11 @@ jobs: env: RUN_POST_HOOK: 'true' SKIP_GIT_PUSH: 'true' - TEMPLATE_REF: ${{ github.event.pull_request.head.sha || github.sha }} run: | git config --global user.name "CI Automation" git config --global user.email "ci@zenable.io" - # The template directory name contains NTFS-illegal characters - # (double quotes, pipe). Use extract_template_zip.py to safely - # extract and rename only the top-level template dir. - zipUrl="https://github.com/${{ github.repository }}/archive/${TEMPLATE_REF}.zip" - scriptUrl="https://raw.githubusercontent.com/${{ github.repository }}/${TEMPLATE_REF}/scripts/extract_template_zip.py" - tmpdir=$(mktemp -d) - curl -fsSL "$zipUrl" -o "$tmpdir/template.zip" - curl -fsSL "$scriptUrl" -o "$tmpdir/extract_template_zip.py" - repoDir=$(python3 "$tmpdir/extract_template_zip.py" "$tmpdir/template.zip" "$tmpdir/src") - - uvx --with gitpython cookiecutter "$repoDir" --no-input \ + uvx --with gitpython cookiecutter . --no-input \ project_name="ci-test-project" \ --output-dir "$RUNNER_TEMP" - name: Verify generated project diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a3e3cf0..896d7fc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,5 @@ --- -exclude: '^{{cookiecutter\.project_name\|replace\(" ", ""\)}}/.*' +exclude: '^{{cookiecutter\.project_name}}/.*' repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: 3db93a2be6f214ed722bf7bce095ec1b1715422a # frozen: v0.14.2 diff --git a/README.md b/README.md index 5b1008c..b1e1e32 100644 --- a/README.md +++ b/README.md @@ -24,14 +24,19 @@ For more details, see our [documentation](docs/index.md). ## Getting Started 1. Create an [empty GitHub repository](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-new-repository) -1. Ensure you have [`brew`](https://brew.sh/) installed -1. Generate a project with the same name as the repo you just made: +1. Install the prerequisites and generate your project: + + **macOS / Linux (Homebrew)** ```bash - # Install the prerequisites brew install uv go-task + uvx --with gitpython cookiecutter gh:zenable-io/ai-native-python + ``` + + **Windows (PowerShell)** - # Initialize your project + ```powershell + winget install astral-sh.uv Task.Task uvx --with gitpython cookiecutter gh:zenable-io/ai-native-python ``` diff --git a/Taskfile.yml b/Taskfile.yml index 6b4f016..59fa561 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -93,12 +93,12 @@ tasks: update: desc: Update the project dev and runtime dependencies cmds: - # This currently assumes uv was installed via uv (locally); we will want to make it more flexible in the future - - '{{if ne .GITHUB_ACTIONS "true"}}brew upgrade uv{{end}}' + # Upgrade uv via brew on macOS/Linux when running locally (not CI) + - '{{if and (ne .GITHUB_ACTIONS "true") (ne OS "windows")}}brew upgrade uv{{end}}' - uv tool upgrade --all - pre-commit autoupdate --freeze --jobs 4 # Copy the newly updated config into the project template, excluding the exclude line - - cat .pre-commit-config.yaml | grep -v ^exclude > '{{`{{cookiecutter.project_name|replace(" ", "")}}`}}/.pre-commit-config.yaml' + - cat .pre-commit-config.yaml | grep -v ^exclude > '{{`{{cookiecutter.project_name}}`}}/.pre-commit-config.yaml' - uv lock --upgrade # This can take a while but it's required for the following step to update BuildKit in the docker driver - '{{if eq .CLI_ARGS "all"}}docker buildx rm multiplatform || true{{end}}' diff --git a/docs/hooks.md b/docs/hooks.md index 9157a64..d98dfaa 100644 --- a/docs/hooks.md +++ b/docs/hooks.md @@ -46,7 +46,7 @@ task init # Installs pre-commit and sets up hooks, alongside setting up other p 6. **GitHub Actions** - Actionlint validation 7. **OpenAPI** - Schema validation -For the full list of hooks, see `{{cookiecutter.project_name|replace(" ", "")}}/.pre-commit-config.yaml` +For the full list of hooks, see `{{cookiecutter.project_name}}/.pre-commit-config.yaml` ### Configuration diff --git a/hooks/pre_gen_project.py b/hooks/pre_gen_project.py index 61d21bc..d9c7d1c 100755 --- a/hooks/pre_gen_project.py +++ b/hooks/pre_gen_project.py @@ -16,11 +16,16 @@ def validate_project_name() -> None: - """Validate that project_name starts with an alphabetical character.""" - # Check if project_name starts with an alphabetical character + """Validate that project_name starts with an alphabetical character and contains no spaces.""" if not re.match(r"^[a-zA-Z]", PROJECT_NAME): LOG.error( - "Invalid project name '%s': Python project names must start with an alphabetical character (a-z or A-Z).", + "Invalid project name '%s': must start with an alphabetical character (a-z or A-Z).", + PROJECT_NAME, + ) + sys.exit(1) + if " " in PROJECT_NAME: + LOG.error( + "Invalid project name '%s': must not contain spaces. Use hyphens instead (e.g. 'my-project').", PROJECT_NAME, ) sys.exit(1) diff --git a/scripts/extract_template_zip.py b/scripts/extract_template_zip.py deleted file mode 100755 index 88ec0fb..0000000 --- a/scripts/extract_template_zip.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python3 -"""Extract a cookiecutter template from a GitHub archive zip. - -The top-level template directory in the zip contains NTFS-illegal characters -(pipe, double-quotes) from the Jinja2 expression in -``{{cookiecutter.project_name|replace(" ", "")}}``. This script renames -**only** that top-level directory to the safe ``{{cookiecutter.project_name}}`` -form while preserving nested cookiecutter directories like -``{{cookiecutter.project_slug}}`` so cookiecutter renders them correctly. - -Usage: - python extract_template_zip.py - -Prints the path to the extracted template root on stdout. -""" - -import os -import sys -import zipfile - - -def extract(zip_path: str, dest: str) -> str: - """Extract the zip, renaming only the top-level template directory.""" - with zipfile.ZipFile(zip_path) as zf: - for info in zf.infolist(): - parts = info.filename.split("/") - - safe = [] - for i, part in enumerate(parts): - # Index 0 is the repo root (e.g. ai-native-python-). - # Index 1 is the template directory with NTFS-illegal chars. - if i == 1 and "{{cookiecutter." in part: - safe.append("{{cookiecutter.project_name}}") - else: - safe.append(part) - - target = os.path.join(dest, *[s for s in safe if s]) - - if info.is_dir(): - os.makedirs(target, exist_ok=True) - else: - os.makedirs(os.path.dirname(target), exist_ok=True) - with zf.open(info) as src, open(target, "wb") as dst: - dst.write(src.read()) - - return os.path.join(dest, os.listdir(dest)[0]) - - -if __name__ == "__main__": - if len(sys.argv) != 3: - print(f"Usage: {sys.argv[0]} ", file=sys.stderr) - sys.exit(1) - print(extract(sys.argv[1], sys.argv[2])) diff --git a/tests/test_cookiecutter.py b/tests/test_cookiecutter.py index fdf9a03..71cf765 100755 --- a/tests/test_cookiecutter.py +++ b/tests/test_cookiecutter.py @@ -220,11 +220,13 @@ def test_autofix_hook(cookies, context): "-invalid", # starts with dash "9project", # starts with number "!invalid", # starts with special character + "My Project", # contains space + "has space", # contains space ], ) def test_invalid_project_name_validation(cookies, invalid_name): """ - Test that project names starting with non-alphabetical characters are rejected + Test that project names with invalid characters are rejected """ result = cookies.bake(extra_context={"project_name": invalid_name}) @@ -238,7 +240,7 @@ def test_invalid_project_name_validation(cookies, invalid_name): [ "ValidProject", # starts with uppercase "validproject", # starts with lowercase - "My Project", # starts with uppercase, has space + "My-Project", # starts with uppercase, has hyphen "a1234", # starts with lowercase, has numbers "Z_project", # starts with uppercase, has underscore ], diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.cursor/rules/project.mdc" b/{{cookiecutter.project_name}}/.cursor/rules/project.mdc similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.cursor/rules/project.mdc" rename to {{cookiecutter.project_name}}/.cursor/rules/project.mdc diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.cursor/rules/testing.mdc" b/{{cookiecutter.project_name}}/.cursor/rules/testing.mdc similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.cursor/rules/testing.mdc" rename to {{cookiecutter.project_name}}/.cursor/rules/testing.mdc diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.dockerignore" b/{{cookiecutter.project_name}}/.dockerignore similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.dockerignore" rename to {{cookiecutter.project_name}}/.dockerignore diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/.grant.yml" b/{{cookiecutter.project_name}}/.github/.grant.yml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/.grant.yml" rename to {{cookiecutter.project_name}}/.github/.grant.yml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/CODEOWNERS" b/{{cookiecutter.project_name}}/.github/CODEOWNERS similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/CODEOWNERS" rename to {{cookiecutter.project_name}}/.github/CODEOWNERS diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/ISSUE_TEMPLATE.md" b/{{cookiecutter.project_name}}/.github/ISSUE_TEMPLATE.md similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/ISSUE_TEMPLATE.md" rename to {{cookiecutter.project_name}}/.github/ISSUE_TEMPLATE.md diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/PULL_REQUEST_TEMPLATE.md" b/{{cookiecutter.project_name}}/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/PULL_REQUEST_TEMPLATE.md" rename to {{cookiecutter.project_name}}/.github/PULL_REQUEST_TEMPLATE.md diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/actions/bootstrap/action.yml" b/{{cookiecutter.project_name}}/.github/actions/bootstrap/action.yml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/actions/bootstrap/action.yml" rename to {{cookiecutter.project_name}}/.github/actions/bootstrap/action.yml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/copilot-instructions.md" b/{{cookiecutter.project_name}}/.github/copilot-instructions.md similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/copilot-instructions.md" rename to {{cookiecutter.project_name}}/.github/copilot-instructions.md diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/dependabot.yml" b/{{cookiecutter.project_name}}/.github/dependabot.yml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/dependabot.yml" rename to {{cookiecutter.project_name}}/.github/dependabot.yml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/etc/dictionary.txt" b/{{cookiecutter.project_name}}/.github/etc/dictionary.txt similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/etc/dictionary.txt" rename to {{cookiecutter.project_name}}/.github/etc/dictionary.txt diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/linters/cspell.config.js" b/{{cookiecutter.project_name}}/.github/linters/cspell.config.js similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/linters/cspell.config.js" rename to {{cookiecutter.project_name}}/.github/linters/cspell.config.js diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/linters/lychee.toml" b/{{cookiecutter.project_name}}/.github/linters/lychee.toml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/linters/lychee.toml" rename to {{cookiecutter.project_name}}/.github/linters/lychee.toml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/ci.yml" b/{{cookiecutter.project_name}}/.github/workflows/ci.yml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/ci.yml" rename to {{cookiecutter.project_name}}/.github/workflows/ci.yml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/commit.yml" b/{{cookiecutter.project_name}}/.github/workflows/commit.yml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/commit.yml" rename to {{cookiecutter.project_name}}/.github/workflows/commit.yml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/release.yml" b/{{cookiecutter.project_name}}/.github/workflows/release.yml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/release.yml" rename to {{cookiecutter.project_name}}/.github/workflows/release.yml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/security.yml" b/{{cookiecutter.project_name}}/.github/workflows/security.yml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/security.yml" rename to {{cookiecutter.project_name}}/.github/workflows/security.yml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/update.yml" b/{{cookiecutter.project_name}}/.github/workflows/update.yml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/update.yml" rename to {{cookiecutter.project_name}}/.github/workflows/update.yml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/validate_pr_titles.yml" b/{{cookiecutter.project_name}}/.github/workflows/validate_pr_titles.yml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/validate_pr_titles.yml" rename to {{cookiecutter.project_name}}/.github/workflows/validate_pr_titles.yml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.gitignore" b/{{cookiecutter.project_name}}/.gitignore similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.gitignore" rename to {{cookiecutter.project_name}}/.gitignore diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.pre-commit-config.yaml" b/{{cookiecutter.project_name}}/.pre-commit-config.yaml similarity index 97% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.pre-commit-config.yaml" rename to {{cookiecutter.project_name}}/.pre-commit-config.yaml index 0b757a8..8b591c6 100644 --- "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.pre-commit-config.yaml" +++ b/{{cookiecutter.project_name}}/.pre-commit-config.yaml @@ -1,5 +1,5 @@ --- -exclude: '^{{ cookiecutter.project_name|replace(" ", "") }}/.*' +exclude: '^{{ cookiecutter.project_name }}/.*' repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: 3db93a2be6f214ed722bf7bce095ec1b1715422a # frozen: v0.14.2 diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/CLAUDE.md" b/{{cookiecutter.project_name}}/CLAUDE.md similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/CLAUDE.md" rename to {{cookiecutter.project_name}}/CLAUDE.md diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/CONTRIBUTING.md" b/{{cookiecutter.project_name}}/CONTRIBUTING.md similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/CONTRIBUTING.md" rename to {{cookiecutter.project_name}}/CONTRIBUTING.md diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/Dockerfile" b/{{cookiecutter.project_name}}/Dockerfile similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/Dockerfile" rename to {{cookiecutter.project_name}}/Dockerfile diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/FAQ.md" b/{{cookiecutter.project_name}}/FAQ.md similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/FAQ.md" rename to {{cookiecutter.project_name}}/FAQ.md diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/LICENSE" b/{{cookiecutter.project_name}}/LICENSE similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/LICENSE" rename to {{cookiecutter.project_name}}/LICENSE diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/README.md" b/{{cookiecutter.project_name}}/README.md similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/README.md" rename to {{cookiecutter.project_name}}/README.md diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/SECURITY.md" b/{{cookiecutter.project_name}}/SECURITY.md similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/SECURITY.md" rename to {{cookiecutter.project_name}}/SECURITY.md diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/Taskfile.yml" b/{{cookiecutter.project_name}}/Taskfile.yml similarity index 98% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/Taskfile.yml" rename to {{cookiecutter.project_name}}/Taskfile.yml index 2a9b823..c218022 100644 --- "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/Taskfile.yml" +++ b/{{cookiecutter.project_name}}/Taskfile.yml @@ -173,8 +173,8 @@ tasks: update: desc: Update the project dev and runtime dependencies cmds: - # This currently assumes uv was installed via uv (locally); we will want to make it more flexible in the future - - '{{ '{{if ne .GITHUB_ACTIONS "true"}}' }}brew upgrade uv{{ '{{end}}' }}' + # Upgrade uv via brew on macOS/Linux when running locally (not CI) + - '{{ '{{if and (ne .GITHUB_ACTIONS "true") (ne OS "windows")}}' }}brew upgrade uv{{ '{{end}}' }}' - uv tool upgrade --all - pre-commit autoupdate --freeze --jobs 4 - uv lock --upgrade diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/pyproject.toml" b/{{cookiecutter.project_name}}/pyproject.toml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/pyproject.toml" rename to {{cookiecutter.project_name}}/pyproject.toml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/renovate.json" b/{{cookiecutter.project_name}}/renovate.json similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/renovate.json" rename to {{cookiecutter.project_name}}/renovate.json diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/get_epoch.sh" b/{{cookiecutter.project_name}}/scripts/get_epoch.sh similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/get_epoch.sh" rename to {{cookiecutter.project_name}}/scripts/get_epoch.sh diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/get_os.sh" b/{{cookiecutter.project_name}}/scripts/get_os.sh similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/get_os.sh" rename to {{cookiecutter.project_name}}/scripts/get_os.sh diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/get_platform.sh" b/{{cookiecutter.project_name}}/scripts/get_platform.sh similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/get_platform.sh" rename to {{cookiecutter.project_name}}/scripts/get_platform.sh diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/get_rfc3339_timestamp.py" b/{{cookiecutter.project_name}}/scripts/get_rfc3339_timestamp.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/get_rfc3339_timestamp.py" rename to {{cookiecutter.project_name}}/scripts/get_rfc3339_timestamp.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/scan_image.py" b/{{cookiecutter.project_name}}/scripts/scan_image.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/scan_image.py" rename to {{cookiecutter.project_name}}/scripts/scan_image.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/scan_workflow_logs.sh" b/{{cookiecutter.project_name}}/scripts/scan_workflow_logs.sh similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/scan_workflow_logs.sh" rename to {{cookiecutter.project_name}}/scripts/scan_workflow_logs.sh diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/validate_service_definition.py" b/{{cookiecutter.project_name}}/scripts/validate_service_definition.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/validate_service_definition.py" rename to {{cookiecutter.project_name}}/scripts/validate_service_definition.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/service_definition.yaml" b/{{cookiecutter.project_name}}/service_definition.yaml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/service_definition.yaml" rename to {{cookiecutter.project_name}}/service_definition.yaml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/src/main.py" b/{{cookiecutter.project_name}}/src/main.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/src/main.py" rename to {{cookiecutter.project_name}}/src/main.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/src/{{cookiecutter.project_slug}}/__init__.py" b/{{cookiecutter.project_name}}/src/{{cookiecutter.project_slug}}/__init__.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/src/{{cookiecutter.project_slug}}/__init__.py" rename to {{cookiecutter.project_name}}/src/{{cookiecutter.project_slug}}/__init__.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/src/{{cookiecutter.project_slug}}/config.py" b/{{cookiecutter.project_name}}/src/{{cookiecutter.project_slug}}/config.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/src/{{cookiecutter.project_slug}}/config.py" rename to {{cookiecutter.project_name}}/src/{{cookiecutter.project_slug}}/config.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/src/{{cookiecutter.project_slug}}/constants.py" b/{{cookiecutter.project_name}}/src/{{cookiecutter.project_slug}}/constants.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/src/{{cookiecutter.project_slug}}/constants.py" rename to {{cookiecutter.project_name}}/src/{{cookiecutter.project_slug}}/constants.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/__init__.py" b/{{cookiecutter.project_name}}/tests/__init__.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/__init__.py" rename to {{cookiecutter.project_name}}/tests/__init__.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/test_config.py" b/{{cookiecutter.project_name}}/tests/test_config.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/test_config.py" rename to {{cookiecutter.project_name}}/tests/test_config.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/test_integration.py" b/{{cookiecutter.project_name}}/tests/test_integration.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/test_integration.py" rename to {{cookiecutter.project_name}}/tests/test_integration.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/test_main.py" b/{{cookiecutter.project_name}}/tests/test_main.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/test_main.py" rename to {{cookiecutter.project_name}}/tests/test_main.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/test_package.py" b/{{cookiecutter.project_name}}/tests/test_package.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/test_package.py" rename to {{cookiecutter.project_name}}/tests/test_package.py From ee9b7f522887cf452a99671a1eabb93ca52c0f7a Mon Sep 17 00:00:00 2001 From: Jon Zeolla Date: Sun, 5 Apr 2026 12:04:19 -0400 Subject: [PATCH 2/6] ci: use README command for Windows CI, disable linux jobs temporarily Use `gh:` syntax in Windows CI to match the exact README command. Pass github.head_ref through env var to satisfy actionlint. Disable lint/test jobs while iterating on Windows support. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/ci.yml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d59bd80..4efcc83 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,7 @@ defaults: jobs: lint: + if: false # Temporarily disabled while iterating on Windows support name: Lint runs-on: ubuntu-24.04 steps: @@ -38,6 +39,7 @@ jobs: - name: Lint run: task -v lint test: + if: false # Temporarily disabled while iterating on Windows support name: Test runs-on: ubuntu-24.04 permissions: @@ -106,12 +108,12 @@ jobs: name: Windows Smoke Test runs-on: windows-latest steps: - - name: Checkout the repository - uses: actions/checkout@v6 - name: Setup uv uses: astral-sh/setup-uv@v7 with: python-version: ${{ env.python_version }} + enable-cache: false + ignore-empty-workdir: true - name: Install Task uses: go-task/setup-task@v2 with: @@ -121,12 +123,17 @@ jobs: env: RUN_POST_HOOK: 'true' SKIP_GIT_PUSH: 'true' + TEMPLATE_REPO: ${{ github.repository }} + TEMPLATE_REF: ${{ github.head_ref || github.ref_name }} run: | git config --global user.name "CI Automation" git config --global user.email "ci@zenable.io" - uvx --with gitpython cookiecutter . --no-input \ - project_name="ci-test-project" \ + # Same command as README, plus --checkout for the PR branch and --no-input for CI + uvx --with gitpython cookiecutter \ + "gh:${TEMPLATE_REPO}" \ + --checkout "${TEMPLATE_REF}" \ + --no-input project_name="ci-test-project" \ --output-dir "$RUNNER_TEMP" - name: Verify generated project shell: pwsh From 6ce4ffad8a256f7c000b9ad2f726b3a4d110055b Mon Sep 17 00:00:00 2001 From: Jon Zeolla Date: Sun, 5 Apr 2026 12:11:08 -0400 Subject: [PATCH 3/6] ci: pre-populate cookiecutter cache to work around NTFS clone issue Cookiecutter's gh: syntax does git clone of the default branch first, which fails on Windows when main still has NTFS-illegal paths. Pre-clone with --no-checkout and checkout the PR branch to seed the cache. The actual cookiecutter command remains the same as the README. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/ci.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4efcc83..6dcb2bb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -129,6 +129,16 @@ jobs: git config --global user.name "CI Automation" git config --global user.email "ci@zenable.io" + # Pre-populate cookiecutter's cache so the gh: command doesn't need to + # clone main (which may still have NTFS-illegal paths before this PR merges). + # After merge, this is a no-op since the cache will already be valid. + repo_name=$(basename "${TEMPLATE_REPO}") + cache_dir="$HOME/.cookiecutters/${repo_name}" + if [[ ! -d "$cache_dir" ]]; then + git clone --no-checkout "https://github.com/${TEMPLATE_REPO}.git" "$cache_dir" + git -C "$cache_dir" checkout "${TEMPLATE_REF}" + fi + # Same command as README, plus --checkout for the PR branch and --no-input for CI uvx --with gitpython cookiecutter \ "gh:${TEMPLATE_REPO}" \ From 4095f0024cc1dbc6689c8ba271f7849643cb76c9 Mon Sep 17 00:00:00 2001 From: Jon Zeolla Date: Sun, 5 Apr 2026 12:15:12 -0400 Subject: [PATCH 4/6] ci: use core.protectNTFS=false to allow gh: clone on Windows Cookiecutter's gh: syntax clones the default branch first. When main still has NTFS-illegal paths, this fails on Windows. Setting core.protectNTFS=false lets git skip path validation so the clone can succeed, then cookiecutter checks out the PR branch which has valid paths. This setting becomes a no-op after the PR merges. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/ci.yml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6dcb2bb..ac23112 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -129,15 +129,10 @@ jobs: git config --global user.name "CI Automation" git config --global user.email "ci@zenable.io" - # Pre-populate cookiecutter's cache so the gh: command doesn't need to - # clone main (which may still have NTFS-illegal paths before this PR merges). - # After merge, this is a no-op since the cache will already be valid. - repo_name=$(basename "${TEMPLATE_REPO}") - cache_dir="$HOME/.cookiecutters/${repo_name}" - if [[ ! -d "$cache_dir" ]]; then - git clone --no-checkout "https://github.com/${TEMPLATE_REPO}.git" "$cache_dir" - git -C "$cache_dir" checkout "${TEMPLATE_REF}" - fi + # Allow git to clone repos with NTFS-illegal paths (e.g. pipes/quotes in + # the old template directory name on main). After this PR merges, the + # illegal paths are gone and this setting becomes a no-op. + git config --global core.protectNTFS false # Same command as README, plus --checkout for the PR branch and --no-input for CI uvx --with gitpython cookiecutter \ From 98d43ae76d196d8866f38c678509250533021b94 Mon Sep 17 00:00:00 2001 From: Jon Zeolla Date: Sun, 5 Apr 2026 12:18:34 -0400 Subject: [PATCH 5/6] ci: add local clone fallback for pre-merge Windows NTFS issue The gh: command tries to clone main first which fails on Windows when main still has NTFS-illegal paths. Add a fallback that manually clones with --no-checkout and checks out the PR branch. After merge, the gh: command succeeds directly and the fallback is never triggered. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/ci.yml | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ac23112..140e0ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -129,17 +129,24 @@ jobs: git config --global user.name "CI Automation" git config --global user.email "ci@zenable.io" - # Allow git to clone repos with NTFS-illegal paths (e.g. pipes/quotes in - # the old template directory name on main). After this PR merges, the - # illegal paths are gone and this setting becomes a no-op. - git config --global core.protectNTFS false - - # Same command as README, plus --checkout for the PR branch and --no-input for CI + # Use the same command as README.md (gh: syntax). On Windows, if the + # default branch still has NTFS-illegal paths (pre-merge), the gh: clone + # fails because cookiecutter clones main first. Fall back to a local + # clone in that case. After merge, the gh: command succeeds directly. uvx --with gitpython cookiecutter \ "gh:${TEMPLATE_REPO}" \ --checkout "${TEMPLATE_REF}" \ --no-input project_name="ci-test-project" \ - --output-dir "$RUNNER_TEMP" + --output-dir "$RUNNER_TEMP" \ + || { + echo "::warning::gh: clone failed (expected pre-merge on Windows). Using local clone." + template_dir="$RUNNER_TEMP/template-repo" + git clone --no-checkout "https://github.com/${TEMPLATE_REPO}.git" "$template_dir" + git -C "$template_dir" checkout "${TEMPLATE_REF}" + uvx --with gitpython cookiecutter "$template_dir" \ + --no-input project_name="ci-test-project" \ + --output-dir "$RUNNER_TEMP" + } - name: Verify generated project shell: pwsh run: | From 8a1e817e89e2f89e0ce3a8b87d90b31906ef0f0f Mon Sep 17 00:00:00 2001 From: Jon Zeolla Date: Sun, 5 Apr 2026 12:25:08 -0400 Subject: [PATCH 6/6] ci: re-enable lint and test jobs Windows smoke test passes with the gh: command (using local clone fallback pre-merge). Re-enable all Linux CI jobs. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 140e0ce..0e30011 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,6 @@ defaults: jobs: lint: - if: false # Temporarily disabled while iterating on Windows support name: Lint runs-on: ubuntu-24.04 steps: @@ -39,7 +38,6 @@ jobs: - name: Lint run: task -v lint test: - if: false # Temporarily disabled while iterating on Windows support name: Test runs-on: ubuntu-24.04 permissions: