Skip to content

Commit a9ef564

Browse files
committed
--doc with TAB completion for easy access to docs
1 parent 3fa4bed commit a9ef564

9 files changed

Lines changed: 330 additions & 99 deletions

File tree

v2/Changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ For a high-level summary see [CHANGELOG.md](../CHANGELOG.md) in the repo root.
77

88
### Added
99

10+
- **`--doc` / `--doc-pretty` CLI options** (`dar-backup`) — print any installed documentation file by name and exit. Tab completion lists available doc names from the installed package (`dar-backup --doc <TAB>`). Plain and rich-rendered variants mirror the existing `--readme` / `--readme-pretty` pattern.
11+
12+
- **`scripts/copy_docs.sh`** — shared script called by `build.sh`, `release.sh`, and test fixtures to copy user-facing `doc/*.md` files into `src/dar_backup/` for wheel inclusion. Internal files (`todo.md`, `dev.md`, `dar_manager_w_dst_bug_report.md`, `NFS server notes.md`) are excluded. Tests in `tests/test_readme_changelog.py` verify the correct set of files is present and absent after the script runs.
13+
1014
- **Structured large-scale test results** (`doc/test-report/large-scale-results.jsonl`) — `large_scale_test.sh` now appends one JSONL record per run containing datestamp, git commit, tool versions (dar-backup, dar, par2, python), OS, kernel, elapsed times, backup sizes, per-tool peak memory, failure count, and pass/fail status. The file is written to `${RESULTS_DIR}` and mirrored to `doc/test-report/` in the repo so runs are git-tracked. Four historical runs (2026-06-09 through 2026-06-20) are backfilled.
1115

1216
- **`show_large_scale_results.py`** (`scripts/`) — parses `large-scale-results.jsonl` and prints a fixed-width ASCII table sorted newest-first. Defaults to `doc/test-report/large-scale-results.jsonl`; accepts an optional path argument.

v2/build.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ set -euo pipefail
66
if [ ! -d venv ]; then
77
python3 -m venv venv
88
fi
9+
# shellcheck disable=SC1091
910
source venv/bin/activate
1011

11-
PYTHON="$(which python3)"
1212
PIP="$(which pip)"
1313

1414
echo "🔧 Installing project in editable mode in venv: $VIRTUAL_ENV"
@@ -19,6 +19,10 @@ echo "✅ Project installed in editable mode."
1919
echo "🧹 Cleaning old build artifacts..."
2020
rm -rf dist/* 2>/dev/null || true
2121

22+
echo "📄 Copying docs into package for wheel inclusion..."
23+
trap 'rm -f src/dar_backup/README.md src/dar_backup/Changelog.md; rm -f src/dar_backup/doc/*.md 2>/dev/null; rmdir src/dar_backup/doc 2>/dev/null || true' EXIT
24+
bash scripts/copy_docs.sh
25+
2226
echo "📦 Building installable packages (sdist + wheel)..."
2327
hatch build --clean
2428
echo "✅ Packages written to dist/:"

v2/doc/cli-reference.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ Available options:
5353
--readme-pretty Print README.md with Markdown styling and exit
5454
--changelog Print Changelog and exit
5555
--changelog-pretty Print Changelog with Markdown styling and exit
56+
--doc <name> Print a documentation file by name and exit (tab completion lists available docs)
57+
--doc-pretty <name> Print a documentation file with Markdown styling and exit
5658
-v, --version Show version and license information.
5759
```
5860

v2/doc/shell-completion.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ The `dar-backup`, `manager`, and `cleanup` scripts support dynamic tab-completio
2020

2121
manager: --list-archive-contents, --add-specific-archive (autocomplete those **not** in the catalog database), --remove-specific-archive
2222

23+
- `--doc` and `--doc-pretty` complete from the installed documentation files:
24+
25+
```bash
26+
dar-backup --doc <TAB>
27+
# ⤷ Suggests: cli-reference, config-reference, dar-tips, getting-started, ...
28+
dar-backup --doc-pretty rest<TAB>
29+
# ⤷ Suggests: restoring
30+
```
31+
2332
- Supports paths like ~ and $HOME correctly
2433

2534
## Use it

v2/release.sh

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -297,27 +297,13 @@ TEMP_CHANGELOG="src/dar_backup/Changelog.md"
297297
trap 'rm -f "$TEMP_README" "$TEMP_CHANGELOG"; rm -f src/dar_backup/doc/*; rmdir src/dar_backup/doc' EXIT
298298

299299
if $DRY_RUN; then
300-
dryrun "copy README.md and Changelog.md into src/dar_backup/ for wheel inclusion"
301300
dryrun "refresh v2/README.md from root README.md for PyPI description"
302-
dryrun "copy doc/*.md into src/dar_backup/doc/ for wheel inclusion"
301+
dryrun "copy docs into src/dar_backup/ for wheel inclusion (scripts/copy_docs.sh)"
303302
else
304303
cp ../README.md README.md ||
305304
{ red "❌ Error: Failed to refresh README.md from root"; exit 1; }
306305

307-
cp ../README.md "$TEMP_README" ||
308-
{ red "❌ Error: Failed to copy README.md to $TEMP_README"; exit 1; }
309-
310-
cp Changelog.md "$TEMP_CHANGELOG" ||
311-
{ red "❌ Error: Failed to copy Changelog.md to $TEMP_CHANGELOG"; exit 1; }
312-
313-
mkdir -p src/dar_backup/doc
314-
315-
find doc/ -maxdepth 1 -name "*.md" \
316-
! -name "todo.md" \
317-
! -name "dev.md" \
318-
! -name "dar_manager_w_dst_bug_report.md" \
319-
! -name "NFS server notes.md" \
320-
-exec cp {} src/dar_backup/doc/ \;
306+
scripts/copy_docs.sh || { red "❌ Error: copy_docs.sh failed"; exit 1; }
321307
fi
322308

323309

v2/scripts/copy_docs.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env bash
2+
# SPDX-License-Identifier: GPL-3.0-or-later
3+
#
4+
# copy_docs.sh — copy user-facing docs into src/dar_backup/ for wheel inclusion
5+
#
6+
# Called by build.sh, release.sh, and test fixtures so that README.md,
7+
# Changelog.md, and doc/*.md are present in installed packages and the
8+
# --readme / --changelog / --readme-pretty / --changelog-pretty CLI options
9+
# can find them via _resolve_doc_path().
10+
#
11+
# Must be run from the v2/ directory.
12+
# Cleanup of the copied files is the caller's responsibility.
13+
14+
set -euo pipefail
15+
16+
[[ -f "pyproject.toml" && -d "src/dar_backup" ]] \
17+
|| { echo "ERROR: must be run from the v2/ directory" >&2; exit 1; }
18+
19+
cp ../README.md src/dar_backup/README.md
20+
cp Changelog.md src/dar_backup/Changelog.md
21+
22+
mkdir -p src/dar_backup/doc
23+
find doc/ -maxdepth 1 -name "*.md" \
24+
! -name "todo.md" \
25+
! -name "dev.md" \
26+
! -name "dar_manager_w_dst_bug_report.md" \
27+
! -name "NFS server notes.md" \
28+
-exec cp {} src/dar_backup/doc/ \;
29+
30+
echo "docs copied to src/dar_backup/"

v2/src/dar_backup/__about__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "1.1.10.dev21"
1+
__version__ = "1.1.10.dev22"
22

33
__author__ = "Per Jensen"
44

v2/src/dar_backup/dar_backup.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1928,6 +1928,61 @@ def print_readme(path: str = None, pretty: bool = True):
19281928
resolved_path = _resolve_doc_path(path, "README.md")
19291929
print_markdown(str(resolved_path), pretty=pretty)
19301930

1931+
1932+
def _list_available_docs() -> list[str]:
1933+
"""Return sorted list of doc names available in the installed package.
1934+
1935+
Returns:
1936+
Sorted list of doc stems (filenames without .md extension),
1937+
or an empty list if the doc directory cannot be found.
1938+
"""
1939+
for doc_dir in [
1940+
Path(__file__).parent / "doc",
1941+
Path.cwd() / "src" / "dar_backup" / "doc",
1942+
]:
1943+
if doc_dir.is_dir():
1944+
return sorted(p.stem for p in doc_dir.glob("*.md"))
1945+
return []
1946+
1947+
1948+
def _doc_completer(prefix: str, **kwargs) -> list[str]:
1949+
"""Argcomplete completer for --doc and --doc-pretty.
1950+
1951+
Args:
1952+
prefix: Characters typed so far.
1953+
1954+
Returns:
1955+
Doc stems that start with prefix.
1956+
"""
1957+
return [name for name in _list_available_docs() if name.startswith(prefix)]
1958+
1959+
1960+
def print_doc(name: str, pretty: bool = False) -> None:
1961+
"""Print a documentation file from the installed doc/ directory.
1962+
1963+
Args:
1964+
name: Doc filename stem without .md extension (e.g. 'getting-started').
1965+
pretty: If True, render with rich Markdown formatting.
1966+
1967+
Raises:
1968+
SystemExit: If the named doc cannot be found.
1969+
"""
1970+
candidates = [
1971+
Path(__file__).parent / "doc" / f"{name}.md",
1972+
Path.cwd() / "src" / "dar_backup" / "doc" / f"{name}.md",
1973+
]
1974+
for candidate in candidates:
1975+
if candidate.exists():
1976+
print_markdown(str(candidate), pretty=pretty)
1977+
return
1978+
1979+
available = _list_available_docs()
1980+
print(f"❌ Doc '{name}' not found.", file=stderr)
1981+
if available:
1982+
print(f"Available docs: {', '.join(available)}", file=stderr)
1983+
raise SystemExit(1)
1984+
1985+
19311986
def list_definitions(backup_d_dir: str, *, allow_unsafe: bool = False) -> List[str]:
19321987
"""
19331988
Return backup definition filenames from BACKUP.D_DIR, sorted by name.
@@ -2080,6 +2135,10 @@ def _sigterm_handler(signum, frame):
20802135
parser.add_argument("--readme-pretty", action="store_true", help="Print README.md to stdout with Markdown styling and exit.")
20812136
parser.add_argument("--changelog", action="store_true", help="Print Changelog.md to stdout and exit.")
20822137
parser.add_argument("--changelog-pretty", action="store_true", help="Print Changelog.md to stdout with Markdown styling and exit.")
2138+
doc_arg = parser.add_argument("--doc", metavar="NAME", help="Print a documentation file by name and exit (use tab completion to list available docs).")
2139+
doc_arg.completer = _doc_completer
2140+
doc_pretty_arg = parser.add_argument("--doc-pretty", metavar="NAME", help="Print a documentation file with Markdown styling and exit.")
2141+
doc_pretty_arg.completer = _doc_completer
20832142
parser.add_argument('-v', '--version', action='store_true', help="Show version and license information.")
20842143

20852144
argcomplete.autocomplete(parser)
@@ -2116,6 +2175,12 @@ def _sigterm_handler(signum, frame):
21162175
elif args.changelog_pretty:
21172176
print_changelog(None, pretty=True)
21182177
exit(0)
2178+
elif args.doc:
2179+
print_doc(args.doc, pretty=False)
2180+
exit(0)
2181+
elif args.doc_pretty:
2182+
print_doc(args.doc_pretty, pretty=True)
2183+
exit(0)
21192184

21202185

21212186
config_settings_path = get_config_file(args)

0 commit comments

Comments
 (0)