diff --git a/CLAUDE.md b/CLAUDE.md index 81b73c6..44f8caa 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -76,6 +76,7 @@ bundle exec jekyll serve --watch # Browse to http://localhost:4000 # Full CI-equivalent build (build + htmlproofer + prod rebuild) +# Output goes to _site/ ./build.sh ``` diff --git a/articles/_posts/2026-04-01-project-info-json-dump.md b/articles/_posts/2026-04-01-project-info-json-dump.md new file mode 100644 index 0000000..9122b72 --- /dev/null +++ b/articles/_posts/2026-04-01-project-info-json-dump.md @@ -0,0 +1,39 @@ +--- +layout: post +title: "New Feature: pyb --project-info for Machine-Readable Project Configuration" +author: arcivanov +categories: news +--- + +PyBuilder now supports dumping the full project configuration as machine-readable +JSON, without running a build. Use `pyb -i` or `pyb --project-info` to inspect +everything PyBuilder knows about your project after loading plugins and running +initializers. + +**Clean stdout/stderr separation.** JSON goes to stdout, log messages go to +stderr. This means you can pipe the output directly into `jq`, `python -m +json.tool`, or any JSON consumer: + +```bash +# Get the project name +pyb -i 2>/dev/null | jq .project.name + +# Inspect all properties with environment and overrides applied +pyb -i -E ci -P verbose=true 2>/dev/null | jq .properties + +# Feed into CI scripts +PROJECT_VERSION=$(pyb -i 2>/dev/null | jq -r .project.version) +``` + +**What's included.** The JSON output covers: project metadata (name, version, +authors, license, URLs), all build properties (built-in and plugin-defined), +loaded plugins, runtime/build/plugin/extras dependencies, available tasks with +their descriptions and dependency graphs, manifest files, and package data. + +**No side effects.** Plugin initializers run to populate properties, but no tasks +execute, no venvs are created, and no dependencies are installed. It is safe and +fast to run in any context. + +See the [usage documentation](/documentation/manual.html#inspecting-project-configuration) +for details, or the [dedicated project-info page](/documentation/project-info.html) +for the full JSON schema reference and integration examples. diff --git a/articles/_release-notes/v0.13.x.md b/articles/_release-notes/v0.13.x.md index d48c013..19158b9 100644 --- a/articles/_release-notes/v0.13.x.md +++ b/articles/_release-notes/v0.13.x.md @@ -6,6 +6,15 @@ list_title: Versions 0.13.x # Release Notes - Versions 0.13.x +## Version 0.13.20 + +### New Features +* `pyb -i` / `pyb --project-info` — outputs the full project configuration as + pretty-printed JSON without running a build. Runs plugin initializers to + populate all properties but does not execute any tasks or create venvs. + JSON goes to stdout; log messages go to stderr so the output is + machine-parseable. + ## Version 0.13.19 ### New Features diff --git a/documentation/coding-agents.md b/documentation/coding-agents.md index 115ff82..7f86b99 100644 --- a/documentation/coding-agents.md +++ b/documentation/coding-agents.md @@ -41,6 +41,9 @@ pyb clean package # List available tasks pyb -t +# Dump project configuration as JSON (no build, stdout is clean JSON) +pyb -i 2>/dev/null | jq . + # Set a property from the command line pyb -P property_name=value ``` @@ -218,6 +221,7 @@ This project uses PyBuilder. All build commands use `pyb`: - `pyb` — full build - `pyb run_unit_tests` — unit tests only - `pyb run_integration_tests` — integration tests only +- `pyb -i` — dump project configuration as JSON (no build) - `pyb -v` — verbose output - `pyb -vX` — debug verbose output ``` @@ -268,6 +272,7 @@ This project uses PyBuilder. All build commands use `pyb`: - `pyb` — full build (test + analyze + package) - `pyb run_unit_tests` — unit tests only - `pyb run_integration_tests` — integration tests only +- `pyb -i` — dump project configuration as JSON (no build) - `pyb -v` — verbose output - `pyb -vX` — debug verbose output diff --git a/documentation/index.md b/documentation/index.md index 76458c9..996e330 100644 --- a/documentation/index.md +++ b/documentation/index.md @@ -15,6 +15,8 @@ title: PyBuilder Documentation ### [Manual]({% link documentation/manual.md %}) +### [Project Info (JSON Dump)]({% link documentation/project-info.md %}) + ### [Plugins]({% link documentation/plugins.md %}) ### [External Plugin List]({% link documentation/external-plugin-list.md %}) diff --git a/documentation/manual.md b/documentation/manual.md index 6db5049..1f121d6 100644 --- a/documentation/manual.md +++ b/documentation/manual.md @@ -215,6 +215,33 @@ This command sets/ overrides the property with the name ```spam``` with the valu Note that command line switches only allow properties to be set/ overridden using string values. +#### Inspecting Project Configuration + +Use `pyb -i` (or `pyb --project-info`) to dump the full project configuration as +pretty-printed JSON without running a build. This runs all plugin initializers to +populate properties but does not execute any tasks or create build/test venvs. + +JSON is written to stdout and all log messages go to stderr, so the output is +safe to pipe into other tools: + +
+$ pyb -i 2>/dev/null | python -m json.tool
+$ pyb -i 2>/dev/null | jq .project.name
+$ pyb -i -E ci -P verbose=true 2>/dev/null | jq .properties
+
+ +The JSON output includes: + +* **project** — name, version, basedir, summary, authors, license, URLs, etc. +* **properties** — all build properties (built-in and plugin-defined) +* **plugins** — list of loaded plugins +* **dependencies** — runtime, build, plugin, and extras dependencies +* **tasks** — available tasks with descriptions and dependency information +* **manifest_included_files**, **package_data**, **files_to_install** + +This is useful for CI/CD pipelines, editor integrations, and debugging +property values without running a full build. + ## Virtual Environment Infrastructure *PyBuilder* manages isolated Python virtual environments for building and testing. diff --git a/documentation/project-info.md b/documentation/project-info.md new file mode 100644 index 0000000..865ecfc --- /dev/null +++ b/documentation/project-info.md @@ -0,0 +1,212 @@ +--- +layout: documentation +title: Project Info — JSON Configuration Dump +--- + +# Project Info — JSON Configuration Dump + +The `pyb -i` (or `pyb --project-info`) command outputs the complete project +configuration as pretty-printed JSON to stdout, without running a build. + +## Quick Start + +```bash +# Dump full project configuration +pyb -i 2>/dev/null | python -m json.tool + +# Extract a single value +pyb -i 2>/dev/null | jq -r .project.version + +# Apply environment and property overrides +pyb -i -E ci -P coverage_break_build=false 2>/dev/null | jq .properties +``` + +## Output Separation + +JSON is written to **stdout**. All log messages (plugin loading, initializer +execution, debug output) are written to **stderr**. This separation ensures +that the stdout stream is always valid JSON, regardless of verbosity level: + +```bash +# JSON only +pyb -i 2>/dev/null + +# JSON to file, logs visible +pyb -i > project.json + +# JSON piped, debug logs to a file +pyb -i -X 2>debug.log | jq . + +# Both visible (JSON to stdout, logs to stderr) +pyb -i +``` + +## What Runs + +When `pyb -i` is invoked: + +1. **`build.py` is loaded** — plugins are imported, project attributes are set. +2. **Plugin initializers run** — these populate default property values (e.g. + `dir_source_main_python`, `coverage_threshold_warn`). Initializers only call + `set_property_if_unset()` and bind utility functions; they do not create + directories, install packages, or modify the filesystem. +3. **Property overrides are applied** — values from `-P` flags override + plugin defaults. + +**What does NOT run:** No tasks execute. No build/test virtual environments are +created. No dependencies are installed. No files are written to `target/`. + +## JSON Schema + +The output is a single JSON object with the following top-level keys: + +### `pybuilder_version` + +The PyBuilder version string. + +### `project` + +Project metadata: + +| Field | Type | Description | +|-------|------|-------------| +| `name` | string | Project name (from `build.py` or directory name) | +| `version` | string | Declared version (e.g. `"1.0.dev"`) | +| `dist_version` | string | Distribution version with timestamp (e.g. `"1.0.dev20260401120000"`) | +| `basedir` | string | Absolute path to project root | +| `summary` | string | One-line project summary | +| `description` | string | Full project description | +| `author` | string | Primary author (legacy field) | +| `authors` | array | List of author objects with `name`, `email`, and optional `roles` | +| `maintainer` | string | Primary maintainer (legacy field) | +| `maintainers` | array | List of maintainer objects | +| `license` | string | License identifier | +| `url` | string | Primary project URL | +| `urls` | object | Named URL map (e.g. `{"Source Code": "https://..."}`) | +| `requires_python` | string | Python version specifier (e.g. `">=3.10"`) | +| `default_task` | string or array | Default task(s) when `pyb` is run without arguments | +| `obsoletes` | array | Obsoleted package names | +| `explicit_namespaces` | array | Explicit namespace packages | + +### `environments` + +Array of active environment names (from `-E` flags). + +### `properties` + +Object mapping property names to their values. Includes all built-in and +plugin-defined properties. Values that are not JSON-serializable (e.g. function +objects) are converted to their string representation. + +### `plugins` + +Array of loaded plugin names in load order. + +### `dependencies` + +Object with four sub-keys: + +| Key | Contents | +|-----|----------| +| `runtime` | Dependencies from `depends_on()` | +| `build` | Dependencies from `build_depends_on()` | +| `plugin` | Dependencies from `plugin_depends_on()` | +| `extras` | Object mapping extra name to dependency array | + +Each dependency is an object: + +```json +{ + "name": "requests", + "version": ">=2.28", + "url": null, + "extras": null, + "markers": "sys_platform == 'linux'", + "declaration_only": false, + "type": "dependency" +} +``` + +Requirements files have `"type": "requirements_file"` and only `name`, +`version` (always null), and `declaration_only` fields. + +### `tasks` + +Array of task objects: + +```json +{ + "name": "run_unit_tests", + "description": "Runs all unit tests", + "dependencies": [ + {"name": "compile_sources", "optional": false}, + {"name": "coverage", "optional": true} + ] +} +``` + +### `manifest_included_files` + +Array of glob patterns included in the distribution manifest. + +### `package_data` + +Object mapping package names to arrays of included file patterns. + +### `files_to_install` + +Array of `[destination, [filenames]]` pairs for files installed outside packages. + +## Integration Examples + +### CI/CD: Extract Version for Tagging + +```bash +VERSION=$(pyb -i 2>/dev/null | jq -r .project.dist_version) +docker build -t myapp:$VERSION . +``` + +### Dependency Auditing + +```bash +# List all runtime dependencies +pyb -i 2>/dev/null | jq -r '.dependencies.runtime[].name' + +# Find dependencies without version constraints +pyb -i 2>/dev/null | jq '.dependencies.runtime[] | select(.version == null) | .name' +``` + +### Editor/IDE Integration + +```bash +# Get source directory for language server configuration +SRC=$(pyb -i 2>/dev/null | jq -r '.properties.dir_source_main_python') + +# Get test directory +TEST=$(pyb -i 2>/dev/null | jq -r '.properties.dir_source_unittest_python') +``` + +### Diffing Configuration Between Environments + +```bash +diff <(pyb -i 2>/dev/null | jq -S .properties) \ + <(pyb -i -E ci 2>/dev/null | jq -S .properties) +``` + +## Command Line Options + +`pyb -i` is compatible with most project options: + +| Flag | Effect | +|------|--------| +| `-E ` | Activate environment (repeatable) | +| `-P =` | Override property value | +| `-D ` | Set project directory | +| `-O` | Offline mode | +| `--no-venvs` | Disable venv creation | +| `-X` | Debug log output (to stderr) | +| `-v` | Verbose log output (to stderr) | +| `-q` / `-Q` | Suppress log output (on stderr) | + +`pyb -i` is mutually exclusive with `-t`, `-T`, `--start-project`, and +`--update-project`. diff --git a/documentation/tutorial.md b/documentation/tutorial.md index 35ba79b..345c99d 100644 --- a/documentation/tutorial.md +++ b/documentation/tutorial.md @@ -580,6 +580,41 @@ The distribution directory contains the same sources but in a Python-typical dir You can also find the `setup.py` script there, as well as generated binary wheel and sdist gzip'ed tar in `target/dist/dist`. +## Inspecting Project Configuration + +At any point you can inspect the full project configuration as JSON without +running a build: + +``` +$ pyb -i 2>/dev/null | python -m json.tool +{ + "pybuilder_version": "0.13.20", + "project": { + "name": "helloworld", + "version": "1.0.dev0", + ... + }, + "properties": { + "dir_source_main_python": "src/main/python", + "coverage_threshold_warn": 70, + ... + }, + "plugins": [ + "python.core", + "python.unittest", + "python.coverage", + "python.distutils" + ], + "tasks": [ ... ], + ... +} +``` + +This is useful for verifying that properties are set correctly or for feeding +project metadata into CI/CD scripts. See the +[project-info documentation](/documentation/project-info.html) for the full +JSON schema and integration examples. + ## Recap In this tutorial we saw how PyBuilder can be used to "build" a typical Python project. Building in an interpreted