oct
Loading...
Searching...
No Matches
workspace Namespace Reference

Classes

class  WorkspaceError
class  Subproject
class  Workspace

Functions

tuple[bool, bool, bool] _parse_html_docs (object raw, int idx)
Workspace load_workspace (Path root)
Path|None find_workspace_root (Path start)
tuple[bool, str] _validate_workspace_pattern (object pattern)
re.Pattern[str] _glob_to_regex (str pattern)
bool matches_workspace_files (str rel_path, tuple[str,...] patterns)
Subproject|None assign_to_subproject (Path file_path, Workspace workspace)

Variables

str WORKSPACE_MANIFEST = ".option_c_workspace.json"
str _SCHEMA_VERSION = "1.0"
tuple _VALID_PROFILES = ("proto", "compact", "strict")
str _DEFAULT_WORKSPACE_PROFILE = "compact"
tuple _HTML_DOCS_KEYS = ("hub", "doxygen")
re _VALID_WORKSPACE_PATTERN_RE

Detailed Description

Purpose
-------
Workspace (monorepo) support for OCT (F1, leanest first cut). A
workspace is a directory containing a ``.option_c_workspace.json``
manifest and one or more Option C subprojects. ``oct git`` commands
invoked from a workspace root may operate on the entire repo without
the ``--root-dir <subproj> --allow-outside-project`` escape hatch
required pre-F1.

Responsibilities
----------------
- Define :class:`Workspace` - the loaded representation of the
  manifest.
- Provide :func:`load_workspace` - read and validate the manifest at
  ``<root>/.option_c_workspace.json``.
- Provide :func:`find_workspace_root` - walk upward from a starting
  directory to locate the nearest workspace manifest.
- Be intentionally conservative: invalid manifests raise
  :class:`WorkspaceError` rather than degrading silently.

Diagnostics
-----------
Domain: WORKSPACE
Levels:
    L1 - errors (invalid manifest, IO failure)
    L2 - lifecycle (load start / end)
    L3 - details (resolved subproject paths)
    L4 - verbose tracing (workspace-root walk misses)

Contracts
---------
- This module imports nothing from ``oct.git`` or ``oct.cli``; it sits
  beneath them in the dependency graph.
- Manifest schema is JSON-only (no TOML); the file is small enough
  that adding a TOML parser is unwarranted.
- Subproject paths in the manifest are interpreted relative to the
  workspace root. Absolute paths in the manifest are rejected.
- F1 fan-out (v0.25.0): the manifest now also carries
  ``workspace_profile`` (default ``"compact"``), ``workspace_files``
  (gitignore-style globs of files owned by the workspace itself
  rather than any subproject), and ``ignore_at_workspace_scope``
  (extra ignore patterns applied at the workspace ``.gitignore``).
  All three are optional with documented defaults.
- Plan 209: a subproject entry's ``nested_git`` must be a JSON boolean
  or omitted (default ``False``); any other type raises
  :class:`WorkspaceError` rather than being silently coerced, the same
  strictness already applied to ``profile``.
- 2026-09-26: a subproject entry may carry ``html_docs`` to declare its
  participation in the generated docs site. It is the ONLY control over
  which subprojects ``oct docs`` builds and cards; there is deliberately
  no workspace-level exclusion pattern list, so precedence between two
  competing declarations can never arise. Accepted shapes are a boolean
  or the object ``{"hub": <bool>}``; anything else raises
  :class:`WorkspaceError`.
- 2026-09-28: the ``html_docs`` object also accepts ``"doxygen": <bool>``
  (default ``True``), which decides whether the subproject's Doxygen
  output is part of the generated docs site. It is stored as
  :attr:`Subproject.html_docs_doxygen`; ``html_docs: false`` forces it
  ``False``. It governs only the workspace fan-out: a direct
  ``oct docs --doxygen`` inside the subproject still builds. Unknown keys
  are still rejected, and the message names every supported key.

Dependencies
------------
- oct.core.diagnostics (_dbg structured logger)

Function Documentation

◆ _glob_to_regex()

re.Pattern[str] workspace._glob_to_regex ( str pattern)
protected
Compile a gitignore-style glob to a regex.

Supports::

    *   matches any sequence of chars within one path segment (no /)
    **  matches across path segments, including /
    ?   matches a single non-/ char
    []  literal char class (treated character-by-character)

Patterns are anchored: the resulting regex must match the entire
relative path. Caching is global and unbounded - the working set
of patterns per session is small and stable.

Definition at line 537 of file workspace.py.

Here is the caller graph for this function:

◆ _parse_html_docs()

tuple[bool, bool, bool] workspace._parse_html_docs ( object raw,
int idx )
protected
Parse a subproject's ``html_docs`` declaration.

Parameters
----------
raw
    The raw manifest value, or ``None`` when the key is absent.
idx
    Index of the owning ``subprojects[]`` entry, used only to make
    the error message point at the offending entry.

Returns
-------
tuple[bool, bool, bool]
    ``(html_docs, html_docs_hub, html_docs_doxygen)``.

Raises
------
WorkspaceError
    On any shape other than ``None``, a boolean, or an object whose
    keys are drawn from :data:`_HTML_DOCS_KEYS` and each mapped to a
    boolean. Rejecting unknown keys rather than ignoring them means a
    typo such as ``{"hubb": false}`` fails loudly instead of quietly
    leaving the card in place, which is the failure mode the operator
    would have had to notice by eye on the rendered page.

Definition at line 206 of file workspace.py.

Here is the caller graph for this function:

◆ _validate_workspace_pattern()

tuple[bool, str] workspace._validate_workspace_pattern ( object pattern)
protected
Return ``(valid, message)`` for a workspace_files / ignore pattern.

Mirrors ``oct.git.policy.validate_ignore_pattern`` so the manifest
surface is held to the same safety bar as ``oct git ignore``: no
negation, no absolute paths, no shell metacharacters, no newlines.

Definition at line 509 of file workspace.py.

Here is the caller graph for this function:

◆ assign_to_subproject()

Subproject | None workspace.assign_to_subproject ( Path file_path,
Workspace workspace )
Return the subproject containing *file_path*, or ``None``.

Uses longest-path-prefix match so nested subprojects (rare)
resolve to the deepest match. The returned :class:`Subproject` is
one of ``workspace.subprojects`` (identity-comparable). Returns
``None`` when *file_path* falls outside every subproject - the
caller then decides whether the file matches *workspace_files*
or should be dropped.

Performance: O(N subprojects) per call; N is bounded by manifest
size and is small in practice.

Definition at line 596 of file workspace.py.

◆ find_workspace_root()

Path | None workspace.find_workspace_root ( Path start)
Walk upward from *start* to locate a workspace manifest.

Returns the workspace root, or ``None`` when no manifest is found
by the time the filesystem root is reached.

Definition at line 480 of file workspace.py.

◆ load_workspace()

Workspace workspace.load_workspace ( Path root)
Load and validate the workspace manifest at ``<root>``.

Parameters
----------
root
    Path to the workspace root (the directory containing
    ``.option_c_workspace.json``).

Returns
-------
:class:`Workspace`

Raises
------
WorkspaceError
    If the manifest is missing, malformed, or contains invalid
    subproject paths.

Definition at line 278 of file workspace.py.

Here is the call graph for this function:

◆ matches_workspace_files()

bool workspace.matches_workspace_files ( str rel_path,
tuple[str, ...] patterns )
Return True if *rel_path* matches any *patterns* glob.

*rel_path* must be a forward-slash POSIX-style relative path
(e.g. ``"docs/README.md"``). Backslashes - common on Windows -
must be normalised by the caller.

Definition at line 578 of file workspace.py.

Here is the call graph for this function:

Variable Documentation

◆ _DEFAULT_WORKSPACE_PROFILE

str workspace._DEFAULT_WORKSPACE_PROFILE = "compact"
protected

Definition at line 95 of file workspace.py.

◆ _HTML_DOCS_KEYS

tuple workspace._HTML_DOCS_KEYS = ("hub", "doxygen")
protected

Definition at line 100 of file workspace.py.

◆ _SCHEMA_VERSION

str workspace._SCHEMA_VERSION = "1.0"
protected

Definition at line 88 of file workspace.py.

◆ _VALID_PROFILES

tuple workspace._VALID_PROFILES = ("proto", "compact", "strict")
protected

Definition at line 91 of file workspace.py.

◆ _VALID_WORKSPACE_PATTERN_RE

re workspace._VALID_WORKSPACE_PATTERN_RE
protected
Initial value:
1= re.compile(
2 r"^[A-Za-z0-9_./*?\‍[\‍]\-]+$",
3)

Definition at line 504 of file workspace.py.

◆ WORKSPACE_MANIFEST

str workspace.WORKSPACE_MANIFEST = ".option_c_workspace.json"

Definition at line 85 of file workspace.py.