mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-24 16:02:55 +08:00
Merge pull request #8531 from Kilo-Org/fix/opencode-annotation-ci-path
feat(cli): re-apply kilocode_change annotation CI check (lost in force push)
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
name: Check opencode annotations
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "packages/opencode/**"
|
||||
- "script/check-opencode-annotations.ts"
|
||||
- ".github/workflows/check-opencode-annotations.yml"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
check-annotations:
|
||||
name: Check kilocode_change annotations
|
||||
if: github.repository == 'Kilo-Org/kilocode'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
|
||||
- name: Check kilocode_change annotations in shared opencode files
|
||||
env:
|
||||
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
run: |
|
||||
if [ -n "$BASE_SHA" ]; then
|
||||
bun run script/check-opencode-annotations.ts --base "$BASE_SHA"
|
||||
else
|
||||
echo "No PR base SHA available (workflow_dispatch without PR context) — skipping."
|
||||
fi
|
||||
@@ -18,6 +18,7 @@ Kilo CLI is an open source AI coding agent that generates code from natural lang
|
||||
- **Knip** (unused exports): `bun run knip` from `packages/kilo-vscode/`. CI runs this — all exported types/functions must be imported somewhere. Remove or unexport unused exports before pushing.
|
||||
- **Source links**: After adding or changing URLs in `packages/kilo-vscode/`, `packages/kilo-vscode/webview-ui/`, or `packages/opencode/src/`, run `bun run script/extract-source-links.ts` from the repo root and commit the updated `packages/kilo-docs/source-links.md`. CI runs this check — the build fails if the file is stale.
|
||||
- **kilocode_change check**: `bun run check-kilocode-change` from `packages/kilo-vscode/`. CI runs this — `kilocode_change` is a marker for upstream merge conflicts and must not appear in `packages/kilo-vscode/` or `packages/kilo-ui/` (these are entirely Kilo Code additions). Remove the markers before pushing.
|
||||
- **opencode annotation check**: `bun run script/check-opencode-annotations.ts` from repo root. CI runs this on PRs touching `packages/opencode/` — every Kilo-specific change in shared opencode files must be annotated with `kilocode_change` markers. Exempt paths (no markers needed): `packages/opencode/src/kilocode/`, `packages/opencode/test/kilocode/`, and any path containing `kilocode` in the name.
|
||||
|
||||
## Products
|
||||
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
/**
|
||||
* Verifies that every Kilo-specific change in shared packages/opencode/ files
|
||||
* is annotated with a kilocode_change marker.
|
||||
*
|
||||
* Usage:
|
||||
* bun run script/check-opencode-annotations.ts # diff against origin/main
|
||||
* bun run script/check-opencode-annotations.ts --base <ref> # diff against <ref>
|
||||
*
|
||||
* A line is "covered" if it:
|
||||
* - contains // kilocode_change (inline annotation)
|
||||
* - falls inside a // kilocode_change start/end block (block annotation)
|
||||
* - is in a file whose first non-empty line is (whole-file annotation)
|
||||
* // kilocode_change - new file
|
||||
* - is empty / whitespace-only (skipped)
|
||||
* - is itself a marker line (auto-covered)
|
||||
*
|
||||
* Exempt paths (no markers needed — entirely Kilo-specific):
|
||||
* - packages/opencode/src/kilocode/**
|
||||
* - packages/opencode/test/kilocode/**
|
||||
* - Any path containing "kilocode" in directory or filename
|
||||
*/
|
||||
|
||||
import { spawnSync } from "node:child_process"
|
||||
import { readFileSync } from "node:fs"
|
||||
import path from "node:path"
|
||||
|
||||
const ROOT = path.resolve(import.meta.dir, "..")
|
||||
const SOURCE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx"])
|
||||
|
||||
const args = process.argv.slice(2)
|
||||
const baseIdx = args.indexOf("--base")
|
||||
const base = baseIdx !== -1 ? args[baseIdx + 1] : "origin/main"
|
||||
|
||||
function run(cmd: string, args: string[]) {
|
||||
const result = spawnSync(cmd, args, { cwd: ROOT, encoding: "utf8" })
|
||||
return result.stdout?.trim() ?? ""
|
||||
}
|
||||
|
||||
function changedFiles() {
|
||||
const out = run("git", ["diff", "--name-only", "--diff-filter=AMRT", `${base}...HEAD`, "--", "packages/opencode"])
|
||||
return out ? out.split("\n").filter(Boolean) : []
|
||||
}
|
||||
|
||||
function isExempt(file: string) {
|
||||
const norm = file.replaceAll("\\", "/").toLowerCase()
|
||||
return norm.split("/").some((part) => part.includes("kilocode"))
|
||||
}
|
||||
|
||||
function isSource(file: string) {
|
||||
return SOURCE_EXTS.has(path.extname(file))
|
||||
}
|
||||
|
||||
function addedLines(file: string): Set<number> {
|
||||
const diff = run("git", ["diff", "--unified=0", "--diff-filter=AMRT", `${base}...HEAD`, "--", file])
|
||||
const out = new Set<number>()
|
||||
for (const line of diff.split("\n")) {
|
||||
const m = line.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/)
|
||||
if (!m) continue
|
||||
const start = Number(m[1])
|
||||
const count = m[2] !== undefined ? Number(m[2]) : 1
|
||||
for (let i = 0; i < count; i++) out.add(start + i)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
function coveredLines(text: string): { lines: string[]; covered: Set<number> } {
|
||||
const lines = text.split(/\r?\n/)
|
||||
const covered = new Set<number>()
|
||||
|
||||
// Whole-file annotation: first non-empty line is "// kilocode_change - new file"
|
||||
const first = lines.find((x) => x.trim() !== "")
|
||||
if (first?.match(/\/\/\s*kilocode_change\s*-\s*new\s*file\b/)) {
|
||||
for (let i = 1; i <= lines.length; i++) covered.add(i)
|
||||
return { lines, covered }
|
||||
}
|
||||
|
||||
let block = false
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const n = i + 1
|
||||
const line = lines[i] ?? ""
|
||||
|
||||
if (line.match(/\/\/\s*kilocode_change\s+start\b/)) {
|
||||
block = true
|
||||
covered.add(n)
|
||||
continue
|
||||
}
|
||||
|
||||
if (line.match(/\/\/\s*kilocode_change\s+end\b/)) {
|
||||
covered.add(n)
|
||||
block = false
|
||||
continue
|
||||
}
|
||||
|
||||
if (block) {
|
||||
covered.add(n)
|
||||
continue
|
||||
}
|
||||
|
||||
if (line.match(/\/\/\s*kilocode_change\b/)) covered.add(n)
|
||||
}
|
||||
|
||||
return { lines, covered }
|
||||
}
|
||||
|
||||
// --- main ---
|
||||
|
||||
const files = changedFiles().filter((f) => !isExempt(f) && isSource(f))
|
||||
|
||||
if (files.length === 0) {
|
||||
console.log("No shared opencode source files changed — nothing to check.")
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const violations: string[] = []
|
||||
|
||||
for (const file of files) {
|
||||
const nums = addedLines(file)
|
||||
if (nums.size === 0) continue
|
||||
|
||||
const abs = path.join(ROOT, file)
|
||||
const text = readFileSync(abs, "utf8")
|
||||
const { lines, covered } = coveredLines(text)
|
||||
|
||||
for (const n of nums) {
|
||||
const line = lines[n - 1] ?? ""
|
||||
const trim = line.trim()
|
||||
if (!trim) continue
|
||||
if (trim.match(/\/\/\s*kilocode_change\b/)) continue
|
||||
if (!covered.has(n)) violations.push(` ${file}:${n}: ${trim}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (violations.length === 0) {
|
||||
console.log("All shared opencode changes are annotated with kilocode_change markers.")
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
console.error(
|
||||
[
|
||||
"Unannotated Kilo changes found in shared opencode files:",
|
||||
"",
|
||||
...violations,
|
||||
"",
|
||||
"Every Kilo-specific change in packages/opencode/ must be annotated.",
|
||||
"",
|
||||
"Inline (single line):",
|
||||
" const url = Flag.KILO_MODELS_URL || 'https://models.dev' // kilocode_change",
|
||||
"",
|
||||
"Block (multiple lines):",
|
||||
" // kilocode_change start",
|
||||
" ...",
|
||||
" // kilocode_change end",
|
||||
"",
|
||||
"New file:",
|
||||
" // kilocode_change - new file",
|
||||
"",
|
||||
"Exempt paths (no markers needed):",
|
||||
" - packages/opencode/src/kilocode/**",
|
||||
" - packages/opencode/test/kilocode/**",
|
||||
" - Any path containing 'kilocode' in the directory or filename",
|
||||
"",
|
||||
"See AGENTS.md for details.",
|
||||
].join("\n"),
|
||||
)
|
||||
|
||||
process.exit(1)
|
||||
Reference in New Issue
Block a user