open-code-review/examples
chethanuk 870fc6a4a9
feat(examples): add Gerrit CI integration example (Jenkins + Gerrit Trigger) (#401)
* feat(examples): add Gerrit CI integration for publishing review results

Adds examples/gerrit_ci following the gitflic_ci CI-glue pattern (#316):
a stdlib-only post_review.py that reads 'ocr review --format json' and
publishes summary, inline comments, suggestion blocks, and file-level
findings in ONE batched POST /a/changes/{change}/revisions/{rev}/review,
tagged autogenerated:opencodereview with notify=OWNER and
omit_duplicate_comments.

Decisions (validated against a live Gerrit 3.14 in Docker):
- Plain comments, bare line = end_line, no CommentRange: the range form
  {start,0,end,0} renders lines start..end-1 in the UI (end_character 0
  excludes the final line), so correct ranges would need file contents
  in CI. Verified via UI screenshots during E2E.
- Preemptive HTTP Basic auth (urllib's handler does not preempt),
  XSSI )]}' stripping, HTML-200 detected as config error, 400 batch
  fold-retry, 409 change-closed tolerated (defensive on modern Gerrit:
  label-free reviews post fine on closed changes), password scrubbed
  from all error output.
- Jenkins Gerrit Trigger as the reference integration; the script is
  trigger-agnostic via flags/env (Zuul/hook recipes in the README).
  Jenkinsfile always passes the injected patchset SHA (revision race).

45 table-driven stdlib-unittest tests, red-to-green TDD; E2E against
gerritcodereview/gerrit covering live post, unicode round-trip, dedup
re-run, and failure paths (401 exit 2 with scrubbed password).

* fix(examples/gerrit_ci): harden post_review.py per review findings

- Send Authorization via add_unredirected_header so Basic credentials
  cannot follow a redirect to another host (urllib forwards ordinary
  headers cross-host).
- Reject non-object JSON input cleanly instead of an AttributeError
  traceback; validate --timeout > 0 at argparse time.
- Warn on stderr when the 400-fallback folded summary is truncated, and
  report the fold accurately instead of claiming N inline comments.
- Pin drafts=KEEP in ReviewInput (depot_tools convention; old servers
  defaulted to deleting the caller's drafts).
- Jenkinsfile: fetch with an explicit dest refspec so
  origin/$GERRIT_BRANCH materializes under narrow-refspec clones;
  comment out extra_body thinking (OpenAI rejects unknown fields).
- Gitflic parity: optional positional input arg; single-sourced
  'current' revision default; scrub() skips sub-4-char passwords.
- README: document exit 1 and the defensive 409 branch; add gerrit_ci
  row to all five root READMEs (parity with the GitFlic example).
- Tests: 44 -> 55, covering stdin input, flag-over-env precedence,
  fold-retry failure, fold truncation, GERRIT_CHANGE_URL wiring,
  non-dict JSON, timeout validation, positional input.

* fix(examples/gerrit_ci): address OCR bot review on #401

- Jenkinsfile: resolve the LLM endpoint from the OCR_LLM_URL/TOKEN/MODEL
  env triple instead of `ocr config set`, so the auth token stays
  env-only and is never written to ~/.opencodereview/config.json on a
  shared agent (OCR_CONFIG_PATH is deliberately ignored by write paths,
  so it can't redirect the leak). Pin the npm install to a validated
  version. Document the config-file fallback (and its cleanup) for
  extra_body, which has no env equivalent.
- post_review.py: scrub the base64(user:password) Authorization value
  from error output too, not just the raw password — a proxy echoing the
  request header would otherwise leak decodable credentials. +1 test.

* feat(examples/gerrit_ci): scoped retry + robustness polish

Post-review hardening from an OSS-precedent study (depot_tools, kudu,
Gerrit REST docs):

- Bounded retry (3 attempts, exp backoff) in make_poster, scoped to the
  provably-safe failures only: HTTP 5xx and pre-response connection
  errors (refused/reset/DNS). Read-timeouts are deliberately NOT retried
  — a timeout is ambiguous (the server may have applied the review) and
  omit_duplicate_comments dedupes only inline comments, not the summary
  message, so a blind retry could post a duplicate change message. 4xx
  (400/401/404/409) propagate unchanged so main() classifies them as
  before.
- Document why plain comments are used, not robot_comments: the latter
  is deprecated since Gerrit 3.6, disabled-by-default in 3.12, and
  slated for removal; the tag already marks bot origin.
- Fold fallback: strip the '; N posted as inline comment(s).' clause
  from the reused summary so the folded message doesn't claim inline
  comments were posted and then explain they couldn't be placed.
- README: document the retry scoping and note fix_suggestions / label
  voting as intentional future options.

Tests: 56 -> 61 (5 retry cases: 5xx-then-ok, conn-err-then-ok,
5xx-exhaust, read-timeout-not-retried, 4xx/409-not-retried).
2026-07-21 13:25:30 +08:00
..
gerrit_ci feat(examples): add Gerrit CI integration example (Jenkins + Gerrit Trigger) (#401) 2026-07-21 13:25:30 +08:00
gitflic_ci docs(examples): add GitFlic CI auto-review example (#201) 2026-07-02 10:41:39 +08:00
github_actions feat(action): extract reusable composite PR-review GitHub Action (#337) 2026-07-09 19:08:19 +08:00
gitlab_ci fix: actions rate limit (#164) 2026-06-22 10:10:47 +08:00
README.md feat(examples): add Gerrit CI integration example (Jenkins + Gerrit Trigger) (#401) 2026-07-21 13:25:30 +08:00

CI/CD Integration Examples

This directory contains examples for integrating OpenCodeReview (OCR) into various CI/CD pipelines.

Contents

Each subdirectory contains its own README with detailed setup instructions.