Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ Review mode does not need `contents: write`: PR-specific generated files are sto
| `github_token` | both | `${{ github.token }}` | Token for GitHub API calls; in review mode it posts or updates the PR comment. |
| `push_token` | sync | `${{ github.token }}` | Token used for sync-mode pushes to `target_branch`. The workflow token can push when the workflow grants `permissions: contents: write`. Separate from `github_token` so commenting can use a GitHub App token while the push uses the workflow token. |
| `codeboarding_version` | both | `0.12.3` | CodeBoarding PyPI package version used as the analysis engine. Pin for reproducibility. |
| `depth_level` | both | empty (`2` for cold starts) | Analysis depth, 1 to 3, used for first analysis and `force_full` rebuilds. Once `.codeboarding/analysis.json` exists, its `metadata.depth_level` is the source of truth for incremental analysis and fallback-full recovery. |
| `depth_level` | both | empty (`2` for cold starts) | Analysis depth for first analysis and `force_full` rebuilds. Max depends on tier: **3** on the free hosted tier, **10** with a CodeBoarding license or your own `llm_api_key`. Once `.codeboarding/analysis.json` exists, its `metadata.depth_level` is the source of truth: sync runs incremental at the baseline depth, and review analyzes the PR head at the committed baseline depth so the diff is apples-to-apples (clamped to the tier max). |
| `render_depth` | review | `1` | Display depth for the PR diagram. Keep `1` for a clean top-level view. |
| `diagram_direction` | review | `LR` | Mermaid direction: `LR`, `TD`, `TB`, `RL`, or `BT`. |
| `changed_only` | review | `false` | Render only changed components and incident edges. |
Expand Down
102 changes: 80 additions & 22 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ inputs:
required: false
default: '0.12.3'
depth_level:
description: 'Analysis depth (1-3) for cold-start or force_full rebuilds. Once .codeboarding/analysis.json exists, its metadata.depth_level is the source of truth for incremental analysis and fallback-full recovery. Empty (default): 2 for cold starts.'
description: 'Analysis depth for cold-start or force_full rebuilds. Max depends on tier: 3 on the free hosted tier, 10 with a CodeBoarding license or your own llm_api_key. Once .codeboarding/analysis.json exists, its metadata.depth_level is the source of truth: sync runs incremental at the baseline depth, and review analyzes the PR head at the committed baseline depth so the diff is apples-to-apples (clamped to the tier max). Empty (default): 2 for cold starts.'
required: false
default: ''
agent_model:
Expand Down Expand Up @@ -209,9 +209,12 @@ runs:
review|sync) ;;
*) echo "::error::mode must be 'review' or 'sync' (got '$MODE')."; exit 1 ;;
esac
# Structural range only (1-10). The per-tier ceiling (free=3, licensed=10)
# is enforced later in 'Resolve analysis depth', once the credential mode
# is known, with a message pointing at the license/BYO-key upgrade path.
case "$DEPTH" in
''|1|2|3) ;;
*) echo "::error::depth_level must be 1, 2, or 3 (empty = default cold-start depth 2)."; exit 1 ;;
''|1|2|3|4|5|6|7|8|9|10) ;;
*) echo "::error::depth_level must be an integer from 1 to 10 (empty = default cold-start depth 2; the usable max depends on your tier)."; exit 1 ;;
esac
echo "mode=$MODE" >> "$GITHUB_OUTPUT"

Expand Down Expand Up @@ -468,25 +471,6 @@ runs:
git cat-file -e "$BASE_SHA" && echo "Target branch commit reachable." || \
(echo "::error::Target branch commit $BASE_SHA is not reachable." && exit 1)

- name: Resolve analysis depth
id: resolve_depth
if: steps.guard.outputs.skip != 'true'
shell: bash
env:
INPUT_DEPTH: ${{ inputs.depth_level }}
run: |
set -euo pipefail
# Explicit input controls cold-start/force_full rebuilds. Existing
# analysis.json metadata is the source of truth for incremental runs.
if [ -n "$INPUT_DEPTH" ]; then
echo "depth=$INPUT_DEPTH" >> "$GITHUB_OUTPUT"
echo "Using explicit depth_level=$INPUT_DEPTH."
exit 0
fi
DEPTH=2
echo "depth=$DEPTH" >> "$GITHUB_OUTPUT"
echo "Using default cold-start depth_level=$DEPTH."

- name: Set up Python 3.12
if: steps.guard.outputs.skip != 'true'
uses: actions/setup-python@v5
Expand Down Expand Up @@ -704,6 +688,70 @@ runs:
echo "cache_parsing_model=$(_safe "${PARSING_MODEL:-default}")"
} >> "$GITHUB_OUTPUT"

# Runs AFTER the LLM-key step so the credential mode (byokey/license/oidc) is
# known: it sets the per-tier depth ceiling (free = 3, licensed/BYO = 10). The
# CodeBoarding package is installed by now, so baseline-depth runs against the
# set-up python (stdlib-only parse either way).
- name: Resolve analysis depth
id: resolve_depth
if: steps.guard.outputs.skip != 'true'
shell: bash
working-directory: target-repo
env:
INPUT_DEPTH: ${{ inputs.depth_level }}
MODE: ${{ steps.guard.outputs.mode }}
BASE_SHA: ${{ steps.guard.outputs.base_sha }}
EMPTY_BASE: ${{ steps.guard.outputs.empty_base }}
ACTION_PATH: ${{ github.action_path }}
CRED_MODE: ${{ steps.llm.outputs.mode }}
run: |
set -euo pipefail
# Licensed = BYO key or a CodeBoarding license; the free OIDC tier is capped lower.
if [ "$CRED_MODE" = "byokey" ] || [ "$CRED_MODE" = "license" ]; then
LICENSED_FLAG="--licensed"; MAX_DEPTH=10; TIER="licensed"
else
LICENSED_FLAG=""; MAX_DEPTH=3; TIER="free-tier"
fi
# Explicit input controls cold-start/force_full rebuilds. It is capped to the
# tier ceiling (an over-cap request is a clear error, not a silent clamp).
if [ -n "$INPUT_DEPTH" ]; then
if [ "$INPUT_DEPTH" -gt "$MAX_DEPTH" ]; then
echo "::error::depth_level=$INPUT_DEPTH exceeds the $TIER maximum of $MAX_DEPTH. Use a CodeBoarding license or your own llm_api_key for deeper analysis."
exit 1
fi
echo "depth=$INPUT_DEPTH" >> "$GITHUB_OUTPUT"
echo "Using explicit depth_level=$INPUT_DEPTH (max $MAX_DEPTH for $TIER)."
exit 0
fi
# Review against a committed baseline: analyze the PR head at the SAME
# depth the committed .codeboarding/analysis.json was generated with, so
# head and base diff apples-to-apples. Defaulting to a shallower depth
# would make validate-base reject the deeper baseline, force the action to
# regenerate a shallower base, and (because the artifact then carries no
# committed base SHA) leave the webview diffing the deeper committed base
# against the shallower head — reporting phantom "deleted" components.
# baseline-depth clamps the inherited value to the tier ceiling and only
# parses the committed JSON (stdlib).
if [ "$MODE" = "review" ] && [ "$EMPTY_BASE" != "true" ] && [ -n "$BASE_SHA" ]; then
BASE_ANALYSIS="$(mktemp)"
if git show "${BASE_SHA}:.codeboarding/analysis.json" > "$BASE_ANALYSIS" 2>/dev/null; then
INHERITED="$(python3 "$ACTION_PATH/scripts/engine_adapter.py" baseline-depth --analysis "$BASE_ANALYSIS" $LICENSED_FLAG | sed -n 's/^depth_level=//p')"
rm -f "$BASE_ANALYSIS"
if [ -n "$INHERITED" ]; then
echo "depth=$INHERITED" >> "$GITHUB_OUTPUT"
echo "Inheriting committed baseline depth_level=$INHERITED for the PR-head analysis (max $MAX_DEPTH for $TIER)."
exit 0
fi
echo "Committed baseline has no usable depth_level; using default cold-start depth."
else
rm -f "$BASE_ANALYSIS"
echo "No committed baseline at ${BASE_SHA}; using default cold-start depth."
fi
fi
DEPTH=2
echo "depth=$DEPTH" >> "$GITHUB_OUTPUT"
echo "Using default cold-start depth_level=$DEPTH."

- name: Resolve base analysis (committed baseline)
if: steps.guard.outputs.skip != 'true' && steps.guard.outputs.mode == 'review'
id: base
Expand Down Expand Up @@ -933,6 +981,7 @@ runs:
BASELINE_SHA: ${{ steps.base.outputs.baseline_sha }}
HEAD_SHA: ${{ steps.guard.outputs.head_sha }}
EMPTY_BASE: ${{ steps.base.outputs.empty_base }}
CRED_MODE: ${{ steps.llm.outputs.mode }}
run: |
# Export the key under the selected provider's env var (only this one),
# so the engine auto-selects that provider.
Expand Down Expand Up @@ -969,6 +1018,11 @@ runs:
--target-ref "$HEAD_SHA"
--source-sha "$HEAD_SHA"
)
# Raise the depth ceiling for licensed/BYO-key runs (controls the
# fallback-full depth when incremental can't run).
if [ "$CRED_MODE" = "byokey" ] || [ "$CRED_MODE" = "license" ]; then
head_args+=(--licensed)
fi
if [ "$EMPTY_BASE" = "true" ]; then
head_args+=(--force-full)
fi
Expand Down Expand Up @@ -1131,6 +1185,7 @@ runs:
TARGET_SHA: ${{ steps.guard.outputs.target_sha }}
DEPTH: ${{ steps.resolve_depth.outputs.depth }}
FORCE_FULL: ${{ inputs.force_full }}
CRED_MODE: ${{ steps.llm.outputs.mode }}
run: |
set -euo pipefail
# Export the key under the selected provider's env var (only this one),
Expand Down Expand Up @@ -1160,6 +1215,9 @@ runs:
--source-sha "$TARGET_SHA"
--depth "$DEPTH"
)
if [ "$CRED_MODE" = "byokey" ] || [ "$CRED_MODE" = "license" ]; then
args+=(--licensed)
fi
[ "$FORCE_FULL" = "true" ] && args+=(--force-full)
LOG="${RUNNER_TEMP}/cb-sync-analyze.log"
python "$ACTION_PATH/scripts/engine_adapter.py" analyze "${args[@]}" | tee "$LOG"
Expand Down
Loading
Loading