Skip to content
Closed
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
10 changes: 5 additions & 5 deletions .github/workflows/codeboarding-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ on:
type: boolean
required: false
default: false
sync_strategy:
description: 'Deliver directly to the target branch or open/update a rolling baseline PR.'
save_baseline_to:
description: 'Commit to main, open/update a rolling baseline PR, or commit to codeboarding/baseline.'
type: choice
options: [push, pull_request]
options: [synced_branch, pull_request, baseline_branch]
required: false
default: push
default: synced_branch

# No workflow-level permissions: the single job below requests only what it
# needs (least privilege), so the default token starts with none.
Expand Down Expand Up @@ -144,7 +144,7 @@ jobs:
# CodeBoarding's own repositories run on the CodeBoarding plan.
llm: license
license_key: ${{ secrets.CODEBOARDING_LICENSE }}
sync_strategy: ${{ inputs.sync_strategy || 'push' }}
save_baseline_to: ${{ inputs.save_baseline_to || 'synced_branch' }}
force_full: ${{ inputs.force_full || false }}
# App token authenticates the baseline push so the commit is attributed
# to the CodeBoarding App (logo avatar). Falls back to the workflow token,
Expand Down
133 changes: 125 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,15 +272,17 @@ jobs:
with:
mode: sync
llm: hosted
target_branch: main
synced_branch: main
force_full: ${{ inputs.force_full || false }}
```

The first run, `force_full: true`, or an incompatible baseline causes a full analysis. Otherwise sync asks Core for an incremental update. If the generated state is unchanged, no commit is created. If the target advances while analysis is running, the stale result is not rebased onto code it did not analyze; the newer push run is allowed to produce the current baseline.
`synced_branch` is the code branch sync keeps an up-to-date analysis of; it defaults to the branch that triggered the run. `save_baseline_to` chooses where that analysis is saved: `synced_branch` (the default, a commit on that branch), `pull_request` (a rolling PR into it) or `baseline_branch` (a branch of its own, see below).

The first run, `force_full: true`, or an incompatible baseline causes a full analysis. Otherwise sync asks Core for an incremental update. If the generated state is unchanged, no commit is created. If the synced branch advances while analysis is running, the stale result is not rebased onto code it did not analyze; the newer push run is allowed to produce the current baseline.

### Protected branches

Set `sync_strategy: pull_request` and grant `pull-requests: write`:
Set `save_baseline_to: pull_request` and grant `pull-requests: write`:

```yaml
permissions:
Expand All @@ -293,14 +295,126 @@ permissions:
with:
mode: sync
llm: hosted
target_branch: main
sync_strategy: pull_request
synced_branch: main
save_baseline_to: pull_request
```

Generation is identical to direct push. Only delivery changes: the same commit is force-with-lease pushed to the machine-owned `codeboarding/sync` branch and one rolling PR is opened into `target_branch`. When there is no longer a generated diff, an obsolete rolling PR is closed.
Generation is identical to direct push. Only delivery changes: the same commit is force-with-lease pushed to the machine-owned `codeboarding/sync` branch and one rolling PR is opened into `synced_branch`. When there is no longer a generated diff, an obsolete rolling PR is closed.

With the default `github.token`, the repository or organization must allow GitHub Actions to create pull requests. A GitHub App token or PAT can instead be passed as `github_token`. The same input is used for review comments and sync delivery.

### Save the diagram to a branch of its own

Set `save_baseline_to: baseline_branch` to keep the analysis off your code branches entirely:

```yaml
- uses: CodeBoarding/CodeBoarding-action@v1
with:
mode: sync
llm: hosted
synced_branch: main
save_baseline_to: baseline_branch
```

`main` is then only read. Each sync adds one commit to `codeboarding/baseline` in the same repository (set `baseline_branch` to change the name), an orphan branch that shares no history with `main`. It holds the same `.codeboarding/` files sync would otherwise commit to `main`, plus `.codeboarding/source.json` naming the commit they describe and the configuration that made them; the commit message carries both as `CodeBoarding-Source:` and `CodeBoarding-Config:` trailers. Pushes only ever fast-forward, `main` is never written, and no pull request is opened. Reviews read their base from the branch, and the web platform reads the latest diagram from it. The [baseline branch section](docs/COMMIT_STRATEGY.md#the-baseline-branch) covers what happens if the branch is deleted, and a ruleset you should import to protect it: sync and review load a pickle from it.

No permission beyond the `contents: write` every sync already needs: the first sync creates the branch with an ordinary push. If you renamed it with `baseline_branch`, set the same name in your review workflow too, since reviews read it — or keep both jobs in one workflow, below.

**Moving an existing setup.** Nothing changes until you opt in: the default keeps committing to the synced branch. To switch, paste this into your coding agent:

```text
Move this repository's CodeBoarding sync to save_baseline_to: baseline_branch.
1. In the workflow that runs CodeBoarding/CodeBoarding-action with mode: sync, set
`save_baseline_to: baseline_branch` in its `with:` block, replacing any
sync_strategy. Rename target_branch to synced_branch if it is set. Keep every
other input.
2. Delete the generated files under .codeboarding/ from the default branch, keeping
the user configuration: .codeboarding/.codeboardingignore,
.codeboarding/health/health_config.json and .codeboarding/health/.healthignore.
Reviews prefer a baseline committed on the branch, so a stale one left there
would keep being used.
3. Remove any .gitattributes lines that mark .codeboarding/ files as
linguist-generated, if nothing else is left under .codeboarding/ for them.
4. Open a pull request with these changes. After it merges, close any open
pull request from the codeboarding/sync branch and delete that branch.
```

The first sync after the merge creates `codeboarding/baseline`, catching up from a saved analysis when there is one.

### Review and sync in one workflow

The two workflows above can be one file with two jobs. Each job keeps its own permissions, and settings both modes read live in one `env:` block, so review always looks for the baseline where sync saves it:

```yaml
name: CodeBoarding

on:
pull_request:
types: [opened, reopened, synchronize]
issue_comment:
types: [created]
push:
branches: [main] # the synced branch; `on:` cannot read env
workflow_dispatch:
inputs:
force_full:
description: Rebuild without the saved baseline
type: boolean
default: false

env:
SYNCED_BRANCH: main
BASELINE_BRANCH: codeboarding/baseline

permissions: {}

# Reviews queue per pull request, syncs per branch.
concurrency:
group: codeboarding-${{ github.event.pull_request.number || github.event.issue.number || github.ref_name }}
cancel-in-progress: false

jobs:
review:
if: >
(github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name == github.repository) ||
(github.event_name == 'issue_comment' && github.event.issue.pull_request != null &&
startsWith(github.event.comment.body, '/codeboarding') &&
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association))
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: read
actions: read
pull-requests: write
issues: write
id-token: write
steps:
- uses: CodeBoarding/CodeBoarding-action@v1
with:
llm: hosted
baseline_branch: ${{ env.BASELINE_BRANCH }}

sync:
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: write
id-token: write
steps:
- uses: CodeBoarding/CodeBoarding-action@v1
with:
mode: sync
llm: hosted
synced_branch: ${{ env.SYNCED_BRANCH }}
save_baseline_to: baseline_branch
baseline_branch: ${{ env.BASELINE_BRANCH }}
force_full: ${{ inputs.force_full || false }}
```

With `save_baseline_to: baseline_branch` sync never commits to `main`, so the `push` trigger needs no `paths-ignore`. Existing two-file setups keep working unchanged; this is only a different way to call the same action.

## Inputs

| Input | Mode | Default | Description |
Expand All @@ -316,11 +430,14 @@ With the default `github.token`, the repository or organization must allow GitHu
| `parsing_model` | both | empty | Parsing-only override for `model`. |
| `depth_cap` | both | `2` | Positive integer maximum analysis depth, including full-analysis fallbacks. Changing it rebuilds incompatible state. |
| `github_token` | both | `${{ github.token }}` | Token for comments and sync delivery. |
| `sync_strategy` | sync | `push` | `push` or `pull_request`. |
| `target_branch` | sync | event branch | Branch receiving the baseline or rolling PR. |
| `synced_branch` | sync | event branch | Code branch sync keeps an up-to-date analysis of. |
| `save_baseline_to` | sync | `synced_branch` | Where sync saves the analysis: `synced_branch` (a commit on it), `pull_request` (a rolling PR into it), or `baseline_branch` (a commit on `baseline_branch`; the synced branch is never written). |
| `baseline_branch` | both | `codeboarding/baseline` | Branch `save_baseline_to: baseline_branch` saves the analysis to; reviews read their base from it when it exists. |
| `force_full` | sync | `false` | Ignore the committed baseline for this run. |
| `warmstart_retention_days` | review | `1` | Days to keep the reusable analysis. Only the next run reads it. |

`target_branch` and `sync_strategy` are deprecated names for `synced_branch` and `save_baseline_to` (`push` is `synced_branch`, `pull_request` is `pull_request`). They still work, with a warning; setting a value under both names fails.

The `/codeboarding` command, comment heading, Mermaid direction (`LR`), hosted webview URL, rolling sync branch, commit message, and CodeBoarding 0.14.5 version are intentionally fixed rather than exposed as configuration.

Review mode needs no sync workflow or committed `.codeboarding` directory. If no
Expand Down
69 changes: 46 additions & 23 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,18 @@ inputs:
description: 'Token used for comments and sync delivery.'
required: false
default: ${{ github.token }}
sync_strategy:
description: 'Sync delivery method: push or pull_request.'
required: false
default: 'push'
target_branch:
description: 'Branch updated by sync mode. Defaults to the event branch.'
synced_branch:
description: 'Code branch that sync keeps an up-to-date analysis of. Defaults to the event branch.'
required: false
default: ''
save_baseline_to:
description: 'Where sync saves the analysis: synced_branch (a commit on synced_branch), pull_request (a rolling PR into synced_branch), or baseline_branch (a commit on baseline_branch; synced_branch is never written).'
required: false
default: 'synced_branch'
baseline_branch:
description: 'Branch that save_baseline_to: baseline_branch stores the analysis on, one commit per sync. Reviews read their base analysis from it when it exists.'
required: false
default: 'codeboarding/baseline'
force_full:
description: 'Run a full sync analysis instead of reusing the committed baseline.'
required: false
Expand All @@ -158,6 +162,17 @@ inputs:
description: 'Days to keep the reusable analysis. Only the next run reads it.'
required: false
default: '1'
# Deprecated inputs. scripts/action/old_api_migrator.sh is the only reader.
target_branch:
description: 'Deprecated: use synced_branch.'
deprecationMessage: 'target_branch is deprecated; use synced_branch.'
required: false
default: ''
sync_strategy:
description: 'Deprecated: use save_baseline_to (push is synced_branch, pull_request is pull_request).'
deprecationMessage: 'sync_strategy is deprecated; use save_baseline_to.'
required: false
default: ''
outputs:
llm_tier:
description: 'Resolved credential tier: hosted, license, byok, or byok+license.'
Expand All @@ -183,9 +198,6 @@ outputs:
seed_source:
description: 'Which state the review head analysis grew from: pr-chain or base.'
value: ${{ steps.review_analyze.outputs.seed_source }}
base_analysis_method:
description: 'How the analysis of the review base commit was obtained: reused, incremental, or full.'
value: ${{ steps.review_analyze.outputs.base_analysis_method }}
merge_base_sha:
description: 'Merge base used as the review comparison baseline.'
value: ${{ steps.guard.outputs.merge_base_sha }}
Expand All @@ -199,15 +211,25 @@ outputs:
description: 'Whether sync delivered a baseline commit.'
value: ${{ steps.sync_commit.outputs.committed }}
sync_pr_url:
description: 'Rolling sync PR URL when sync_strategy is pull_request.'
description: 'Rolling sync PR URL when save_baseline_to is pull_request.'
value: ${{ steps.sync_commit.outputs.sync_pr_url }}
sync_pr_number:
description: 'Rolling sync PR number when sync_strategy is pull_request.'
description: 'Rolling sync PR number when save_baseline_to is pull_request.'
value: ${{ steps.sync_commit.outputs.sync_pr_number }}

runs:
using: 'composite'
steps:
- name: Translate deprecated inputs
id: inputs
shell: bash
env:
SYNCED_BRANCH: ${{ inputs.synced_branch }}
SAVE_BASELINE_TO: ${{ inputs.save_baseline_to }}
OLD_TARGET_BRANCH: ${{ inputs.target_branch }}
OLD_SYNC_STRATEGY: ${{ inputs.sync_strategy }}
run: "$GITHUB_ACTION_PATH/scripts/action/old_api_migrator.sh"

- name: Resolve event
id: guard
shell: bash
Expand All @@ -221,8 +243,9 @@ runs:
REF_TYPE: ${{ github.ref_type }}
EVENT_SHA: ${{ github.sha }}
HEAD_AUTHOR_EMAIL: ${{ github.event.head_commit.author.email }}
TARGET_BRANCH_INPUT: ${{ inputs.target_branch }}
SYNC_STRATEGY: ${{ inputs.sync_strategy }}
SYNCED_BRANCH_INPUT: ${{ steps.inputs.outputs.synced_branch }}
SAVE_BASELINE_TO: ${{ steps.inputs.outputs.save_baseline_to }}
BASELINE_BRANCH: ${{ inputs.baseline_branch }}
COMMENT_BODY: ${{ github.event.comment.body }}
AUTHOR_ASSOCIATION: ${{ github.event.comment.author_association }}
ISSUE_PR_URL: ${{ github.event.issue.pull_request.url }}
Expand Down Expand Up @@ -451,6 +474,8 @@ runs:
CHECKOUT_DIR: ${{ github.workspace }}/.codeboarding-target
STAGE_DIR: ${{ runner.temp }}/cb-state/${{ github.action }}/out
FORCE_FULL: ${{ inputs.force_full }}
SAVE_BASELINE_TO: ${{ steps.inputs.outputs.save_baseline_to }}
BASELINE_BRANCH: ${{ inputs.baseline_branch }}
CFG_HASH: ${{ steps.state.outputs.cfg_hash }}
# Lets a branch without a usable committed baseline catch up from a saved analysis.
ANCESTOR_LOOKUP: ${{ github.server_url == 'https://github.com' && steps.state.outputs.cfg_hash != '' }}
Expand All @@ -472,9 +497,13 @@ runs:
ACTION_PATH: ${{ github.action_path }}
ANALYSIS_DIR: ${{ steps.sync_analyze.outputs.analysis_dir }}
CHECKOUT_DIR: ${{ github.workspace }}/.codeboarding-target
TARGET_BRANCH: ${{ steps.guard.outputs.target_branch }}
SYNCED_BRANCH: ${{ steps.guard.outputs.synced_branch }}
SYNC_BRANCH_START_SHA: ${{ steps.guard.outputs.sync_branch_start_sha }}
SYNC_STRATEGY: ${{ inputs.sync_strategy }}
SAVE_BASELINE_TO: ${{ steps.inputs.outputs.save_baseline_to }}
BASELINE_BRANCH: ${{ inputs.baseline_branch }}
ENGINE_VERSION: ${{ steps.state.outputs.engine_version }}
# Recorded with each baseline-branch commit, so a reader can tell which configuration made it.
CFG_HASH: ${{ steps.state.outputs.cfg_hash }}
GITHUB_TOKEN: ${{ inputs.github_token }}
GH_TOKEN: ${{ inputs.github_token }}
GH_ENTERPRISE_TOKEN: ${{ inputs.github_token }}
Expand Down Expand Up @@ -526,7 +555,7 @@ runs:
MODE: ${{ steps.sync_analyze.outputs.analysis_mode }}
COMMITTED: ${{ steps.sync_commit.outputs.committed }}
FILES: ${{ steps.sync_commit.outputs.files_written }}
STRATEGY: ${{ inputs.sync_strategy }}
SAVE_BASELINE_TO: ${{ steps.inputs.outputs.save_baseline_to }}
PR_URL: ${{ steps.sync_commit.outputs.sync_pr_url }}
run: "$GITHUB_ACTION_PATH/scripts/action/sync-summary.sh"

Expand All @@ -543,13 +572,13 @@ runs:
REVIEW_BASE_REPO: ${{ steps.guard.outputs.base_repo }}
PR_NUMBER: ${{ steps.guard.outputs.pr_number }}
BASE_DIR: ${{ runner.temp }}/cb-state/${{ github.action }}/base
BASE_FETCH_SECONDS: ${{ steps.fetch_base.outputs.seconds }}
WARMSTART_DIR: ${{ runner.temp }}/cb-state/${{ github.action }}/warmstart
RENEW_BASE: ${{ steps.fetch_base.outputs.renew }}
# Lets a branch without a usable committed baseline catch up from a saved analysis.
ANCESTOR_LOOKUP: ${{ github.server_url == 'https://github.com' && steps.state.outputs.cfg_hash != '' }}
# For rewriting the progress comment while a base is built from scratch.
PROGRESS_HEADER: ${{ steps.guard.outputs.comment_id }}
BASELINE_BRANCH: ${{ inputs.baseline_branch }}
BASE_REF: ${{ steps.guard.outputs.base_ref }}
REPOSITORY: ${{ github.repository }}
GH_HOST: ${{ github.server_url }}
Expand Down Expand Up @@ -628,8 +657,6 @@ runs:
ANALYSED_FILES_CHANGED: ${{ steps.review_render.outputs.analysed_files_changed }}
BASE_ANALYSIS_METHOD: ${{ steps.review_analyze.outputs.base_analysis_method }}
BASE_ANALYSIS_REASON: ${{ steps.review_analyze.outputs.base_analysis_reason }}
BASE_SECONDS: ${{ steps.review_analyze.outputs.base_seconds }}
HEAD_SECONDS: ${{ steps.review_analyze.outputs.head_seconds }}
run: "$GITHUB_ACTION_PATH/scripts/action/build-review-artifact.sh"

- name: Upload review artifact
Expand Down Expand Up @@ -661,10 +688,6 @@ runs:
HEAD_SHA: ${{ steps.guard.outputs.head_sha }}
# Analysed files whose content hash differs between base and head, from the render step.
ANALYSED_FILES_CHANGED: ${{ steps.review_render.outputs.analysed_files_changed }}
BASE_ANALYSIS_METHOD: ${{ steps.review_analyze.outputs.base_analysis_method }}
BASE_ANALYSIS_REASON: ${{ steps.review_analyze.outputs.base_analysis_reason }}
BASE_SECONDS: ${{ steps.review_analyze.outputs.base_seconds }}
HEAD_SECONDS: ${{ steps.review_analyze.outputs.head_seconds }}
run: "$GITHUB_ACTION_PATH/scripts/action/build-review-comment.sh"

- name: Post review comment
Expand Down
Loading
Loading