From 0da9e5bb28e44b0d3cb609fa7f6644021e2f1124 Mon Sep 17 00:00:00 2001
From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com>
Date: Wed, 5 Aug 2026 20:27:40 -0700
Subject: [PATCH 1/2] docs: publish MkDocs site
---
.github/workflows/docs.yml | 81 ++++++++++++++++++++++++++++
CHANGELOG.md | 5 ++
README.md | 33 ++++++------
SECURITY.md | 8 +--
docs/adopter-readiness.md | 8 +--
docs/index.md | 71 ++++++++++++++++++++++++
docs/releasing.md | 12 +++++
mkdocs.yml | 53 ++++++++++++++++++
pyproject.toml | 4 ++
scripts/validate_package_artifact.py | 3 ++
tests/validate.sh | 3 ++
11 files changed, 258 insertions(+), 23 deletions(-)
create mode 100644 .github/workflows/docs.yml
create mode 100644 docs/index.md
create mode 100644 mkdocs.yml
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 0000000..75e0118
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,81 @@
+name: Documentation
+
+on:
+ push:
+ branches:
+ - main
+ paths:
+ - "README.md"
+ - "SECURITY.md"
+ - "docs/**"
+ - "mkdocs.yml"
+ - "pyproject.toml"
+ - "scripts/validate_docs.py"
+ - "tests/validate.sh"
+ - ".github/workflows/docs.yml"
+ pull_request:
+ paths:
+ - "README.md"
+ - "SECURITY.md"
+ - "docs/**"
+ - "mkdocs.yml"
+ - "pyproject.toml"
+ - "scripts/validate_docs.py"
+ - "tests/validate.sh"
+ - ".github/workflows/docs.yml"
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build:
+ name: Build and validate documentation
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ steps:
+ - name: Check out source
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+
+ - name: Set up Python
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
+ with:
+ python-version: "3.13"
+
+ - name: Install documentation tools
+ run: python -m pip install ".[docs]"
+
+ - name: Validate repository links and examples
+ run: python scripts/validate_docs.py
+
+ - name: Configure GitHub Pages
+ uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0
+
+ - name: Build documentation with strict warnings
+ run: python -m mkdocs build --strict --site-dir site
+
+ - name: Upload Pages artifact
+ uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0
+ with:
+ path: site
+
+ deploy:
+ name: Deploy documentation site
+ if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
+ needs: build
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ permissions:
+ pages: write
+ id-token: write
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@f33f41b675f0ab2dc5a6863c9a170fe83af3571e # v4.0.0
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 816cc4e..4627684 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -98,6 +98,11 @@ the API stability policy and migration guide before upgrading from `0.3.x`.
## [Unreleased]
+### Added
+
+- Add the MkDocs documentation site configuration, strict documentation checks,
+ and GitHub Pages deployment workflow.
+
### Changed
- Reclassify the project as Beta in package metadata to reflect its documented
diff --git a/README.md b/README.md
index 7622f54..19a34b3 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,9 @@ Install it with:
python -m pip install base-cli
```
+Read the complete documentation at
+.
+
## Quick start
Create a small command with a consistent context, logging, and cleanup
@@ -47,7 +50,7 @@ Run it with `python hello.py --name Ada`. The full lifecycle and configuration
options are documented below.
Release builds, TestPyPI rehearsals, and protected PyPI publication are
-documented in [`docs/releasing.md`](docs/releasing.md). The package exposes
+documented in [`docs/releasing.md`](https://basefoundry.github.io/base-cli/releasing/). The package exposes
`base_cli.__version__`, which matches the distribution version.
The package is distributed under the Apache License 2.0. Base itself remains
@@ -71,33 +74,33 @@ execution, while `base_cli` provides reusable lifecycle behavior:
- test helpers built on Click's `CliRunner`
Typer applications can opt into the same lifecycle with the optional
-`base-cli[typer]` extra. See [`docs/typer-adapter.md`](docs/typer-adapter.md)
+`base-cli[typer]` extra. See [`docs/typer-adapter.md`](https://basefoundry.github.io/base-cli/typer-adapter/)
for the migration path; Typer remains optional and is never imported by the
core Click integration.
Automation-facing JSON output, errors, and logs are opt-in through the
-versioned contracts documented in [`docs/json-contracts.md`](docs/json-contracts.md).
+versioned contracts documented in [`docs/json-contracts.md`](https://basefoundry.github.io/base-cli/json-contracts/).
Human output and Click error behavior remain the default.
The supported public facade, compatibility promises, deprecation warning
mechanism, and migration requirements are documented in
-[`docs/api-stability.md`](docs/api-stability.md) and
-[`docs/migrations.md`](docs/migrations.md).
+[`docs/api-stability.md`](https://basefoundry.github.io/base-cli/api-stability/) and
+[`docs/migrations.md`](https://basefoundry.github.io/base-cli/migrations/).
Security reporting, runtime trust boundaries, threat assumptions, and the
release security checklist are documented in [`SECURITY.md`](SECURITY.md),
-[`docs/security-threat-model.md`](docs/security-threat-model.md), and
-[`docs/security-review.md`](docs/security-review.md).
+[`docs/security-threat-model.md`](https://basefoundry.github.io/base-cli/security-threat-model/), and
+[`docs/security-review.md`](https://basefoundry.github.io/base-cli/security-review/).
Shared record renderers keep machine output stable: CSV and TSV stream
one-pass iterables without headers or footers, while terminal tables account
for Unicode display width and safely truncate oversized cells. See
-[`docs/output-contracts.md`](docs/output-contracts.md) for the output rules and
+[`docs/output-contracts.md`](https://basefoundry.github.io/base-cli/output-contracts/) for the output rules and
deterministic width controls.
Optional Rich tables and OpenTelemetry lifecycle spans are available through
separate extras; they are never imported or required by the default install.
-See [`docs/integrations.md`](docs/integrations.md) for opt-in configuration and
+See [`docs/integrations.md`](https://basefoundry.github.io/base-cli/integrations/) for opt-in configuration and
graceful-degradation behavior.
## Design Goals
@@ -141,7 +144,7 @@ The generic profile has no manifest filename convention, no product-owned
configuration directory, and no implicit history writer. Applications can
provide those policies through callbacks or build their own profile. The
consumer-owned adapters should supply any product-specific policies. See
-[`docs/consumer-profiles.md`](docs/consumer-profiles.md) for the boundary and
+[`docs/consumer-profiles.md`](https://basefoundry.github.io/base-cli/consumer-profiles/) for the boundary and
migration guidance.
### Typed extension contracts
@@ -190,7 +193,7 @@ can opt into `base_cli.ExtensionDiscovery`. It recognizes the documented
`base_cli.commands`, `base_cli.profiles`, and `base_cli.plugins` entry-point
groups. Discovery is lazy and cached, duplicate names fail explicitly, broken
extensions are isolated by `load_all()`, and consumers can disable discovery or
-provide an allowlist. See [`docs/extensions.md`](docs/extensions.md) for the
+provide an allowlist. See [`docs/extensions.md`](https://basefoundry.github.io/base-cli/extensions/) for the
entry-point contracts and deterministic ordering rules.
## Public API
@@ -199,7 +202,7 @@ The supported facade is `import base_cli`. It exports the command lifecycle
(`App`, `Context`, `run_app`, decorators, and logging helpers), command filters,
and the structured command protocol helpers. Consumer-owned user configuration
is passed through `Context.user_config`; the library does not impose a schema.
-See [`docs/user-config-typing.md`](docs/user-config-typing.md) for the
+See [`docs/user-config-typing.md`](https://basefoundry.github.io/base-cli/user-config-typing/) for the
intentional opaque boundary and the recommended typed accessor pattern.
The corresponding modules are also available as
`base_cli.command_filters`, `base_cli.command_protocol`, and
@@ -230,7 +233,7 @@ Typer, and automation/observability flows; each example has its own packaging,
tests, completion, release, and troubleshooting guidance.
Teams evaluating adoption can follow the [adopter readiness and migration
-guide](docs/adopter-readiness.md) and run the three independent
+guide](https://basefoundry.github.io/base-cli/adopter-readiness/) and run the three independent
[downstream compatibility consumers](compatibility/README.md).
## Minimal Command
@@ -538,7 +541,7 @@ Every `base_cli.App` command gets these options:
- `--log-file `: write the persistent log to a specific file.
- `--version`: shown when the `App` was created with a version.
- `--json`: opt-in machine output, when `LifecycleOptions.json` is enabled;
- emits the versioned envelopes described in [`docs/json-contracts.md`](docs/json-contracts.md).
+ emits the versioned envelopes described in [`docs/json-contracts.md`](https://basefoundry.github.io/base-cli/json-contracts/).
`LifecycleOptions()` preserves this default set. Its `debug`, `quiet`,
`environment`, `config`, `keep_temp`, `log_file`, and `version` fields are
@@ -986,7 +989,7 @@ On POSIX, base-cli enforces owner-only `0600`/`0700` modes. On Windows, the
default user-local cache root relies on inherited user-profile ACLs; consumers
using a custom cache root must provide the appropriate ACL themselves.
-See [Platform support](docs/platform-support.md) for the supported Linux,
+See [Platform support](https://basefoundry.github.io/base-cli/platform-support/) for the supported Linux,
WSL2, macOS, and native Windows boundaries. Native Windows support covers the
generic `base-cli` framework; it does not imply native Windows support for
Base or `basectl`.
diff --git a/SECURITY.md b/SECURITY.md
index 33ddd99..aa07523 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -2,7 +2,7 @@
`base-cli` is a library for embedding a lifecycle into Python command-line
applications. We take reports about the framework, its release artifacts, and
-the security controls documented in the [runtime threat model](docs/security-threat-model.md)
+the security controls documented in the [runtime threat model](https://basefoundry.github.io/base-cli/security-threat-model/)
seriously.
## Reporting a vulnerability
@@ -40,7 +40,7 @@ changes the supported window will update this table and the changelog.
| `main` | Supported for fixes merged before the next release |
| `<0.3` | Upgrade strongly recommended; best effort only |
-The [API stability policy](docs/api-stability.md) explains the pre-1.0
+The [API stability policy](https://basefoundry.github.io/base-cli/api-stability/) explains the pre-1.0
compatibility boundary. A security fix may require an emergency breaking
change when leaving a vulnerable behavior in place would expose users.
@@ -68,6 +68,6 @@ The framework protects the lifecycle data it owns: argv redaction, private
runtime files, fail-closed temporary cleanup, bounded JSON contracts, and
opt-in telemetry with a safe attribute set. It does not sandbox consumer
callbacks, third-party plugins, Python dependencies, shell commands, or the
-operating system. Consumers must review the [threat model](docs/security-threat-model.md)
-and complete the [security review checklist](docs/security-review.md) for their
+operating system. Consumers must review the [threat model](https://basefoundry.github.io/base-cli/security-threat-model/)
+and complete the [security review checklist](https://basefoundry.github.io/base-cli/security-review/) for their
own profile, plugins, paths, history writer, and telemetry exporter.
diff --git a/docs/adopter-readiness.md b/docs/adopter-readiness.md
index 9e0bf19..6da2b4d 100644
--- a/docs/adopter-readiness.md
+++ b/docs/adopter-readiness.md
@@ -29,7 +29,7 @@ box below:
- [ ] Publish an owner, escalation path, and a redacted support bundle format.
The maintainable downstream fixtures in
-[`compatibility/consumers`](../compatibility/README.md) are the executable
+[compatibility consumers](https://github.com/basefoundry/base-cli/tree/main/compatibility/consumers) are the executable
version of this checklist.
## Migration path
@@ -58,13 +58,13 @@ version of this checklist.
For public API and deprecation rules, see [`api-stability.md`](api-stability.md)
and [`migrations.md`](migrations.md). The four framework reference applications
-show copy-pasteable packaging patterns in [`examples/README.md`](../examples/README.md).
+show copy-pasteable packaging patterns in the [reference application catalog](https://github.com/basefoundry/base-cli/blob/main/examples/README.md).
## Support channel
-Use the repository's [Adoption support issue template](../.github/ISSUE_TEMPLATE/support.md)
+Use the repository's [Adoption support issue template](https://github.com/basefoundry/base-cli/blob/main/.github/ISSUE_TEMPLATE/support.md)
for migration questions, compatibility failures, and redacted reproductions.
-Security reports must follow [`SECURITY.md`](../SECURITY.md), not a public issue.
+Security reports must follow [`SECURITY.md`](https://github.com/basefoundry/base-cli/blob/main/SECURITY.md), not a public issue.
Include the framework version, Python/platform, installed dependency versions,
command shape (with secrets removed), exit code, and a support bundle path.
Maintainers triage adoption issues during normal project work and link any
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..b768bc9
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,71 @@
+# base-cli
+
+`base-cli` is a small, consumer-neutral Python framework for writing
+professional command-line applications. It gives commands a consistent
+lifecycle, context, logging, cleanup, configuration, and test boundary while
+leaving application policy in the consuming project.
+
+## Quick start
+
+Install the package:
+
+```bash
+python -m pip install base-cli
+```
+
+Create a command:
+
+```python
+from __future__ import annotations
+
+import base_cli
+
+
+app = base_cli.App(name="hello", version="0.1.0")
+
+
+@app.command()
+@base_cli.option("--name", default="world", show_default=True)
+def hello(ctx: base_cli.Context, name: str) -> int:
+ ctx.log.info("greeting %s", name)
+ print(f"Hello, {name}!")
+ return base_cli.ExitCode.SUCCESS
+
+
+if __name__ == "__main__":
+ raise SystemExit(base_cli.run_app(app))
+```
+
+Run it with:
+
+```bash
+python hello.py --name Ada
+```
+
+The command receives a context with structured logging, per-run paths,
+configuration, environment metadata, and deterministic cleanup. The same
+lifecycle can be attached to an existing Click tree or an optional Typer
+application.
+
+## Choose a path
+
+- Start with the [adopter readiness guide](adopter-readiness.md) for a
+ production evaluation.
+- Read [API stability](api-stability.md) and the [migration guide](migrations.md)
+ before upgrading across a compatibility boundary.
+- Follow [consumer profiles](consumer-profiles.md) when your application owns
+ project discovery or configuration policy.
+- Use the [Typer adapter](typer-adapter.md) to bring an existing Typer command
+ tree under the same lifecycle.
+- Review the [JSON contracts](json-contracts.md) and [output contracts](output-contracts.md)
+ before building automation around command output.
+
+## Design principles
+
+`base-cli` is intentionally thin: Click owns parsing and command execution,
+while the framework supplies reusable lifecycle behavior. It avoids import-time
+filesystem writes, keeps logs on stderr, preserves application-owned state,
+and treats optional integrations as explicit extras.
+
+See the [repository README](https://github.com/basefoundry/base-cli#readme) for
+the full API overview, reference applications, and installation examples.
diff --git a/docs/releasing.md b/docs/releasing.md
index 819fc79..8cc9e1c 100644
--- a/docs/releasing.md
+++ b/docs/releasing.md
@@ -31,6 +31,18 @@ cleanup.
The publish job downloads that same reviewed artifact; it does not rebuild
during publication.
+## Documentation site
+
+The Documentation workflow builds this site with `mkdocs build --strict` and
+publishes the reviewed site to GitHub Pages after changes land on `main`. The
+canonical URL is and is exposed in
+the PyPI project metadata as the `Documentation` link.
+
+Repository administrators should enable GitHub Pages for the repository using
+the GitHub Actions source and approve the `github-pages` environment the first
+time the workflow deploys. Pull requests run the strict build and repository
+link checks without publishing.
+
## TestPyPI rehearsal
1. Dispatch **Package** from the branch or tag to be rehearsed and choose
diff --git a/mkdocs.yml b/mkdocs.yml
new file mode 100644
index 0000000..8a41d82
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,53 @@
+site_name: base-cli
+site_description: A small, consumer-neutral Python framework for professional command-line applications.
+site_url: https://basefoundry.github.io/base-cli/
+repo_name: basefoundry/base-cli
+repo_url: https://github.com/basefoundry/base-cli
+edit_uri: edit/main/docs/
+
+theme:
+ name: material
+ features:
+ - navigation.sections
+ - navigation.top
+ - search.highlight
+ - search.share
+ icon:
+ repo: fontawesome/brands/github
+
+plugins:
+ - search
+
+markdown_extensions:
+ - admonition
+ - attr_list
+ - pymdownx.highlight
+ - pymdownx.superfences
+ - pymdownx.tabbed:
+ alternate_style: true
+ - toc:
+ permalink: true
+
+nav:
+ - Home: index.md
+ - Getting started:
+ - Adopter readiness: adopter-readiness.md
+ - Platform support: platform-support.md
+ - Consumer profiles: consumer-profiles.md
+ - API and contracts:
+ - API stability: api-stability.md
+ - Migration guide: migrations.md
+ - Output contracts: output-contracts.md
+ - JSON contracts: json-contracts.md
+ - Typed user configuration: user-config-typing.md
+ - Entry-point extensions: extensions.md
+ - Integrations:
+ - Typer adapter: typer-adapter.md
+ - Optional integrations: integrations.md
+ - Operations and security:
+ - Local configuration: local-config.md
+ - Cache ownership and layout: cache-ownership-and-layout.md
+ - Performance: performance.md
+ - Runtime threat model: security-threat-model.md
+ - Security release review: security-review.md
+ - Releasing base-cli: releasing.md
diff --git a/pyproject.toml b/pyproject.toml
index ae9a162..adc372c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -60,11 +60,15 @@ quality = [
"pytest-cov>=5,<7",
"ruff>=0.8,<1",
]
+docs = [
+ "mkdocs-material>=9.5,<10",
+]
[project.urls]
Homepage = "https://github.com/basefoundry/base-cli"
Repository = "https://github.com/basefoundry/base-cli"
Issues = "https://github.com/basefoundry/base-cli/issues"
+Documentation = "https://basefoundry.github.io/base-cli/"
[tool.setuptools]
package-dir = {"" = "lib/python"}
diff --git a/scripts/validate_package_artifact.py b/scripts/validate_package_artifact.py
index d43b3df..de1cabb 100644
--- a/scripts/validate_package_artifact.py
+++ b/scripts/validate_package_artifact.py
@@ -17,6 +17,7 @@
IMPORT_NAME = "base_cli"
MINIMUM_PYTHON = ">=3.10"
REQUIRED_DEPENDENCIES = ("click>=8.1", "PyYAML>=6.0")
+DOCUMENTATION_URL = "Documentation, https://basefoundry.github.io/base-cli/"
ALLOWED_WHEEL_DIST_INFO_FILES = frozenset({"METADATA", "RECORD", "WHEEL", "top_level.txt", "entry_points.txt"})
ALLOWED_SDIST_FILES = frozenset(
{
@@ -88,6 +89,8 @@ def validate_wheel(path: Path, expected_version: str, package_files: set[str]) -
for dependency in REQUIRED_DEPENDENCIES:
if dependency not in dependencies:
fail(f"{path.name} is missing runtime dependency {dependency!r}")
+ if DOCUMENTATION_URL not in metadata.get_all("Project-URL", []):
+ fail(f"{path.name} is missing the canonical Documentation project URL")
if f"{IMPORT_NAME}/py.typed" not in names:
fail(f"{path.name} does not contain {IMPORT_NAME}/py.typed")
diff --git a/tests/validate.sh b/tests/validate.sh
index 8b63b60..925c781 100755
--- a/tests/validate.sh
+++ b/tests/validate.sh
@@ -8,7 +8,9 @@ required_files=(
CONTRIBUTING.md
.github/pull_request_template.md
.github/base-project.yml
+ .github/workflows/docs.yml
LICENSE
+ mkdocs.yml
base_manifest.yaml
.github/workflows/issue-branch-policy.yml
.github/workflows/project-intake.yml
@@ -18,6 +20,7 @@ required_files=(
.github/workflows/compatibility.yml
.github/ISSUE_TEMPLATE/support.md
docs/releasing.md
+ docs/index.md
docs/api-stability.md
docs/user-config-typing.md
docs/migrations.md
From 666141c66f67316306777450379a0d2dd93577c0 Mon Sep 17 00:00:00 2001
From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com>
Date: Wed, 5 Aug 2026 20:29:21 -0700
Subject: [PATCH 2/2] ci: configure Pages only during deployment
---
.github/workflows/docs.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 75e0118..1c86ab4 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -52,9 +52,6 @@ jobs:
- name: Validate repository links and examples
run: python scripts/validate_docs.py
- - name: Configure GitHub Pages
- uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0
-
- name: Build documentation with strict warnings
run: python -m mkdocs build --strict --site-dir site
@@ -76,6 +73,9 @@ jobs:
pages: write
id-token: write
steps:
+ - name: Configure GitHub Pages
+ uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0
+
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@f33f41b675f0ab2dc5a6863c9a170fe83af3571e # v4.0.0