Files
dingtalk-workspace-cli/docs/ci-pr-gates.md
T
2026-07-20 17:14:35 +08:00

6.3 KiB

CI — PR 合入门禁

The pull-request admission layer has exactly nine required external contexts:

Required context Contract
Lint Stable PR revision classification, formatting, go vet, and Actionlint
Test Race/unit/release-script tests plus fast cross-platform compilation
Coverage Overall non-regression and 100% changed-code coverage
Policy Repository policy and the fail-closed CHANGELOG contract
Edition Edition contract tests
Interface Integrity CLI, Schema, Skill, and stable-release compatibility
AI Behavior Base-owned policy for PRs labeled ai-generated
CLI Smoke Offline help for every public top-level command
Mock MCP HTTP and stdio MCP lifecycle smoke tests

The workflow display name is CI. Parallel helper jobs may implement Test and Coverage, but they are not ruleset contexts. Do not require an aggregate alias or a downstream integration check in place of the nine contracts above.

AI Behavior is evaluated by a pull_request_target workflow that never checks out or executes PR code. It writes the exact AI Behavior status to the current PR head. Its Files API read is bracketed by base/head revision checks, so a synchronize race fails closed. The same workflow supplies a successful AI Behavior check run on protected main pushes for release governance.

Exact CHANGELOG-only fast path

A pull request qualifies only when GitHub reports exactly one changed file, that file is an in-place modification of CHANGELOG.md, and the base and head both retain it as a regular non-executable 100644 blob. Add, delete, rename, symlink, executable-mode, and second-file changes do not qualify.

Lint classifies the Files API result only after verifying that the API's base and head equal the event revision both before and after pagination. Policy checks out GitHub's PR merge ref and verifies its parents:

HEAD^1 = pull_request.base.sha
HEAD^2 = pull_request.head.sha

It then runs:

./scripts/policy/check-changelog-pr.sh \
  --fast-path "$PR_BASE_SHA" HEAD

Because the verified PR diff contains only CHANGELOG.md, the validator and its policy dependencies in that merge tree are byte-for-byte the current base versions. Validation targets the synthetic merge tree, not the feature-branch tree, so a stale branch cannot supply an older validator or combine with newer base notes into an invalid final CHANGELOG.

All nine admission contexts are still emitted and must succeed. Expensive implementation helpers are skipped; the named contexts record that their code surface is unaffected. After merge, the protected main push executes the full admission suite.

Any PR that touches CHANGELOG.md but also changes another file runs the same content contract in Policy with --content-only. That mode permits the second file but still rejects invalid dates or versions, missing bullets, placeholder TODO/TBD, unmanaged-section changes, and unsafe tree modes. Adding a second file therefore cannot bypass CHANGELOG validation.

Platform and downstream boundaries

Ordinary PRs run the primary Linux assurance plus fast Darwin/Windows compile checks. Full native macOS/Windows tests and platform coverage run on a PR only when its diff touches auth, keychain, OS-specific Go files, installers, packaging, Formulae, or release automation. Protected main pushes run the complete native matrix.

Complete Multi-profile E2E is not a PR admission context. It belongs to the Main Integration — 主干集成 workflow and runs only after a push to main (or an explicit manual dispatch). A failing downstream run remains a real regression and must be repaired, but it must not be represented by a synthetic successful PR check.

flowchart TB
  PR["Pull request"] --> ADMISSION["CI"]
  ADMISSION --> L["Lint"]
  ADMISSION --> T["Test"]
  ADMISSION --> C["Coverage"]
  ADMISSION --> P["Policy"]
  ADMISSION --> E["Edition"]
  ADMISSION --> I["Interface Integrity"]
  ADMISSION --> A["AI Behavior"]
  ADMISSION --> S["CLI Smoke"]
  ADMISSION --> M["Mock MCP"]
  ADMISSION --> MAIN["Protected main"]
  MAIN --> NATIVE["Full native platform matrix"]
  MAIN --> E2E["Multi-profile E2E"]
  MAIN --> RELEASE["Release delivery"]

Running focused gates locally

Run the contracts relevant to the change:

make build
make policy
make interface-integrity
make authoritative-interface-integrity BASE_REF=<merge-base>
make schema-compatibility BASE_REF=<merge-base>
make skill-command-integrity
make cli-smoke
make mock-mcp-smoke
go test -v -count=1 ./pkg/editiontest/...

For an exact CHANGELOG-only branch:

base_ref=$(git merge-base HEAD origin/main)
./scripts/policy/check-changelog-pr.sh --fast-path "$base_ref" HEAD

make coverage-gate is an enforcement step, not a profile generator. CI generates the candidate, supporting, merge-base, and (when risk-selected) native profiles before the aggregate Coverage context evaluates them. The aggregate and native gates require 100% coverage for changed executable Go statements. Overall coverage remains an unrounded, zero-tolerance merge-base non-regression check. Candidate and baseline profiles are evaluated by the same block-deduplicating checker; supporting policy and shortcut profiles contribute to changed-code coverage only. The checked-in badge is presentation only and is never read as a gate input.

Compatibility checks derive authoritative Interface snapshots from the PR merge-base and the latest reachable stable release. The candidate cannot bless a breaking change by editing a fixture. Schema additions are allowed; historical products, tools, parameters, mappings, positional execution fields, constraints, and safety semantics remain protected.

Required GitHub repository settings

The main quality ruleset must enable strict required-status-check policy (strict_required_status_checks_policy=true) so a PR is revalidated whenever main advances. It must require these exact contexts and no legacy aliases:

  • Lint
  • Test
  • Coverage
  • Policy
  • Edition
  • Interface Integrity
  • AI Behavior
  • CLI Smoke
  • Mock MCP

Do not require helper jobs, Multi-profile E2E, or an aggregate admission alias. Update ruleset contexts only after the new names have appeared on the protected branch, so a rename cannot silently remove enforcement or leave an unproducible required context.