1. Architecture Overview
tldr-pages/tldr is the canonical repository of community-maintained, simplified help pages for command-line tools. It is a content-first project: the vast majority of its 35,000+ files are Markdown help pages organized by platform and language. A thin layer of Node.js and Python tooling handles linting, indexing, testing, and PDF generation.
The project operates as a data source consumed by dozens of independently developed tldr client applications. The repository itself is not an executable application — it is a structured content collection with CI/CD automation for quality enforcement and artifact generation.
Component Architecture
┌─────────────────────────────────────────────────┐
│ tldr-pages/tldr │
├─────────────────────────────────────────────────┤
│ Content Layer (35,000+ Markdown files) │
│ ├── pages/ English pages (11 platforms)│
│ ├── pages.{lang}/ 40+ translation dirs │
│ └── contributing-guides/ Templates & standards│
├─────────────────────────────────────────────────┤
│ Tooling Layer │
│ ├── Node.js: markdownlint-cli, tldr-lint, husky│
│ ├── Python: render.py (PDF), utility scripts │
│ └── Bash: build.sh, deploy.sh, test.sh │
├─────────────────────────────────────────────────┤
│ CI/CD Layer (GitHub Actions) │
│ ├── Lint + test on every PR │
│ ├── Build ZIP archives on merge │
│ └── Build PDFs per language on merge │
└─────────────────────────────────────────────────┘
│ │
▼ ▼
ZIP archives PDF books
(client consumption) (offline reading)
Component Inventory
| Component | Path | Role |
|---|---|---|
| English pages | pages/{platform}/ |
Canonical command help pages (11 platform dirs) |
| Translated pages | pages.{lang}/{platform}/ |
40+ language translations |
| Contributing guides | contributing-guides/ |
Page format standards and translation templates |
| Markdown linter | node_modules/markdownlint-cli/ |
Enforces GFM formatting rules |
| tldr linter | node_modules/tldr-lint/ |
Enforces tldr-specific page format rules |
| Pre-commit hooks | node_modules/husky/ |
Runs linting before commits |
| Build scripts | scripts/ |
Index, archive, PDF, and utility scripts |
| PDF renderer | scripts/pdf/render.py |
Generates PDF books from page collections |
| Lint config | .markdownlint.json |
Markdownlint rule overrides |
| Package config | package.json |
npm scripts, dependencies |
Technology Stack
| Technology | Version / Detail | Purpose |
|---|---|---|
| Node.js | 20+ | Linting tools, build-index, husky hooks |
| Python | 3.x | PDF generation, page utility scripts |
| markdownlint-cli | ^0.48.0 | Markdown formatting enforcement |
| tldr-lint | ^0.0.22 | tldr-specific page validation |
| glob | 13.0.6 | File pattern matching for build scripts |
| husky | ^9.1.7 | Git hook management (dev dependency) |
| WeasyPrint | (via pip) | HTML-to-PDF rendering |
| markdown (Python) | (via pip) | Markdown-to-HTML conversion for PDF pipeline |
| black | 26.3.1 | Python code formatting |
| flake8 | 7.3.0 | Python linting |
| requests | 2.33.1 | HTTP requests (for bot integration) |
Note
The project has intentionally minimal code. The value is in the content (pages), not the tooling. The tooling exists solely to enforce quality and generate distribution artifacts.
Data Flow
Page contribution flow:
Contributor writes page.md
→ git commit (husky pre-commit hook triggers)
→ markdownlint + tldr-lint validate format
→ git push → GitHub PR
→ CI: test.sh + check-pr.sh
→ Maintainer review → merge
→ GitHub Actions: build.sh (ZIP) + build-pdf.sh (PDF)
→ deploy.sh pushes artifacts to static site
→ tldr clients fetch updated index + pages
PDF generation flow:
pages/{platform}/*.md
→ render.py reads all .md files
→ markdown lib converts to HTML
→ CSS stylesheets applied (basic / solarized-light / solarized-dark)
→ WeasyPrint renders to PDF
→ Output: tldr-book.pdf (or per-language/platform variants)
2. System Requirements
| Requirement | Minimum | Purpose |
|---|---|---|
| Node.js | 20+ | Linting, indexing, husky hooks |
| npm | (bundled with Node.js) | Dependency management |
| Python | 3.x | PDF generation, utility scripts |
| Git | Any recent | Repository operations |
| Bash | 4+ | Shell scripts (build.sh, test.sh, deploy.sh) |
| WeasyPrint system deps | GTK3, Pango, Cairo | PDF rendering (only for PDF generation) |
Important
Shell scripts (
build.sh,build-pdf.sh,deploy.sh,test.sh,check-pr.sh) are Linux/macOS only. On Windows, use WSL. Python scripts (render.py,set-alias-page.py,set-more-info-link.py,set-see-also.py,set-page-title.py,wrong-filename.py,update-command.py) work cross-platform.
Platform Compatibility Matrix
| Script | Linux | macOS | Windows |
|---|---|---|---|
render.py |
Yes | Yes | Yes |
build-pdf.sh |
Yes | Yes | WSL only |
build.sh |
Yes | Yes | WSL only |
set-alias-page.py |
Yes | Yes | Yes |
set-more-info-link.py |
Yes | Yes | Yes |
set-see-also.py |
Yes | Yes | Yes |
set-page-title.py |
Yes | Yes | Yes |
wrong-filename.py |
Yes | Yes | Yes |
update-command.py |
Yes | Yes | Yes |
check-errors.sh |
Yes | Untested | Untested |
3. Configuration Reference
Markdownlint Configuration
File: .markdownlint.json
{
"default": true,
"MD003": { "style": "atx" },
"MD007": { "indent": 4 },
"MD013": { "line_length": 250 },
"MD026": false,
"MD029": false,
"MD033": false,
"MD034": false,
"no-hard-tabs": false,
"whitespace": false
}
| Rule | Setting | Effect |
|---|---|---|
default |
true |
All markdownlint rules enabled by default |
MD003 |
style: "atx" |
Heading style must be ATX (# prefix), not setext |
MD007 |
indent: 4 |
Unordered list items must use 4-space indentation |
MD013 |
line_length: 250 |
Maximum line length set to 250 characters |
MD026 |
false |
Disabled — trailing punctuation in headings allowed |
MD029 |
false |
Disabled — ordered list item prefix style not enforced |
MD033 |
false |
Disabled — inline HTML allowed |
MD034 |
false |
Disabled — bare URLs allowed |
no-hard-tabs |
false |
Disabled — hard tabs permitted |
whitespace |
false |
Disabled — whitespace rules relaxed |
Important
The high
line_lengthof 250 and the disabled whitespace/HTML rules are intentional — tldr pages contain command examples with long lines and use specific formatting patterns that standard markdownlint rules would reject.
Package.json Scripts
File: package.json
| Script | Command | Purpose |
|---|---|---|
lint-markdown |
markdownlint pages*/**/*.md |
Lint all pages (all languages) with markdownlint |
lint-tldr-pages |
tldr-lint ./pages |
Lint English pages with tldr-specific rules |
test |
bash scripts/test.sh |
Run the full test suite |
build-index |
node ./scripts/build-index.js > index.json |
Generate the page index as JSON |
prepare |
husky |
Set up husky pre-commit hooks on npm install |
Note
lint-tldr-pagesonly lints the Englishpages/directory. Translated pages (pages.*/) are covered bylint-markdown(markdownlint) but not bytldr-lint.
Python Dependencies
File: requirements.txt (root — for development/CI)
| Package | Version | Purpose |
|---|---|---|
black |
26.3.1 | Python code formatter |
flake8 |
7.3.0 | Python linter |
requests |
2.33.1 | HTTP requests (for send-to-bot.py) |
File: scripts/pdf/requirements.txt (for PDF generation)
| Package | Version | Purpose |
|---|---|---|
markdown |
Latest | Markdown-to-HTML conversion |
weasyprint |
Latest | HTML-to-PDF rendering |
4. Deployment & Scaling
Build Pipeline
The deployment pipeline is driven by GitHub Actions. On merge to main:
test.sh— validates all pages pass lintingbuild.sh— generates ZIP archives of thepages/directoriesbuild-pdf.sh— generates per-language PDF booksdeploy.sh— pushes ZIP and PDF artifacts to the static website repository
Build Scripts Detail
scripts/build.sh — Builds ZIP archives:
- Packages each
pages*directory into a distributable archive - Used by CI for artifact publishing
scripts/build-index.js — Generates index.json:
- Scans all
pages/directories - Outputs a JSON index of all available pages, platforms, and languages
- Consumed by tldr clients for page discovery
scripts/deploy.sh — Deploys artifacts:
- Copies ZIP and PDF archives to the static website repository
- Triggered after successful build
scripts/build-pdf.sh — Orchestrates PDF generation:
# Process all languages
bash scripts/pdf/build-pdf.sh all
# Process only changed languages (CI mode — auto-detects from git diff)
bash scripts/pdf/build-pdf.sh
# Process a specific language
bash scripts/pdf/build-pdf.sh es
Warning
CJK languages (Bengali, Japanese, Korean, Malayalam, Tamil, Thai, Chinese, Chinese Traditional) are skipped by
build-pdf.shdue to font rendering limitations in the PDF pipeline.
PDF Rendering Architecture
The PDF pipeline (scripts/pdf/render.py) uses a multi-step process:
- Read all
.mdfiles from the target directory - Sort by platform subdirectory, then alphabetically
- Convert each page from Markdown to HTML using the
markdownPython library - Transform
#titles to<h2>(grouped under<h1>platform headers) - Convert
>description lines to<h4>elements - Apply CSS stylesheets (
basic.css, optionallysolarized-light.cssorsolarized-dark.css) - Render the combined HTML to PDF via WeasyPrint
CSS themes available in scripts/pdf/:
| Theme | File | Description |
|---|---|---|
| Basic | basic.css |
Default monochrome style (always loaded) |
| Solarized Light | solarized-light.css |
Light variant of Solarized color scheme |
| Solarized Dark | solarized-dark.css |
Dark variant of Solarized color scheme |
Tip
When building PDFs for a specific platform subset, use the
-pflag:python3 render.py ../../pages -p common linux. This produces a smaller, focused PDF.
5. Operations
Running Lint Checks
Full lint suite (all languages, markdownlint):
npm run lint-markdown
English pages only (tldr-lint + markdownlint):
npm run lint-tldr-pages
Run the complete test suite:
npm test
This executes scripts/test.sh, which runs basic syntax and format tests on all pages.
Note
The CI pipeline runs both
test.shandcheck-pr.shon every pull request.check-pr.shperforms additional PR-specific checks (e.g., verifying changed files match expected patterns).
Page Maintenance Scripts
| Script | Purpose | Usage |
|---|---|---|
set-alias-page.py |
Generate/update alias redirect pages | python3 scripts/set-alias-page.py |
set-more-info-link.py |
Update "More information" URLs | python3 scripts/set-more-info-link.py |
set-see-also.py |
Update "See also" cross-references | python3 scripts/set-see-also.py |
set-page-title.py |
Update page titles across languages | python3 scripts/set-page-title.py |
wrong-filename.py |
Detect title/filename mismatches | python3 scripts/wrong-filename.py |
update-command.py |
Update command examples across all languages | python3 scripts/update-command.py |
check-errors.sh |
Grep for common formatting errors | cd pages.{lang} && ../scripts/check-errors.sh |
send-to-bot.py |
Send build/test output to tldr-bot | python3 scripts/send-to-bot.py |
Tip
When making bulk changes across all translations (e.g., updating a "More information" link), use the dedicated scripts rather than manual edits. They handle the cross-language consistency automatically.
Monitoring Repository Health
Key metrics to track:
- Lint pass rate: All pages must pass both markdownlint and tldr-lint
- Translation coverage: Compare page counts in
pages.{lang}/againstpages/ - Title/filename consistency: Run
wrong-filename.pyperiodically - Stale "More information" links: Links rot over time; consider periodic URL validation
6. Performance
Repository Scale
| Metric | Value |
|---|---|
| Total files | 35,171 |
| Language translations | 40+ |
| Platform categories | 11 |
| English pages (estimated) | ~3,000+ (across all platforms) |
Note
Due to the massive file count, operations like
npm run lint-markdown(which globspages*/**/*.md) can take several minutes. Theglob13.0.6 dependency is used for efficient file matching.
PDF Build Times
PDF generation time scales linearly with the number of pages. English (pages/) is the largest and slowest. Individual language PDFs are significantly faster.
Git Operations
With 35,000+ files, initial git clone is large. Shallow clones reduce time significantly:
git clone --depth 1 https://github.com/tldr-pages/tldr.git
Tip
Contributors should use full clones (not shallow) to ensure
check-pr.shcan accurately diff against the base branch.
7. Security
Supply Chain
The repository's npm dependencies are minimal and well-maintained:
| Dependency | Risk Level | Notes |
|---|---|---|
markdownlint-cli |
Low | Widely used Markdown linter |
tldr-lint |
Low | tldr-specific linter, maintained by the tldr-pages org |
glob |
Low | Core Node.js ecosystem library |
husky |
Low (dev only) | Git hook manager, not shipped |
Warning
The
package-lock.jsonpins exact versions. Always review dependency updates carefully, especially formarkdownlint-cliwhich processes all page content. Runnpm auditperiodically.
Content Security
Since the repository is community-contributed Markdown, the primary security concern is malicious content injection:
- No executable code in pages — pages contain only descriptive text and example commands
- CI linting catches format deviations that could indicate injection attempts
- Maintainer review is required for all PRs
- HTML is allowed (MD033 disabled) — maintainers should review any inline HTML for XSS vectors if pages are rendered in web contexts
Bot Integration
send-to-bot.py uses the requests library to send build output to an external bot service. The target URL and authentication are configured within the script (not exposed as environment variables in the repository).
8. Developer Internals
Repository Structure
tldr/
├── .github/ GitHub Actions workflows
├── .markdownlint.json Markdownlint configuration
├── package.json npm dependencies and scripts
├── package-lock.json Locked dependency versions
├── requirements.txt Python dev dependencies
├── contributing-guides/ Contribution standards
│ └── translation-templates/ Per-language phrasing guides
├── images/ Repository images/assets
├── pages/ English pages
│ ├── common/ Cross-platform commands
│ ├── linux/ Linux commands
│ ├── osx/ macOS commands
│ ├── windows/ Windows commands
│ ├── android/ Android (Termux) commands
│ ├── sunos/ SunOS/Solaris commands
│ ├── freebsd/ FreeBSD commands
│ ├── openbsd/ OpenBSD commands
│ ├── netbsd/ NetBSD commands
│ ├── dos/ DOS commands
│ └── cisco-ios/ Cisco IOS commands
├── pages.{lang}/ Translated pages (40+ languages)
│ └── {platform}/ Same platform structure as pages/
└── scripts/
├── build.sh Build ZIP archives
├── build-index.js Generate index.json
├── check-pr.sh PR validation checks
├── deploy.sh Deploy artifacts to website
├── test.sh Test suite runner
├── check-errors.sh Grep-based error finder
├── send-to-bot.py Bot notification script
├── set-alias-page.py Alias page generator
├── set-more-info-link.py URL updater
├── set-see-also.py Cross-reference updater
├── set-page-title.py Title updater
├── wrong-filename.py Title/filename mismatch detector
├── update-command.py Cross-language example updater
└── pdf/
├── render.py PDF generator (Python)
├── build-pdf.sh PDF build orchestrator
├── requirements.txt WeasyPrint + markdown deps
├── basic.css Default PDF stylesheet
├── solarized-light.css Light theme
└── solarized-dark.css Dark theme
Module Reference
scripts/pdf/render.py
The primary code module in the repository. Converts Markdown pages to a single PDF document.
Function: main(loc, colorscheme, output_filename, platform)
| Parameter | Type | Description |
|---|---|---|
loc |
str |
Path to the pages directory |
colorscheme |
str |
CSS theme name: basic, solarized-light, or solarized-dark |
output_filename |
str |
Output PDF filename |
platform |
list[str] or None |
Platform filter (e.g., ["common", "linux"]) |
Behavior:
- Validates
locis a valid directory (exits with code 1 if not) - Builds a CSS list: always includes
basic.css, appends theme CSS if notbasic - Iterates platform subdirectories alphabetically
- For each platform, iterates
.mdfiles alphabetically - Transforms the Markdown: converts
#title to<h2>,>lines to#### - Converts each line to HTML via
markdown.markdown() - Inserts page breaks between entries
- Renders the combined HTML string to PDF via
WeasyPrint.HTML(string=).write_pdf() - Appends platform names to filename when
-pfilter is used
CLI entry point: argparse-based, invoked as python3 render.py <args>.
scripts/pdf/build-pdf.sh
Orchestrates PDF builds for CI. Determines which language directories changed (via git diff-tree) and calls render.py for each.
Behavior:
- No argument: auto-detect changed
pages*directories from the latest commit - Argument
all: build PDFs for allpages*directories - Argument
en: build forpages/(English, usessolarized-lighttheme) - Argument
{lang}: build forpages.{lang}/(usesbasictheme) - CJK languages (
bn,ja,ko,ml,ta,th,zh,zh_TW) are skipped with a message
Code Patterns and Conventions
| Pattern | Convention |
|---|---|
| Page filenames | Lowercase, hyphen-separated: command-name.md |
| Platform directories | common, linux, osx, windows, android, sunos, freebsd, openbsd, netbsd, dos, cisco-ios |
| Language codes | ISO 639-1 (e.g., es, fr, zh) or locale (e.g., pt_BR, zh_TW) |
| Translation directories | pages.{lang_code}/ — dot-separated from pages |
| CSS theming | Additive: basic.css always loaded, theme CSS layered on top |
| Page structure | # title → > description → > More information → - example → `command` |
| Script languages | Bash for CI/build; Python for content manipulation; Node.js for linting |
| Linting approach | Two-tier: markdownlint for GFM rules, tldr-lint for domain-specific rules |
Testing Strategy
The repository uses a shell-based testing approach rather than a formal test framework:
scripts/test.sh— Primary test runner invoked bynpm test. Runs syntax and format validation across all pages.scripts/check-pr.sh— PR-specific checks: validates changed files conform to naming conventions, proper directory placement, and format rules.- Pre-commit hooks —
huskyruns linting before every commit (configured vianpm run prepare). - Python formatting —
black(26.3.1) andflake8(7.3.0) for Python script quality.
Note
There is no unit test framework (no Jest, pytest, etc.) for the scripts themselves. Testing is focused on content validation rather than code correctness.
Potential Improvements
- CJK PDF support —
build-pdf.shcurrently skips 8 languages. Adding CJK font support to the PDF pipeline would cover a significant portion of translations. tldr-lintcoverage for translations — Currently,npm run lint-tldr-pagesonly validates English pages. Extending it topages.*/would improve quality across all languages.- URL validation — The "More information" links in pages are not automatically validated. A periodic link-checker script would catch dead links.
- Script test coverage — The Python utility scripts lack automated tests. Adding pytest for
render.py,set-alias-page.py, etc. would improve reliability. - Incremental PDF builds — Currently, changing one page triggers a full PDF rebuild for that language. Incremental builds could speed up CI significantly.
Contributing Guide
Setting up the development environment:
-
Clone the repository:
git clone https://github.com/tldr-pages/tldr.git cd tldr -
Install Node.js dependencies (linting tools + husky):
npm install -
Install Python dependencies (for scripts):
pip install -r requirements.txt pip install -r scripts/pdf/requirements.txt -
Verify the setup:
npm test npm run lint-tldr-pages
Development workflow:
- Create a feature branch
- Make changes (new pages, translations, script improvements)
- Run linting:
npm run lint-markdownandnpm run lint-tldr-pages - Commit (husky hook auto-validates)
- Push and open a PR
- CI runs
test.sh+check-pr.sh - Maintainer review and merge
Caution
Do not commit changes to
package-lock.jsonunless you are intentionally updating dependencies. Unintended lockfile changes cause merge conflicts and can introduce supply chain risks.
9. Integration Points
Client Ecosystem
The repository serves as the upstream data source for all tldr client implementations. Clients consume pages via:
- GitHub raw file access — Direct Markdown file fetches
- ZIP archives — Built by
scripts/build.sh, hosted on the static site index.json— Generated byscripts/build-index.js, used for page discovery and search
GitHub Actions CI/CD
The repository uses GitHub Actions for automation:
- On PR:
test.sh+check-pr.shrun to validate contributions - On merge:
build.shgenerates ZIPs,build-pdf.shgenerates PDFs,deploy.shpushes to the website
Bot Integration
scripts/send-to-bot.py sends build and test output to a tldr-bot service for community notifications (e.g., Gitter, Matrix, or other chat platforms).