# OpenCodeReview - GitLab CI Merge Request Auto-Review Demo
#
# This pipeline automatically reviews Merge Requests using OpenCodeReview
# and posts review comments (discussions) directly on the MR diff.
# Supports both same-repo and forked MR scenarios.
#
# Required CI/CD Variables (Settings → CI/CD → Variables):
#   OCR_LLM_URL        - LLM API endpoint (e.g., https://api.openai.com/v1/chat/completions)
#   OCR_LLM_AUTH_TOKEN - Authentication token for the LLM API (mark as "Masked")
#   OCR_LLM_MODEL      - Model name (e.g., gpt-4o). OCR has no built-in default model; the
#                        pipeline fails fast if this is unset.
#
# Not a CI/CD variable (hardcoded in the script below):
#   llm.use_anthropic  - Pinned to `false` (OpenAI-compatible APIs). GitLab CI/CD cannot
#                        store variables shorter than 8 chars, so this is set via
#                        `ocr config set llm.use_anthropic false`. To use Anthropic
#                        Claude, edit that line in the Configure OCR step to `true`.
#
# Optional CI/CD Variables (LLM provider configuration):
#   OCR_LANGUAGE          - Review output language, written via `ocr config set language`
#                          (e.g. English, Chinese). Defaults to OCR's built-in default (English).
#   OCR_LLM_AUTH_HEADER   - Custom auth header name, written via `ocr config set llm.auth_header`
#                          (e.g. "x-api-key" for some providers).
#   OCR_LLM_EXTRA_HEADERS - Extra headers "K=V,K=V", written via `ocr config set llm.extra_headers`.
#   OCR_LLM_TIMEOUT       - LLM request timeout in seconds (read natively by OCR from the env).
#                          Unset/empty = use OCR's default.
#
# Optional CI/CD Variables (review behavior, passed as `ocr review` flags):
#   OCR_REVIEW_CONCURRENCY - Max concurrent file reviews, passed via `--concurrency` (default: 8).
#   OCR_BACKGROUND         - Business/requirement context, passed via `--background` (e.g. MR title).
#   OCR_RULE               - Path to a custom rules JSON file, passed via `--rule`.
#
# Optional CI/CD Variables (versioning):
#   OCR_VERSION - npm version spec for @alibaba-group/open-code-review (default: latest).
#                 Pinning (e.g. "1.8.8" or "~1.8") makes reviews reproducible.
#
# Optional CI/CD Variables (GitLab token):
#   GITLAB_API_TOKEN   - GitLab Personal/Project Access Token with "api" scope
#                        (falls back to CI_JOB_TOKEN if not set)
#
# Optional CI/CD Variables (for retry/delay tuning):
#   OCR_RETRY_BASE_DELAY    - Base delay (ms) for exponential backoff on rate-limit retries (default: 2000)
#   OCR_MAX_RETRIES         - Max retry attempts per comment when rate-limited or transient error (default: 3)
#   OCR_MAX_RETRY_DELAY     - Maximum delay (ms) per single retry, caps Retry-After/backoff (default: 60000)
#   OCR_SUCCESS_DELAY       - Delay (ms) after a successful comment post to pace requests (default: 2000)
#   OCR_FAILURE_DELAY       - Delay (ms) after a non-rate-limit failure to pace subsequent requests (default: 1000)
#   OCR_RATE_LIMIT_THRESHOLD - Proactively slow down when GitLab RateLimit-Remaining is at/below this value (default: 10, set 0 to disable)
#
# Optional CI/CD Variables (publication behavior, aligned with the GitHub Action):
#   OCR_STICKY_SUMMARY            - "true" updates the summary note in place across runs (default: true)
#   OCR_INCREMENTAL              - "true" skips comments whose line range overlaps prior bot discussions (default: false)
#   OCR_INCREMENTAL_OVERLAP_THRESHOLD - IoU threshold (0-1) for multi-line overlap (default: 0.6)
#   OCR_ROUTE_SEVERITY_BELOW     - route findings at/below this severity to the summary note (e.g. "low")
#   OCR_ROUTE_CATEGORIES         - comma-separated categories to route to the summary (e.g. "style,documentation")
#   OCR_FAIL_ON_SEVERITY         - fail the CI job when a comment's severity is at/above this level (e.g. "critical")
#
# Outputs (written under .ocr/ in the project directory so GitLab Runner can
# upload them as artifacts — Runner rejects paths outside the build dir):
#   .ocr/ocr-stats.env           - dotenv file with OCR_COMMENTS_{TOTAL,INLINE,SUMMARY,ROUTED,SKIPPED,FAILED}
#                                  and OCR_SUMMARY_URL, exposed to downstream jobs via the dotenv artifact.
#   .ocr/ocr-result.json         - raw `ocr review --format json` output (artifact).
#   .ocr/ocr-stderr.log          - OCR stderr (artifact).
#
# Note: The pipeline also configures llm.extra_body to '{"thinking": {"type": "disabled"}}'
#   to disable thinking mode for compatibility with various LLM providers.
#
# Fork MR Support:
#   The script uses CI_COMMIT_SHA as the diff target to correctly resolve the
#   source commit from forked repos. For some GitLab versions, you may need to enable:
#   Project Settings → CI/CD → General pipelines →
#     "Run pipelines in the parent project for merge requests from forked projects"

stages:
  - review

code-review:
  stage: review
  interruptible: true
  resource_group: mr-review-$CI_MERGE_REQUEST_IID
  image: node:20
  only:
    - merge_requests
  variables:
    GIT_DEPTH: 0  # Full history needed for merge-base diff
  script:
    # Install OpenCodeReview
    # OCR_VERSION (optional) pins the npm version spec for reproducible reviews;
    # unset falls back to "latest".
    - |
      : "${OCR_VERSION:=latest}"
      npm install -g "@alibaba-group/open-code-review@${OCR_VERSION}"
      echo "OpenCodeReview installed with version:" && ocr version || true

    # Configure OCR
    # Gitlab CI/CD does not support configuring variables with value length less than 8, so you can't set use_anthropic as a CI variable
    - |
      : "${OCR_LLM_URL:?set OCR_LLM_URL in Settings -> CI/CD -> Variables}"
      : "${OCR_LLM_AUTH_TOKEN:?set OCR_LLM_AUTH_TOKEN in Settings -> CI/CD -> Variables}"
      : "${OCR_LLM_MODEL:?set OCR_LLM_MODEL in Settings -> CI/CD -> Variables}"
      ocr config set llm.url "$OCR_LLM_URL"
      ocr config set llm.auth_token "$OCR_LLM_AUTH_TOKEN"
      ocr config set llm.model "$OCR_LLM_MODEL"
      ocr config set llm.use_anthropic false
      ocr config set llm.extra_body '{"thinking": {"type": "disabled"}}'
      # Optional config: only written when the CI var is non-empty, so an unset
      # variable does not clobber a previously-configured value.
      if [ -n "$OCR_LANGUAGE" ]; then ocr config set language "$OCR_LANGUAGE"; fi
      if [ -n "$OCR_LLM_AUTH_HEADER" ]; then ocr config set llm.auth_header "$OCR_LLM_AUTH_HEADER"; fi
      if [ -n "$OCR_LLM_EXTRA_HEADERS" ]; then ocr config set llm.extra_headers "$OCR_LLM_EXTRA_HEADERS"; fi
      # OCR_LLM_TIMEOUT is read natively by OCR from the env (no `ocr config set`
      # key exists for it); GitLab CI/CD variables are already exported into the
      # job env, so leaving it unset/empty = use OCR's default.

    # Run OCR review (use CI_COMMIT_SHA for --to to support both same-repo and forked MRs)
    # Optional review knobs (only passed when the CI var is non-empty):
    #   OCR_REVIEW_CONCURRENCY -> --concurrency
    #   OCR_BACKGROUND         -> --background
    #   OCR_RULE               -> --rule
    - |
      mkdir -p .ocr
      echo "Reviewing MR: ${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME} against ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}"
      ARGS=(--from "origin/${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}" --to "${CI_COMMIT_SHA}" --format json --audience agent)
      if [ -n "$OCR_REVIEW_CONCURRENCY" ]; then ARGS+=(--concurrency "$OCR_REVIEW_CONCURRENCY"); fi
      if [ -n "$OCR_BACKGROUND" ]; then ARGS+=(--background "$OCR_BACKGROUND"); fi
      if [ -n "$OCR_RULE" ]; then ARGS+=(--rule "$OCR_RULE"); fi
      set +e
      ocr review "${ARGS[@]}" > .ocr/ocr-result.json 2>.ocr/ocr-stderr.log
      OCR_EXIT_CODE=$?
      set -e
      echo "OCR_EXIT_CODE=$OCR_EXIT_CODE"
      echo "=== OCR result ==="
      cat .ocr/ocr-result.json
      echo "=== OCR stderr ==="
      cat .ocr/ocr-stderr.log

    # Post review comments to MR
    # post_review.py lives alongside this file; copy both into your repo.
    # See examples/gitlab_ci/README.md for setup instructions.
    # NB: run unconditionally so a crashed OCR still surfaces its stderr on the
    # MR summary (post_review.py posts the stderr note on parse failure); the
    # job-fail gate below then turns the pipeline red on a non-zero OCR exit.
    # Capture the exit code so the gate below runs regardless of whether
    # post_review.py failed on severity gating or a posting error.
    - |
      set +e
      python3 post_review.py .ocr/ocr-result.json
      POST_EXIT_CODE=$?
      set -e

    # Fail the job on OCR error or severity gate (mirrors the GitHub Action).
    # post_review.py has already posted any stderr to the MR summary by this
    # point, so the failure is visible both as a red pipeline and on the MR.
    - |
      # Default OCR/POST exit codes so the gate below is safe even if a
      # prior step was skipped (e.g., post_review.py step never reached).
      : "${OCR_EXIT_CODE:=0}"
      : "${POST_EXIT_CODE:=0}"
      if [ "$OCR_EXIT_CODE" != "0" ]; then
        echo "ocr review exited with code ${OCR_EXIT_CODE}; see MR summary for stderr."
        exit "$OCR_EXIT_CODE"
      fi
      if [ "$POST_EXIT_CODE" != "0" ]; then
        echo "post_review.py exited with code ${POST_EXIT_CODE}; see MR summary for details."
        exit "$POST_EXIT_CODE"
      fi

  # Expose the review result, stderr, and posting stats to downstream jobs.
  # The dotenv report carries OCR_COMMENTS_* and OCR_SUMMARY_URL.
  artifacts:
    when: always
    expire_in: 1 week
    paths:
      - .ocr/ocr-result.json
      - .ocr/ocr-stderr.log
    reports:
      dotenv: .ocr/ocr-stats.env
