How ensure_executable_scripts Handles Cross-Platform Script Permissions in Spec-Kit
The ensure_executable_scripts function returns immediately without modifying files when os.name == "nt" (Windows), while on POSIX systems it recursively scans .specify/scripts for shell files, validates shebangs, and sets execute bits based on existing read permissions.
The ensure_executable_scripts utility ensures that freshly scaffolded Spec-Kit projects contain runnable scripts regardless of the host operating system. Located in the core CLI module at src/specify_cli/__init__.py (lines 966-1005), this function bridges the gap between Windows and Unix-like environments by conditionally applying POSIX permission bits only where they are meaningful.
Platform Detection and Windows Handling
The function begins with an explicit platform guard that prevents any file system modifications on Windows.
According to the implementation, the code checks os.name immediately upon entry. When the value equals "nt", the function returns instantly without scanning directories or touching files. This design recognizes that Windows does not utilize POSIX execute bits, making the function a no-op on that platform while avoiding unsupported chmod system calls.
POSIX Permission Logic
On macOS and Linux systems, ensure_executable_scripts applies a sophisticated permission inheritance strategy to .sh files within the project_path/.specify/scripts directory.
Shebang Verification
The function opens each candidate file in binary mode and inspects the first two bytes. Only files beginning with b"#!" (a valid shebang) proceed to permission modification. This check prevents the function from altering text files that merely carry a .sh extension, ensuring only genuine scripts are targeted.
Read-to-Execute Mapping
The core permission algorithm examines the current st_mode and applies the following bitwise logic:
- If the owner read bit (
0o400) is set, the owner execute bit (0o100) is added - If the group read bit (
0o040) is set, the group execute bit (0o010) is added - If the other read bit (
0o004) is set, the other execute bit (0o001) is added
If no execute bits are present after this mapping, the owner-execute bit (0o100) is forced as a fallback guarantee. This mirrors the POSIX convention where readable scripts should be executable, while preserving existing permission structures. Finally, os.chmod(script, new_mode) commits the changes atomically.
Integration with StepTracker
The function signature accepts an optional StepTracker instance for programmatic integration during specify init operations. When provided, the function records update counts via tracker.complete or failure states via tracker.error. Without a tracker, it prints a concise console summary indicating how many scripts were modified.
Usage Examples
Initializing a New Project
from pathlib import Path
from specify_cli import ensure_executable_scripts, StepTracker
project_root = Path.cwd() / "my-spec-project"
tracker = StepTracker()
ensure_executable_scripts(project_root, tracker=tracker)
On macOS or Linux, this scans my-spec-project/.specify/scripts/**/*.sh, validates shebangs, and updates permissions. The tracker logs entries such as "3 updated, 0 failed" under the "chmod" operation key.
Windows No-Op Behavior
import os
from pathlib import Path
from specify_cli import ensure_executable_scripts
print(os.name) # → "nt"
ensure_executable_scripts(Path("C:/proj"))
This returns instantly without issuing chmod calls or raising errors, ensuring Windows compatibility.
Handling Write-Only Scripts
# File exists with mode 0o200 (write-only, no read bits)
ensure_executable_scripts(Path("."))
The function forces the owner-execute bit (0o100) when no read bits are present, guaranteeing the script becomes executable even without prior read permissions.
Summary
- Windows Compatibility: The function detects
os.name == "nt"and returns immediately, avoiding unsupportedchmodoperations on Windows file systems. - Shebang Validation: Only files starting with
b"#!"qualify for permission updates, ensuring genuine shell scripts are targeted. - Permission Inheritance: Execute bits mirror existing read bits (
0o400→0o100, etc.), with a mandatory fallback to owner-execute if none are set. - Source Location: Core implementation lives in
src/specify_cli/__init__.pylines 966-1005, supporting both CLI initialization flows and direct API usage.
Frequently Asked Questions
Does ensure_executable_scripts work on Windows?
No, the function is designed as a no-op on Windows. It checks os.name at the start and returns immediately when the value is "nt" because Windows does not use POSIX execute permission bits. This prevents errors while maintaining cross-platform compatibility in the Spec-Kit CLI.
How does the function decide which files to modify?
The function recursively searches for *.sh files in project_path/.specify/scripts, then opens each candidate in binary mode to verify the first two bytes match b"#!". This shebang check ensures only actual shell scripts receive permission updates, ignoring symlinks and non-regular files regardless of their extension.
What permission bits does ensure_executable_scripts set?
The function maps read bits to execute bits: if owner read (0o400) is set, owner execute (0o100) is added; group read (0o040) triggers group execute (0o010); other read (0o004) triggers other execute (0o001). If no execute bits are assigned after this mapping, the owner-execute bit (0o100) is forced to ensure the script is runnable.
Can I use ensure_executable_scripts outside of spec init?
Yes, the function is importable from specify_cli and accepts a pathlib.Path along with an optional StepTracker for custom tooling. When called manually, it provides the same console output or programmatic tracking as during automated project initialization.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →