OpenChmod
Recursively fix file permissions across a project tree in one command.
pychmod walks a directory and applies sensible defaults — 0755 for directories, 0644 for regular files, 0755 for scripts and executables — so you don't have to chain find ... -exec chmod ... invocations or remember which mode goes where. Scripts are detected by extension or by #! shebang, even when there is no extension at all.
Features
- One pass, three modes: separate permissions for directories, files, and executables.
- Smart script detection: known extensions (
.sh,.py,.pl,.rb, ...) plus shebang sniffing for extensionless files. --dry-runto preview changes and--checkto fail CI when permissions drift.- Glob-based
--include/--excludefilters, repeatable. - Optional symlink following, off by default.
- Zero runtime dependencies. Pure Python, 3.10+.
Why not just find -exec chmod?
The classic recipe needs three passes and assumes you know which files are scripts:
find . -type d -exec chmod 0755 {} +
find . -type f -exec chmod 0644 {} +
find . -type f \( -name "*.sh" -o -name "*.py" \) -exec chmod 0755 {} +
pychmod does the same in one walk, classifies scripts by shebang as well as extension (so an extensionless ./build with #!/usr/bin/env bash gets the right mode), and adds --dry-run plus --check so you can preview changes or fail CI on drift — none of which find gives you for free.
Installation
pip install pychmod
Quick start
Apply defaults (dirs=0755, files=0644, exec=0755) to a project tree:
pychmod /path/to/project
Preview without writing:
pychmod /path/to/project --dry-run
Verify in CI — exits non-zero if anything drifts:
pychmod /path/to/project --check
Usage
pychmod [-h] [-p DIR] [--dirperms DIRPERMS] [-f FILEPERMS] [-x EXECPERMS]
[-e PATTERN] [-i PATTERN] [--files-only] [-s] [-v] [-n] [--check]
[DIR]
| Option | Description | Default |
|---|---|---|
DIR / -p DIR |
Directory to traverse (positional or flag). | — |
--dirperms |
Octal mode for directories. | 0755 |
-f, --fileperms |
Octal mode for regular files. | 0644 |
-x, --execperms |
Octal mode for scripts/executables. | 0755 |
-e, --exclude |
Glob pattern to exclude (repeatable). | — |
-i, --include |
Glob pattern to include, files only (repeatable). | — |
--files-only |
Skip directory permissions; only chmod files. | false |
-s, --symlinks |
Follow symlinks. | false |
-v, --verbose |
Print every entry processed. | false |
-n, --dry-run |
Show changes without applying them. | false |
--check |
Exit non-zero if any permissions are wrong. No changes made. | false |
Examples
Tighten down a webroot:
pychmod /var/www --dirperms 0750 --fileperms 0640 --execperms 0750
Skip VCS and cache directories:
pychmod /path/to/project -e .git -e __pycache__ --verbose
Only retouch Python and shell scripts:
pychmod /path/to/project -i "*.py" -i "*.sh"
Only fix C/C++ source files, leave directories untouched:
pychmod /path/to/project -i "*.c" -i "*.h" -i "*.cpp" --files-only
Follow symlinks (use with care):
pychmod /path/to/project --symlinks
Use as a pre-commit / CI gate
Add a --check step to your pipeline so a stray chmod 777 never lands on main:
- name: Verify file permissions
run: pychmod . --check -e .git
Development
git clone https://github.com/Junbo-Zheng/OpenChmod.git
cd OpenChmod
pip install -e ".[dev]"
pytest
Release files for pychmod 0.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pychmod-0.0.2.tar.gz | 11.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pychmod-0.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.3 kB
Release files / pychmod-0.0.2.tar.gz
| Download URL | pychmod-0.0.2.tar.gz |
|---|---|
| Size | 11.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4d941d6e0ad8935b6064063e72114efa23f9ddd9b1aec7061fe911ebb69d3de5
|
|
BLAKE2b-256 checksum How to use checksums |
9b4dab174e5fd4fc6f8e4722b078d613d8f92431d7121ae191541d56f370ba71
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.12
|
Release files / pychmod-0.0.2-py3-none-any.whl
| Download URL | pychmod-0.0.2-py3-none-any.whl |
|---|---|
| Size | 10.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3d4752305c600a8dff13428fcbaf0ef07d0c870407bacdb92bef3f543444f685
|
|
BLAKE2b-256 checksum How to use checksums |
0be6059ac8c819c3ce2bb48f7f371dfdb30ac23ee903b85e72c971abb04dd772
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.12
|