From b5fb49965191bada0647322faf3bf7a62ec72624 Mon Sep 17 00:00:00 2001 From: Jon Zeolla Date: Wed, 27 Aug 2025 11:10:55 -0400 Subject: [PATCH 1/4] feat(mcp): add zenable mcp configs on init --- hooks/post_gen_project.py | 93 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index d173c3a..2758fd2 100755 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -6,6 +6,7 @@ import datetime import json import os +import platform import pprint import shutil import subprocess @@ -168,6 +169,94 @@ def notify_dockerhub_secrets() -> None: print("=" * 70 + "\n") +def ensure_uv_installed() -> None: + """Opportunistically ensure uv is installed on the system.""" + try: + # Check if uvx is already available + if shutil.which("uvx"): + LOG.info("uvx is already available in PATH") + return + + # Check if uv is installed but uvx might not be in PATH + if shutil.which("uv"): + LOG.info("uv is available but uvx might not be in PATH") + return + + print("\n" + "=" * 70) + print("Installing uv package manager...") + print("=" * 70) + + system = platform.system() + + if system in ["Linux", "Darwin"]: # Unix-like systems (Linux and macOS) + # Use the standalone installer for Unix-like systems + install_cmd = "curl -LsSf https://astral.sh/uv/install.sh | sh" + subprocess.run(install_cmd, shell=True, check=True, capture_output=True, timeout=30) + + # Add to PATH for current session + home = Path.home() + uv_bin = home / ".local" / "bin" + if uv_bin.exists(): + os.environ["PATH"] = f"{uv_bin}:{os.environ.get('PATH', '')}" + + elif system == "Windows": + # Use PowerShell for Windows + install_cmd = [ + "powershell", + "-ExecutionPolicy", + "ByPass", + "-c", + "irm https://astral.sh/uv/install.ps1 | iex", + ] + subprocess.run(install_cmd, check=True, capture_output=True, timeout=30) + + # Add to PATH for current session on Windows + home = Path.home() + uv_bin = home / ".local" / "bin" + if uv_bin.exists(): + os.environ["PATH"] = f"{uv_bin};{os.environ.get('PATH', '')}" + else: + LOG.info(f"Unsupported platform for automatic uv installation: {system}") + return + + print("uv has been successfully installed") + print("=" * 70 + "\n") + + except Exception as e: + # Log the error but don't fail - this is opportunistic + LOG.info(f"Could not install uv automatically (this is optional): {e}") + # Don't print anything to the user - this is an optional step + + +def install_zenable_mcp() -> None: + """Opportunistically install zenable-mcp using uvx.""" + try: + # Try to use uvx first + if shutil.which("uvx"): + print("\n" + "=" * 70) + print("Installing zenable-mcp...") + print("=" * 70) + subprocess.run(["uvx", "zenable-mcp@latest", "install"], check=True, timeout=60) + print("zenable-mcp has been successfully installed") + print("=" * 70 + "\n") + # Fallback to uv run if uvx is not available but uv is + elif shutil.which("uv"): + print("\n" + "=" * 70) + print("Installing zenable-mcp...") + print("=" * 70) + subprocess.run(["uv", "run", "--with", "zenable-mcp", "zenable-mcp", "install"], check=True, timeout=60) + print("zenable-mcp has been successfully installed") + print("=" * 70 + "\n") + else: + # No uv/uvx available, silently skip + LOG.info("Neither uvx nor uv found in PATH. Skipping zenable-mcp installation.") + + except Exception as e: + # Log the error but don't fail - this is opportunistic + LOG.info(f"Could not install zenable-mcp automatically (this is optional): {e}") + # Don't print error messages to the user - this is an optional step + + def run_post_gen_hook(): """Run post generation hook""" try: @@ -185,6 +274,10 @@ def run_post_gen_hook(): subprocess.run(["git", "init", "--initial-branch=main"], capture_output=True, check=True) + # Ensure uv is installed and install zenable-mcp + ensure_uv_installed() + install_zenable_mcp() + # This is important for testing project generation for CI if ( os.environ.get("GITHUB_ACTIONS") == "true" From 2c352afcf015caaeaa13dba9c7a82f9cec763fa4 Mon Sep 17 00:00:00 2001 From: JonZeolla Date: Wed, 3 Sep 2025 08:09:05 -0400 Subject: [PATCH 2/4] Update hooks/post_gen_project.py --- hooks/post_gen_project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index 2758fd2..8e9a1dc 100755 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -236,7 +236,7 @@ def install_zenable_mcp() -> None: print("\n" + "=" * 70) print("Installing zenable-mcp...") print("=" * 70) - subprocess.run(["uvx", "zenable-mcp@latest", "install"], check=True, timeout=60) + subprocess.run(["uvx", "zenable-mcp@latest", "install", "--all"], check=True, timeout=60) print("zenable-mcp has been successfully installed") print("=" * 70 + "\n") # Fallback to uv run if uvx is not available but uv is From aa44f460437dc40bb9cf5a1e5c495e49c0789a25 Mon Sep 17 00:00:00 2001 From: Jon Zeolla Date: Wed, 17 Sep 2025 15:27:11 -0400 Subject: [PATCH 3/4] This should work --- hooks/post_gen_project.py | 116 +++++++++++--------------------------- 1 file changed, 33 insertions(+), 83 deletions(-) diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index 8e9a1dc..72579aa 100755 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -6,7 +6,6 @@ import datetime import json import os -import platform import pprint import shutil import subprocess @@ -153,6 +152,7 @@ def notify_envrc() -> None: def notify_dockerhub_secrets() -> None: """Notify user about required Docker Hub secrets for releases.""" + # We no longer need this once https://github.com/docker/roadmap/issues/314 is available print("\n" + "=" * 70) print("IMPORTANT: Docker Hub Publishing Enabled") print("=" * 70) @@ -169,92 +169,44 @@ def notify_dockerhub_secrets() -> None: print("=" * 70 + "\n") -def ensure_uv_installed() -> None: - """Opportunistically ensure uv is installed on the system.""" - try: - # Check if uvx is already available - if shutil.which("uvx"): - LOG.info("uvx is already available in PATH") - return - - # Check if uv is installed but uvx might not be in PATH - if shutil.which("uv"): - LOG.info("uv is available but uvx might not be in PATH") - return - +def opportunistically_install_zenable_tools() -> None: + """Opportunistically install zenable-mcp if uvx is available.""" + # Check if uvx is not available + if not shutil.which("uvx"): + # uvx is not available, notify the user print("\n" + "=" * 70) - print("Installing uv package manager...") + print("NOTE: Skipped configuring the Zenable AI coding guardrails") print("=" * 70) - - system = platform.system() - - if system in ["Linux", "Darwin"]: # Unix-like systems (Linux and macOS) - # Use the standalone installer for Unix-like systems - install_cmd = "curl -LsSf https://astral.sh/uv/install.sh | sh" - subprocess.run(install_cmd, shell=True, check=True, capture_output=True, timeout=30) - - # Add to PATH for current session - home = Path.home() - uv_bin = home / ".local" / "bin" - if uv_bin.exists(): - os.environ["PATH"] = f"{uv_bin}:{os.environ.get('PATH', '')}" - - elif system == "Windows": - # Use PowerShell for Windows - install_cmd = [ - "powershell", - "-ExecutionPolicy", - "ByPass", - "-c", - "irm https://astral.sh/uv/install.ps1 | iex", - ] - subprocess.run(install_cmd, check=True, capture_output=True, timeout=30) - - # Add to PATH for current session on Windows - home = Path.home() - uv_bin = home / ".local" / "bin" - if uv_bin.exists(): - os.environ["PATH"] = f"{uv_bin};{os.environ.get('PATH', '')}" - else: - LOG.info(f"Unsupported platform for automatic uv installation: {system}") - return - - print("uv has been successfully installed") + print("\nConfiguring the Zenable AI coding guardrails requires the uv package manager.") + print("To set this up later:") + print("\n1. Install uv via https://docs.astral.sh/uv/getting-started/installation/") + print("2. Run: uvx zenable-mcp@latest install") print("=" * 70 + "\n") - except Exception as e: - # Log the error but don't fail - this is opportunistic - LOG.info(f"Could not install uv automatically (this is optional): {e}") - # Don't print anything to the user - this is an optional step - + LOG.warning("uvx was not found in PATH, so the Zenable integrations were not installed.") + return -def install_zenable_mcp() -> None: - """Opportunistically install zenable-mcp using uvx.""" + # uvx is available, attempt to install zenable-mcp + LOG.debug("uvx is available in PATH, attempting to install the Zenable tools...") try: - # Try to use uvx first - if shutil.which("uvx"): - print("\n" + "=" * 70) - print("Installing zenable-mcp...") - print("=" * 70) - subprocess.run(["uvx", "zenable-mcp@latest", "install", "--all"], check=True, timeout=60) - print("zenable-mcp has been successfully installed") - print("=" * 70 + "\n") - # Fallback to uv run if uvx is not available but uv is - elif shutil.which("uv"): - print("\n" + "=" * 70) - print("Installing zenable-mcp...") - print("=" * 70) - subprocess.run(["uv", "run", "--with", "zenable-mcp", "zenable-mcp", "install"], check=True, timeout=60) - print("zenable-mcp has been successfully installed") - print("=" * 70 + "\n") - else: - # No uv/uvx available, silently skip - LOG.info("Neither uvx nor uv found in PATH. Skipping zenable-mcp installation.") - - except Exception as e: + subprocess.run(["uvx", "zenable-mcp@latest", "install"], check=True, timeout=60) + print("\n" + "=" * 70) + print("Successfully configured the Zenable AI coding guardrails 🚀") + print("To start using it, just open the IDE of your choice, login to the MCP server, and you're all set 🤖") + print("Learn more at https://docs.zenable.io") + print("=" * 70 + "\n") + except Exception: # Log the error but don't fail - this is opportunistic - LOG.info(f"Could not install zenable-mcp automatically (this is optional): {e}") - # Don't print error messages to the user - this is an optional step + LOG.warning("Failed to configure the Zenable AI coding guardrails") + print("\n" + "=" * 70) + print("WARNING: Failed to configure the Zenable AI coding guardrails") + print("=" * 70) + print("You can retry it later by running:") + print("\n uvx zenable-mcp@latest install") + print("\nTo report issues, please contact:") + print(" • https://zenable.io/feedback") + print(" • support@zenable.io") + print("=" * 70 + "\n") def run_post_gen_hook(): @@ -274,9 +226,7 @@ def run_post_gen_hook(): subprocess.run(["git", "init", "--initial-branch=main"], capture_output=True, check=True) - # Ensure uv is installed and install zenable-mcp - ensure_uv_installed() - install_zenable_mcp() + opportunistically_install_zenable_tools() # This is important for testing project generation for CI if ( From 88b2a1cc1ce3712dfce050b7f58c90da0c2924a9 Mon Sep 17 00:00:00 2001 From: Jon Zeolla Date: Wed, 17 Sep 2025 18:29:13 -0400 Subject: [PATCH 4/4] Update config --- .github/.grant.yml | 61 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/.github/.grant.yml b/.github/.grant.yml index 44821c5..773eb90 100644 --- a/.github/.grant.yml +++ b/.github/.grant.yml @@ -1,13 +1,48 @@ -rules: - - pattern: "*" - name: "Block AGPL licenses" - mode: "block" - reason: "AGPL licenses are not allowed in this project" - licenses: - - "agpl" - - "agpl-1.0" - - "agpl-1.0-only" - - "agpl-1.0-or-later" - - "agpl-3.0" - - "agpl-3.0-only" - - "agpl-3.0-or-later" +require-license: false # Some packages may not have explicit licenses +require-known-license: false + +# Allow list - licenses that are permitted +allow: + # Permissive licenses + - MIT + - Apache-2.0 + - BSD-2-Clause + - BSD-3-Clause + - BSD + - BSD-License + - ISC + - ISC-License + - 0BSD + + # Python-specific licenses + - PSF-2.0 + - Python-2.0 + - Dual-License + + # Weak copyleft licenses (generally acceptable) + - LGPL + - LGPL-2.1 + - LGPL-2.1-or-later + - LGPL-3.0 + - LGPL-3.0-or-later + - MPL-2.0 + + # Other licenses + - Unlicense + - CC0-1.0 + - WTFPL + - Artistic-License + - GPL-3.0-only + +# Block list - licenses that are not allowed +block: + - AGPL-1.0 + - AGPL-1.0-only + - AGPL-1.0-or-later + - AGPL-3.0 + - AGPL-3.0-only + - AGPL-3.0-or-later + +# Ignore specific packages if needed +ignore-packages: + - "UnknownPackage:*"