Option C Tools
Loading...
Searching...
No Matches
oct.git.policy Namespace Reference

Functions

str|None generate_branch_name_from_message (str message)
bool is_protected_branch (str|None branch, dict|None octrc)
tuple[bool, str] validate_branch_name (str|None branch, dict|None octrc)
Profile _resolve_auto_profile (str|None branch)
Profile resolve_effective_profile (str|None branch, dict|None octrc, str|None cli_override)
int apply_profile_policy (str profile, "QualityGateResult" result)
tuple[bool, str] check_protected_branch_commit (str|None branch, dict|None octrc, str profile)
tuple[bool, str] validate_ignore_pattern (str pattern)
list[str] _read_gitignore (Path repo_root)
None _write_gitignore (Path repo_root, list[str] lines)
tuple[int, int] _find_oct_block (list[str] lines)
tuple[list[str], list[str]] list_ignore_patterns (Path repo_root)
list[str] add_ignore_patterns (Path repo_root, list[str] patterns)
tuple[bool, str] remove_ignore_pattern (Path repo_root, str pattern)

Variables

 Profile = Literal["proto", "compact", "strict"]
tuple VALID_PROFILES = ("proto", "compact", "strict")
Profile DEFAULT_PROFILE = "compact"
tuple DEFAULT_PROTECTED_BRANCHES = ("main", "master")
dict PROFILE_MATRIX
bool SECRETS_ALWAYS_BLOCKS = True
tuple DEFAULT_BRANCH_PATTERNS
dict _AUTO_PROFILE_MAP
dict _COMMIT_TYPE_TO_PREFIX
int _BRANCH_SLUG_MAX_LEN = 50
str OC_GITIGNORE_MARKER_START = "# --- Option C defaults ---"
str OC_GITIGNORE_MARKER_END = "# --- end Option C ---"
re _VALID_PATTERN_RE = re.compile(r"^[A-Za-z0-9_./*?\‍[\‍]\-]+$")

Detailed Description

Purpose
-------
Encode the Phase 4B profile-aware enforcement rules for the ``oct git``
quality gate. Maps a combination of profile (``proto`` / ``compact`` /
``strict``), branch context, and raw check findings to a semantic exit
code in the range 0..5 defined by blueprint §5.3.

Responsibilities
----------------
- Resolve the effective lint profile from CLI / octrc / branch context,
  honouring the protected-branch override (blueprint §7.2).
- Apply the profile enforcement matrix (blueprint §5.3 / §7.1) to a
  :class:`~oct.git.quality_gate.QualityGateResult` and return the
  matching exit code.
- Enforce the invariant "secrets always blocks" regardless of profile
  (blueprint §5.3 final clause).
- Manage the OCT-managed block in ``.gitignore`` — add/remove/list
  patterns inside the fenced block, leaving user-managed lines alone.

Diagnostics
-----------
Domain: GIT
Levels:
    L3 — profile resolution and policy decisions
    L4 — per-field raw counts during exit-code derivation

Contracts
---------
- This module is pure: no filesystem, no subprocess, no Click.
- ``resolve_effective_profile`` never raises; unknown values collapse
  to the documented default.
- ``apply_profile_policy`` depends only on the ``QualityGateResult``
  dataclass defined in :mod:`oct.git.quality_gate`.
- Precedence for the exit code is strictly: 5 > 4 > 3 > 2 > 1 > 0.

Function Documentation

◆ _find_oct_block()

tuple[int, int] oct.git.policy._find_oct_block ( list[str] lines)
protected
Return ``(start, end)`` line indices of the OCT block in *lines*.

``start`` is the index of the start marker, ``end`` the index of
the end marker (inclusive). Returns ``(-1, -1)`` if the block is
absent or malformed (start without end).

Definition at line 592 of file policy.py.

Here is the caller graph for this function:

◆ _read_gitignore()

list[str] oct.git.policy._read_gitignore ( Path repo_root)
protected
Return the lines of ``.gitignore`` (no trailing newlines).

Definition at line 575 of file policy.py.

Here is the caller graph for this function:

◆ _resolve_auto_profile()

Profile oct.git.policy._resolve_auto_profile ( str | None branch)
protected
Map *branch* to a profile using :data:`_AUTO_PROFILE_MAP`.

Exact matches (``main``, ``master``) are tried first, then prefix
matches (``feature/``, ``release/``). Returns :data:`DEFAULT_PROFILE`
for unrecognised patterns or ``None`` / ``"HEAD"`` branch.

Definition at line 316 of file policy.py.

Here is the caller graph for this function:

◆ _write_gitignore()

None oct.git.policy._write_gitignore ( Path repo_root,
list[str] lines )
protected
Write *lines* back to ``.gitignore`` with trailing newline.

Definition at line 583 of file policy.py.

Here is the caller graph for this function:

◆ add_ignore_patterns()

list[str] oct.git.policy.add_ignore_patterns ( Path repo_root,
list[str] patterns )
Append *patterns* to the OCT-managed block of ``.gitignore``.

Creates the block (with markers) if it does not exist. Patterns
already present anywhere in the file are skipped. Returns the list
of patterns actually added (in input order, after dedup).

Caller is responsible for validating each pattern via
:func:`validate_ignore_pattern` first.

Definition at line 644 of file policy.py.

Here is the call graph for this function:

◆ apply_profile_policy()

int oct.git.policy.apply_profile_policy ( str profile,
"QualityGateResult" result )
Derive the semantic exit code for a quality-gate result.

Exit code table (blueprint §5.3, verbatim):

==== ==========================================================
Code Meaning
==== ==========================================================
   0 all checks pass
   1 lint violations (blocking under this profile)
   2 format violations (blocking under this profile)
   3 both lint and format violations
   4 test failures (when ``--include-tests``)
   5 secrets detected — always blocking
==== ==========================================================

Precedence: ``5 > 4 > 3 > 2 > 1 > 0``. The first rule that matches
wins. Because secrets takes precedence, a profile that would
otherwise return 0 (e.g. ``proto`` with warnings-only lint) still
returns 5 if secrets are present. This is the "secrets always
blocks" invariant.

For profiles where a check is not blocking (``proto`` lint is
``warn``; ``proto`` format is ``skip``), the corresponding violation
counter never contributes to the exit code even if non-zero.

Parameters
----------
profile
    One of ``"proto"``, ``"compact"``, ``"strict"``. Unknown values
    degrade to :data:`DEFAULT_PROFILE`.
result
    A :class:`~oct.git.quality_gate.QualityGateResult` with the raw
    counts from a completed gate run.

Definition at line 419 of file policy.py.

◆ check_protected_branch_commit()

tuple[bool, str] oct.git.policy.check_protected_branch_commit ( str | None branch,
dict | None octrc,
str profile )
Check if committing directly to a protected branch.

Returns ``(blocked, message)``:

- On a **non-protected branch**: ``(False, "")``.
- On a protected branch in ``strict`` profile: ``(True, error_msg)``.
- On a protected branch in other profiles: ``(False, warning_msg)``.

The caller is responsible for printing the message and aborting if
``blocked`` is ``True``.

Definition at line 497 of file policy.py.

Here is the call graph for this function:

◆ generate_branch_name_from_message()

str | None oct.git.policy.generate_branch_name_from_message ( str message)
Derive a valid branch name from a Conventional Commits *message*.

Returns a branch name like ``feature/auth-add-jwt-tokens`` or
``None`` when the message cannot be parsed.

The generated name is guaranteed to pass
:func:`validate_branch_name` against the default patterns.

Definition at line 156 of file policy.py.

Here is the call graph for this function:

◆ is_protected_branch()

bool oct.git.policy.is_protected_branch ( str | None branch,
dict | None octrc )
Return True if ``branch`` is listed in ``git.protected_branches``.

A missing ``octrc`` or a missing ``git.protected_branches`` key
falls back to :data:`DEFAULT_PROTECTED_BRANCHES`. Detached HEAD
(``branch is None``) is never considered protected — detached
workflows are per-definition outside branch policy.

Definition at line 214 of file policy.py.

Here is the caller graph for this function:

◆ list_ignore_patterns()

tuple[list[str], list[str]] oct.git.policy.list_ignore_patterns ( Path repo_root)
Return ``(oct_managed, other)`` patterns from ``.gitignore``.

Patterns inside the OCT-managed block are returned in the first
list; everything else (user-managed) is returned in the second.
Comment lines and blank lines are excluded from both.

Definition at line 613 of file policy.py.

Here is the call graph for this function:

◆ remove_ignore_pattern()

tuple[bool, str] oct.git.policy.remove_ignore_pattern ( Path repo_root,
str pattern )
Remove *pattern* from the OCT-managed block.

Returns ``(removed, message)``. Refuses to touch patterns located
outside the OCT block (those are user-managed); the caller should
surface the message to the user.

Definition at line 687 of file policy.py.

Here is the call graph for this function:

◆ resolve_effective_profile()

Profile oct.git.policy.resolve_effective_profile ( str | None branch,
dict | None octrc,
str | None cli_override )
Return the profile that should be applied for the current run.

Precedence (highest wins):

1. **Protected-branch override** — if ``branch`` is listed in the
   project's protected-branches set, the result is always
   ``"strict"``. This cannot be overridden by CLI or config.
2. **CLI ``--profile``** — explicit user override from the command
   line. Must be one of :data:`VALID_PROFILES`.
3. **``.octrc.json → git.pre_commit_profile``** — the persistent
   per-project default.
4. **``.octrc.json → linter.profile``** — if the git-specific key
   is absent, fall back to the linter-wide profile.
5. **:data:`DEFAULT_PROFILE`** — final fallback if nothing above
   is set.

Unknown profile strings (from any source) are silently discarded in
favour of the next lower level of precedence. Malformed ``octrc``
values are also ignored — never raise.

Definition at line 343 of file policy.py.

Here is the call graph for this function:

◆ validate_branch_name()

tuple[bool, str] oct.git.policy.validate_branch_name ( str | None branch,
dict | None octrc )
Validate *branch* against the configured naming patterns.

Returns ``(valid, message)``:

- ``git.branch_naming.enabled`` is ``False`` → ``(True, "")``
- ``branch`` is ``None`` (detached HEAD) → ``(True, "")``
- Branch matches at least one pattern → ``(True, "")``
- No pattern matches → ``(False, "Branch name '...' does not ...")``

Patterns default to :data:`DEFAULT_BRANCH_PATTERNS` when not
overridden in ``.octrc.json``.

Definition at line 249 of file policy.py.

Here is the caller graph for this function:

◆ validate_ignore_pattern()

tuple[bool, str] oct.git.policy.validate_ignore_pattern ( str pattern)
Return ``(valid, message)`` for a candidate gitignore pattern.

Rejects empty strings, patterns containing newlines, leading ``!``
(negation — too easy to weaponise via ``oct git ignore``), absolute
paths (leading ``/`` is interpreted as anchored to repo root which
is allowed by gitignore but our policy is to require relative
patterns), and any character outside :data:`_VALID_PATTERN_RE`.

Definition at line 546 of file policy.py.

Variable Documentation

◆ _AUTO_PROFILE_MAP

dict oct.git.policy._AUTO_PROFILE_MAP
protected
Initial value:
1= {
2 "main": "strict",
3 "master": "strict",
4 "release/": "strict",
5 "hotfix/": "strict",
6 "feature/": "compact",
7 "bugfix/": "compact",
8 "docs/": "compact",
9 "chore/": "compact",
10}

Definition at line 122 of file policy.py.

◆ _BRANCH_SLUG_MAX_LEN

int oct.git.policy._BRANCH_SLUG_MAX_LEN = 50
protected

Definition at line 148 of file policy.py.

◆ _COMMIT_TYPE_TO_PREFIX

dict oct.git.policy._COMMIT_TYPE_TO_PREFIX
protected
Initial value:
1= {
2 "feat": "feature",
3 "fix": "bugfix",
4 "docs": "docs",
5 "style": "chore",
6 "refactor": "chore",
7 "perf": "feature",
8 "test": "chore",
9 "chore": "chore",
10 "sec": "hotfix",
11}

Definition at line 135 of file policy.py.

◆ _VALID_PATTERN_RE

re oct.git.policy._VALID_PATTERN_RE = re.compile(r"^[A-Za-z0-9_./*?\‍[\‍]\-]+$")
protected

Definition at line 543 of file policy.py.

◆ DEFAULT_BRANCH_PATTERNS

tuple oct.git.policy.DEFAULT_BRANCH_PATTERNS
Initial value:
1= (
2 r"^(main|master|develop)$",
3 r"^(release|hotfix|feature|bugfix|docs|chore)/.+$",
4)

Definition at line 114 of file policy.py.

◆ DEFAULT_PROFILE

Profile oct.git.policy.DEFAULT_PROFILE = "compact"

Definition at line 69 of file policy.py.

◆ DEFAULT_PROTECTED_BRANCHES

tuple oct.git.policy.DEFAULT_PROTECTED_BRANCHES = ("main", "master")

Definition at line 74 of file policy.py.

◆ OC_GITIGNORE_MARKER_END

str oct.git.policy.OC_GITIGNORE_MARKER_END = "# --- end Option C ---"

Definition at line 538 of file policy.py.

◆ OC_GITIGNORE_MARKER_START

str oct.git.policy.OC_GITIGNORE_MARKER_START = "# --- Option C defaults ---"

Definition at line 537 of file policy.py.

◆ Profile

oct.git.policy.Profile = Literal["proto", "compact", "strict"]

Definition at line 60 of file policy.py.

◆ PROFILE_MATRIX

dict oct.git.policy.PROFILE_MATRIX
Initial value:
1= {
2 "proto": {
3 "lint": "warn",
4 "format": "skip",
5 "secrets": "blocking",
6 "tests": "skip",
7 },
8 "compact": {
9 "lint": "blocking",
10 "format": "blocking",
11 "secrets": "blocking",
12 "tests": "optional",
13 },
14 "strict": {
15 "lint": "blocking",
16 "format": "blocking",
17 "secrets": "blocking",
18 "tests": "blocking",
19 },
20}

Definition at line 85 of file policy.py.

◆ SECRETS_ALWAYS_BLOCKS

bool oct.git.policy.SECRETS_ALWAYS_BLOCKS = True

Definition at line 109 of file policy.py.

◆ VALID_PROFILES

tuple oct.git.policy.VALID_PROFILES = ("proto", "compact", "strict")

Definition at line 64 of file policy.py.