Start here: visual guide EN/RU · Русский: первый запуск
Download start.html and open it in your browser for installation, a failing demo,
a passing comparison and next steps. Examples are synthetic and run locally.
Check whether your Git filenames travel well — before checkout.
A filename accepted on Linux can prevent a Windows contributor from checking out your project. Treeport reads a commit's path list directly from Git, reports naming hazards, and produces text, JSON or a self-contained HTML report. It also checks the entire staged index for use in pre-commit workflows.
This is an initial release with a deliberately small scope. It runs locally, has no runtime Python dependencies, makes no network requests, and needs no AI API. Reports contain repository path names; review them before sharing.
Release v0.1.2 includes a wheel, source
archives, checksums and a verification record. With Python 3.11+, install the
downloaded wheel using python -m pip install treeport-0.1.2-py3-none-any.whl.
Runtime dependencies listed below are resolved by pip when needed.
For source development, clone this repository and follow the existing install steps.
Requires Python 3.11+ and Git on PATH. Git is not needed for JSON path-list input.
Windows, Linux and macOS are CI targets. See docs/VALIDATION.md for actually run checks.
From an extracted source release, in the directory containing pyproject.toml:
python -m pip install .
treeport --versionFrom the supplied wheel (offline, without dependency downloads):
python -m pip install --no-index --no-deps treeport-0.1.2-py3-none-any.whlThe name is provisional and this package has not been published to PyPI.
python -m treeport can replace treeport in every command below.
From the source release directory after installation:
treeport check --paths examples/problem-paths.json
treeport check --paths examples/problem-paths.json --format html --output report.html
treeport check --paths examples/clean-paths.jsonThe first two commands intentionally exit 1 because the sample contains errors.
The last exits 0. Open report.html in a browser. The sample names are synthetic;
they do not represent users or project adoption.
treeport check /path/to/repository
treeport check /path/to/repository --ref main
treeport check /path/to/repository --index
treeport check /path/to/repository --format json --output report.jsonThe default is the HEAD tree, not the files currently on disk. Use --ref for
a branch, tag, commit or tree object and --index for the full staged snapshot.
Untracked files and unstaged changes are not included. An unresolved index fails
with exit 2. An unborn repository can be scanned with --index.
--ref also works in bare repositories and after a clone with --no-checkout:
git clone --bare https://github.com/OWNER/REPOSITORY.git repository.git
treeport check repository.git --ref HEADReplace the example URL with your repository. Cloning is a separate Git operation; Treeport itself never fetches, checks out, renames files or invokes project code. It reads path metadata, not blob contents. Submodule paths are checked, but their contents are not traversed. Symlink names are checked, but targets are not followed.
| Exit | Meaning |
|---|---|
| 0 | No new findings at or above the selected failure threshold |
| 1 | At least one new finding reaches that threshold |
| 2 | Input, Git, arguments, baseline or output error; the scan is not trustworthy |
Warnings fail by default. To fail only on errors, use --fail-on error. This does
not remove warnings from reports. Git commands time out after 30 seconds.
treeport check --max-path 200 --exclude "vendor/*" --exclude "fixtures/intentional/*"--max-path is a repository-relative UTF-16 code-unit budget, default 240;
0 disables it. It is not the Win32 absolute-path limit: your clone directory and
toolchain also matter. Excludes are case-sensitive Python fnmatchcase patterns
applied to complete leaf paths, and * crosses /. They are not .gitignore
patterns. Quote them so your shell does not expand them. Exclusion can hide real
problems; use it only for reviewed, intentionally nonportable content.
Review the initial findings, then explicitly record them:
treeport check --write-baseline treeport-baseline.json
treeport check --baseline treeport-baseline.jsonThe first command writes the baseline and keeps the normal failure status.
Commit the reviewed JSON file (this source template ignores its default name;
use git add -f treeport-baseline.json if needed). Subsequent reports show all
findings, including acknowledged ones. Only unchanged finding fingerprints are
acknowledged. Adding a member to a collision group creates a new finding.
Rule identifiers and affected paths define fingerprints. A changed length policy
or exclude list rejects the baseline; stale entries are counted for cleanup.
Baselines are not proof that a repository is portable, and should be reviewed as code.
Install a reviewed release wheel or pin the source repository to a commit.
The following job assumes Treeport's source is vendored at tools/treeport:
name: Filename portability
on: [push, pull_request]
permissions:
contents: read
jobs:
paths:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: python -m pip install ./tools/treeport
- run: treeport check --ref HEADRun on Linux so a Windows-invalid name cannot stop checkout before the checker runs. The included project CI tests Python 3.11/3.14 on Linux, Windows and macOS, plus builds and installs the wheel. These workflows are prepared; remote CI has not yet run.
The source includes .pre-commit-hooks.yaml. Before publishing it, use this local
configuration with Treeport already installed in the environment running pre-commit:
repos:
- repo: local
hooks:
- id: treeport
name: Check staged filename portability
entry: treeport check --index
language: system
pass_filenames: false
always_run: true| Rule | Check | Default severity |
|---|---|---|
| TP001 | Win32-forbidden characters, including controls and ADS-style colons | Error |
| TP002 | Reserved device stems, including extensions and COM/LPT superscripts | Error |
| TP003 | Trailing ASCII dots/spaces in components | Error |
| TP004 | Case collisions, including directory and file/directory prefixes | Error for ASCII, warning otherwise |
| TP005 | NFC + Unicode case-folding collisions | Warning |
| TP006 | Component over 255 UTF-8 bytes or UTF-16 code units | Warning |
| TP007 | Relative path over the configured UTF-16 budget | Warning |
| TP008 | Filename not decodable as UTF-8 | Error |
| TP009 | Malformed relative path or exact file/directory overlap in input | Error |
Unicode comparisons are conservative Python comparisons, not an emulation of
NTFS, APFS or HFS+ collation. A clean report does not guarantee checkout, archive
extraction or build success. NTFS short-name aliases, .git alias protection,
permissions, executable bits, line endings, symlink behavior, long-path settings,
invalid branch names and file content portability are outside this release.
The scan stores the path list and directory prefixes in memory; million-path
repositories are not a validated target yet. HTML is static with no scripts or
remote assets; names are escaped, including control characters.
Existing pre-commit-hooks already
provides check-case-conflict and check-illegal-windows-names. If these satisfy
your workflow, use them. Treeport focuses on tree inspection without checkout,
combined reviewable reports and gradual adoption. See docs/DECISION.md.
python -m pip install -e .
python -m unittest discover -s tests -v
python -m pip install build twine
python -m build
python -m twine check dist/*On Windows PowerShell, if the last command does not expand the wildcard, use:
python -m twine check (Get-ChildItem dist -File).FullNameTests use only the standard library plus the installed package and real Git. They generate temporary Git objects containing otherwise uncreatable Windows names. They do not need network access. Dependencies for packaging are build-only; the distributed runtime contains only Treeport's own Python files and MIT license.
See CONTRIBUTING.md, roadmap, release instructions and security. Licensed under MIT.
JSON inputs are limited to 32 MiB and 128 nesting levels. Duplicate keys and non-finite constants are rejected. Git metadata reads disable fsmonitor hooks for that invocation; repository configuration is preserved.