Conversation
Replace the Python-based zenable-mcp package (installed via uvx) with the compiled Zenable CLI binary from cli.zenable.app. The CLI is now auto-installed during cookiecutter generation using the official install script, with curl/wget fallback and non-interactive mode. Also fix trufflehog pre-commit hook to work in git worktrees by resolving the repo root via git-common-dir. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
I've got 2 comments for you to consider
Reviewed with 🤟 by Zenable
Guard against detached HEAD by falling back to commit SHA. Use HEAD~1 instead of main~1 to avoid dependency on main branch existing locally. Install trufflehog binary in CI bootstrap since language: system requires it in PATH. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add docstring note that cli.zenable.app/install.sh performs cosign signature verification and checksum validation of the downloaded binary. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add conftest, docstrings, and taskfile to both the root and template dictionaries to fix cspell lint failures in CI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a windows-latest job that generates a project from the template and verifies it renders correctly with all defaults. The bootstrap action is updated to handle Windows by skipping Unix-specific steps (Homebrew, trufflehog, sha256sum) and using PowerShell alternatives. The smoke test verifies: - Project directory is created - Key files exist (pyproject.toml, Taskfile.yml, Dockerfile, etc.) - No unrendered cookiecutter variables remain - Git repo is initialized with at least one commit Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The template directory name contains pipe and quote characters
({{cookiecutter.project_name|replace(" ", "")}}) which are invalid
on NTFS. Set core.protectNTFS=false before checkout to allow these
paths in the Windows smoke test.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
actions/checkout overrides HOME with a temp directory, so global git config set in a prior step is lost. Use system-level config instead which persists across HOME changes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
actions/checkout overrides both HOME and system config. Use the GIT_CONFIG_GLOBAL environment variable to point at a custom gitconfig file that disables core.protectNTFS, which persists through all of checkout's internal git operations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Actionlint flags github.head_ref as potentially untrusted in inline scripts. Pass it through an environment variable instead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cookiecutter internally runs git clone, which also fails on NTFS due to the template directory name containing quote characters. Use GIT_CONFIG_COUNT/KEY/VALUE environment variables to inject core.protectNTFS=false into all git invocations without needing a config file or HOME directory. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Double-quote characters in the template directory name are fundamentally illegal on NTFS — no git config can work around this. Use cookiecutter's zip URL support instead, which extracts via Python's zipfile module and avoids NTFS filesystem restrictions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cookiecutter's zip URL support still fails because Python's zipfile extraction on Windows cannot create paths with double-quote characters. Download the zip manually, extract with Python (which can handle the entries internally), then point cookiecutter at the extracted directory. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
The \\?\ extended-length path prefix requires backslashes and no trailing slashes. Zip entries use forward slashes which must be converted to OS-native separators before joining with the prefix. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Double quotes are fundamentally illegal in NTFS paths — even the \\?\
extended-length prefix cannot work around this. Replace " with ' during
zip extraction, which is safe because Jinja2 treats both quote types
identically: replace(" ", "") and replace(' ', '') produce the same
output.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use Git Bash (available on all Windows runners) for the zip download and extraction. Git Bash's unzip may handle NTFS-illegal characters differently than Python's os.makedirs through MSYS2's path translation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tter
The template directory contains | and " which are illegal on NTFS.
Extract the zip with unzip (which handles these in MSYS2), then rename
the directory to {{cookiecutter.project_name}} which is NTFS-safe and
produces identical output for the default project name (no spaces).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
unzip silently skips entries with NTFS-illegal chars. Use Python's
zipfile to extract all entries, renaming any directory containing
{{cookiecutter. to {{cookiecutter.project_name}} which is NTFS-safe
and renders identically for the default project name.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fetch release metadata from cli.zenable.app/zenable/latest and verify the install.sh SHA-256 checksum before piping it to bash. Uses Python stdlib (urllib.request, hashlib) instead of curl/wget for downloading. The install script itself also performs cosign signature verification of the binary it downloads, providing defense in depth. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
JonZeolla
commented
Apr 4, 2026
JonZeolla
commented
Apr 4, 2026
Introduce _https_urlopen() that validates the URL uses HTTPS before calling urlopen, centralizing the SSRF mitigation. The noqa: S310 is now confined to a single validated call site instead of scattered across multiple callers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use sys.platform to select the appropriate installer (install.ps1 on Windows, install.sh elsewhere) and binary name (zenable.exe on Windows). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The installer updates PATH for future shells (via GITHUB_PATH in CI or shell profile locally) but not the current Python process. After a successful install, prepend ~/.zenable/bin to the process PATH so _find_zenable_binary() can locate the binary immediately. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace urlopen (which accepts arbitrary URL schemes) with an opener built from HTTPSHandler only. This makes HTTPS-only enforcement structural rather than relying on runtime validation + linter suppression. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add end-to-end verification steps to the Windows CI job: initialize the generated project, run tests, build the Docker image, verify the image runs, and confirm the zenable CLI is functional. Also add diagnostic logging to the post-gen hook when the zenable binary cannot be found after installation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Task eagerly evaluates top-level vars including VERSION which runs `uv run python -c` to import the project. Bootstrap the venv with `uv sync` and set PYTHONPATH so the namespace package import works before any task commands. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the namespace-package import (`from src.X import __version__`) with a helper script that reads __init__.py directly. The script resolves the file path relative to its own location, avoiding CWD and import-mechanism issues on Windows. Includes ZENABLE_LOGLEVEL=DEBUG support for diagnostics. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Task resolves vars in definition order, so VERSION must be defined after the variables it references (RUN_SCRIPT, SCRIPTS_DIR). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove get_version.py in favor of inline sys.path.insert approach (which should now work since var ordering is fixed). Disable lint/test jobs temporarily to iterate faster on Windows support. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Print CWD and src directory existence to stderr to diagnose why the import fails on Windows. Use os.path.join for absolute path. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The zip extraction was replacing ALL {{cookiecutter.*}} directory names
with {{cookiecutter.project_name}}, including nested ones like
{{cookiecutter.project_slug}}. This caused src/replace_me/ to become
src/replace-me/ on Windows, breaking Python imports. Only rename the
top-level template dir (which has NTFS-illegal pipe and quote chars).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add docker/setup-buildx-action to the Windows CI job so docker buildx is available. Also make init-docker-multiplatform skip gracefully if buildx is not available rather than failing the init task. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
BuildKit has no Windows image, so Linux Docker builds cannot work on Windows runners. Remove Docker buildx setup, build, and image verification steps. Docker builds are already tested by the Linux CI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Integration tests build and run Docker images which require Linux containers, unavailable on Windows runners. Run only unit tests on Windows; integration tests are covered by the Linux CI job. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PowerShell's -Command - does not reliably read scripts from stdin, causing the zenable CLI installer to exit 0 without actually installing. Write the install.ps1 to a temp file and use -File instead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Windows smoke test is passing. Re-enable lint and test jobs, remove ZENABLE_LOGLEVEL=DEBUG and diagnostic ls output. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Install Ubuntu 24.04 in WSL, set up Docker Engine inside it, and use it to build and verify the Linux Docker image on the Windows runner. No third-party actions required — uses native wsl commands. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Git Bash strips backslashes when passing paths to wsl commands. Use PowerShell to convert RUNNER_TEMP to WSL /mnt/ path format. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Revert the graceful buildx skip — missing buildx is a real failure. Move WSL+Docker setup before task init and create a docker wrapper script that routes all docker commands through WSL, so task init and task build work transparently. Also use task build instead of manual docker commands. Fix get_epoch.sh to use bash directly (uv run can't execute .sh files on Windows). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Task's Go-based shell (mvdan/sh) uses os/exec.LookPath which only finds files with Windows-recognized extensions (.exe, .bat, .cmd). Replace the bash wrapper with a .bat file that routes docker commands through WSL. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The .bat wrapper is found by Task's mvdan/sh but not by Git Bash. Add a bash script wrapper too so both Task and direct bash steps route docker commands through WSL. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…_zip.py Move the inline Python zip extraction into a proper script file that can be tested and maintained independently. The CI downloads it from the raw GitHub URL since the repo cannot be checked out on Windows. Also add python3 --version check to Docker image verification. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add argparse to main.py so the generated project supports --version (from __init__.__version__) and --help out of the box. Update Docker verify to test both flags directly via the container entrypoint. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mock sys.argv in test_main_function to avoid pytest arg conflicts. Add test_main_version and test_main_as_script_version to verify --version works both in-process and as a subprocess. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Import __version__ from the package instead of hardcoding "0.0.0" so the test doesn't break after a release increments the version. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
zenable-mcpPython package with compiled CLI binary fromcli.zenable.app, auto-installed during cookiecutter project generation with checksum verification{{cookiecutter.project_slug}}dirs too)bashfor LOCAL_PLATFORM shell script (Windows needs explicit bash)init-docker-multiplatformskip gracefully when buildx unavailableTest plan
zenable versionconfirms CLI is installed and functional🤖 Generated with Claude Code