From 625c034360fd0e57dacb596c208c42c3015ecaed Mon Sep 17 00:00:00 2001 From: kirillk Date: Tue, 16 Jun 2026 21:32:26 -0400 Subject: [PATCH 1/2] fix(jetbrains): harden release skill --- .kilo/skills/release-jetbrains/SKILL.md | 41 +++++++++- .../script/update-changelog.ts | 2 +- .../release-jetbrains/script/watch-publish.ts | 75 +++++++++++++------ 3 files changed, 93 insertions(+), 25 deletions(-) diff --git a/.kilo/skills/release-jetbrains/SKILL.md b/.kilo/skills/release-jetbrains/SKILL.md index 1d749bc416a..32da47c8c3a 100644 --- a/.kilo/skills/release-jetbrains/SKILL.md +++ b/.kilo/skills/release-jetbrains/SKILL.md @@ -58,7 +58,13 @@ Create a changelog draft after the prepare PR exists: 1. Read the PR body with `gh pr view --json body`. 2. Extract `JetBrains-From-Tag`, `JetBrains-Tag`, and `## Generated Notes`. -3. Use the release range and path filter as the primary relevance signal: +3. Fetch the release range tags if they are missing locally: + +```bash +git fetch origin refs/tags/:refs/tags/ refs/tags/:refs/tags/ +``` + +4. Use the release range and path filter as the primary relevance signal: ```bash git log --oneline .. -- packages/opencode packages/kilo-jetbrains @@ -103,9 +109,33 @@ The script updates `packages/kilo-jetbrains/CHANGELOG.md` on `jetbrains/release/ docs(jetbrains): edit changelog for v ``` +If `update-changelog.ts` fails with `gh: Not Found (HTTP 404)`, verify the release branch and changelog path with: + +```bash +gh api "repos/Kilo-Org/kilocode/contents/packages/kilo-jetbrains/CHANGELOG.md?ref=jetbrains/release/v" +``` + +Then either fix and retry the helper, or perform the equivalent contents API update using `ref` in the query string. + ## Approve And Publish -Ask the user to approve the release changelog and metadata. By default, have the user merge the release PR manually in GitHub, then watch the publish workflow: +Ask the user to approve the release changelog and metadata. Before merging or publishing, verify the PR approval and required checks are green: + +```bash +gh pr view --json mergeStateStatus,reviewDecision,statusCheckRollup +gh pr checks --watch --interval 10 +``` + +Do not merge or publish while required checks are failing unless the user explicitly gives a maintainer override. + +If a required check fails from an apparent flake, rerun only the failed jobs and wait for the run to finish: + +```bash +gh run rerun --failed +gh run watch --exit-status +``` + +By default, have the user merge the release PR manually in GitHub, then watch the publish workflow: ```bash bun .kilo/skills/release-jetbrains/script/watch-publish.ts --pr --version 7.0.1-rc.7 @@ -123,11 +153,18 @@ Pass a generous Bash timeout, such as `1800000` ms. If the shell times out, re-a bun .kilo/skills/release-jetbrains/script/watch-publish.ts --pr --version 7.0.1-rc.7 --run-id ``` +If `watch-publish.ts --merge` reports that the PR is already merged, or a transient GitHub API `5xx` interrupts publish-run discovery, rerun without `--merge`: + +```bash +bun .kilo/skills/release-jetbrains/script/watch-publish.ts --pr --version +``` + Report the Marketplace channel and GitHub Release URL. RC versions publish to the `eap` channel; stable versions publish to the default Marketplace channel. ## Recovery - If prepare created the tag but failed before creating a PR, rerun prepare for the same version. The existing workflow reuses the tag if it points to the same commit. - If a tag points to an unexpected SHA, stop and inspect manually. Do not move or delete release tags casually. +- If release PR checks fail from an apparent flake, use `gh run rerun --failed`, then `gh run watch --exit-status` before publishing. - If publish fails after merge, rerun the failed workflow only if Marketplace did not already accept the version. - If Marketplace succeeds but GitHub Release upload fails, manually create or edit the GitHub Release for `jetbrains/v` using the reviewed changelog. diff --git a/.kilo/skills/release-jetbrains/script/update-changelog.ts b/.kilo/skills/release-jetbrains/script/update-changelog.ts index b6a3ff071fc..c8fba6db0f9 100644 --- a/.kilo/skills/release-jetbrains/script/update-changelog.ts +++ b/.kilo/skills/release-jetbrains/script/update-changelog.ts @@ -30,7 +30,7 @@ const branch = `jetbrains/release/v${ver}` const section = strip((await Bun.file(file).text()).trim()) validate(section, ver) -const current = (await $`gh api ${`repos/${repo}/contents/${path}`} -f ref=${branch}`.json()) as { +const current = (await $`gh api ${`repos/${repo}/contents/${path}?ref=${encodeURIComponent(branch)}`}`.json()) as { content: string encoding: string sha: string diff --git a/.kilo/skills/release-jetbrains/script/watch-publish.ts b/.kilo/skills/release-jetbrains/script/watch-publish.ts index 0719bcb2071..17130256e13 100644 --- a/.kilo/skills/release-jetbrains/script/watch-publish.ts +++ b/.kilo/skills/release-jetbrains/script/watch-publish.ts @@ -37,10 +37,14 @@ console.log(`runUrl=${url}`) await $`gh run watch ${id} --repo ${repo} --exit-status` -const rel = (await $`gh release view ${`jetbrains/v${ver}`} --repo ${repo} --json url,isPrerelease`.json()) as { - url: string - isPrerelease: boolean -} +const rel = await retry( + async () => + (await $`gh release view ${`jetbrains/v${ver}`} --repo ${repo} --json url,isPrerelease`.json()) as { + url: string + isPrerelease: boolean + }, + "view release", +) console.log( JSON.stringify( { @@ -56,7 +60,15 @@ console.log( async function merge() { const before = new Set((await runs()).map((run) => run.databaseId)) - await $`gh pr merge ${pr} --repo ${repo} --merge` + try { + await $`gh pr merge ${pr} --repo ${repo} --merge` + } catch (err) { + if (await merged()) { + console.warn(`PR ${pr} is already merged; looking for the publish workflow run`) + return await find() + } + throw err + } for (const _ of Array.from({ length: 120 })) { const run = (await runs()).find((item) => item.headBranch === branch && !before.has(item.databaseId)) @@ -68,30 +80,49 @@ async function merge() { async function find() { for (const _ of Array.from({ length: 120 })) { - const run = (await runs()).find((item) => item.headBranch === branch && active(item.status)) + const run = (await runs()).find((item) => item.headBranch === branch) if (run) return String(run.databaseId) await Bun.sleep(1000) } - throw new Error( - `No ${workflow} run found for ${branch}. Merge PR ${pr} first, or pass --merge to merge it automatically.`, - ) + throw new Error(`No ${workflow} run found for ${branch}. Merge PR ${pr} first, or pass --merge to merge it automatically.`) } -function active(status: string) { - return ( - status === "queued" || - status === "in_progress" || - status === "waiting" || - status === "requested" || - status === "pending" +async function merged() { + const info = await retry( + async () => (await $`gh pr view ${pr} --repo ${repo} --json state`.json()) as { state: string }, + "check PR state", ) + return info.state === "MERGED" } async function runs() { - return (await $`gh run list --repo ${repo} --workflow ${workflow} --event pull_request --json databaseId,createdAt,headBranch,status --limit 100`.json()) as { - databaseId: number - createdAt: string - headBranch: string - status: string - }[] + return await retry( + async () => + (await $`gh run list --repo ${repo} --workflow ${workflow} --event pull_request --json databaseId,createdAt,headBranch,status --limit 100`.json()) as { + databaseId: number + createdAt: string + headBranch: string + status: string + }[], + "list workflow runs", + ) +} + +async function retry(task: () => Promise, label: string, tries = 5): Promise { + try { + return await task() + } catch (err) { + if (tries <= 1 || !transient(err)) throw err + console.warn(`${label} failed with a transient GitHub error; retrying (${tries - 1} left): ${message(err)}`) + await Bun.sleep(2000) + return await retry(task, label, tries - 1) + } +} + +function transient(err: unknown) { + return /\bHTTP 5\d\d\b|\b50[234]\b|Bad Gateway|Gateway Timeout|Service Unavailable/i.test(message(err)) +} + +function message(err: unknown) { + return err instanceof Error ? err.message : String(err) } From 53219a8d091085114363ad12ea364e268dfbd763 Mon Sep 17 00:00:00 2001 From: kirillk Date: Tue, 16 Jun 2026 21:34:30 -0400 Subject: [PATCH 2/2] docs: skip routine Java preflight checks --- AGENTS.md | 4 ++-- packages/kilo-jetbrains/AGENTS.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 56c25b60c1c..1a332992b49 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,7 +12,7 @@ Kilo CLI is an open source AI coding agent that generates code from natural lang - **Dev**: `bun run dev` (runs from root) or `bun run --cwd packages/opencode --conditions=browser src/index.ts` - **Dev with params**: `bun dev -- help` - **Extension**: `bun run extension` (build + launch VS Code with the extension in dev mode). Pass `--no-build` to skip the build. -- **Typecheck**: `bun turbo typecheck` (uses `tsgo`, not `tsc`). Includes the JetBrains plugin — requires Java 21. Check with `java -version` before running. If missing, install via SDKMAN: `sdk install java 21-tem && sdk use java 21-tem`. If SDKMAN is not installed, see https://sdkman.io/install. +- **Typecheck**: `bun turbo typecheck` (uses `tsgo`, not `tsc`). Includes the JetBrains plugin and requires Java 21; do not run `java -version` as a routine preflight. Only check Java when a Gradle/Java command fails with a Java-version or missing-Java error. If missing, install via SDKMAN: `sdk install java 21-tem && sdk use java 21-tem`. If SDKMAN is not installed, see https://sdkman.io/install. - **Test**: `bun test` from `packages/opencode/` (NOT from root -- root blocks tests) - **Single test**: `bun test ./test/tool/tool-define.test.ts` from `packages/opencode/` - **CLI build artifact size check**: after `bun run script/build.ts --single --skip-install` in `packages/opencode/`, use `du -h dist/*/*/bin/kilo` (scoped package output lives under `dist/@kilocode/`) @@ -35,7 +35,7 @@ Before saying an implementation is ready, run the smallest relevant checks that | CLI | From `packages/opencode/`: `bun run typecheck`, `bun test` or targeted `bun test ./path/to/file.test.ts` | | VS Code extension | From `packages/kilo-vscode/`: `bun run typecheck`, `bun run lint`, `bun run test:unit` or `bun run test` | | Extension build/package | From `packages/kilo-vscode/`: `bun run compile` or `bun run package` when touching build, packaging, SDK, or webview integration paths | -| JetBrains plugin | From `packages/kilo-jetbrains/`: `./gradlew typecheck`, `./gradlew test`. Requires Java 21 — check first with `java -version`. Install via SDKMAN if missing: `sdk install java 21-tem && sdk use java 21-tem`. | +| JetBrains plugin | From `packages/kilo-jetbrains/`: `./gradlew typecheck`, `./gradlew test`. Requires Java 21; do not run `java -version` as a routine preflight. Check Java only after a Java-version or missing-Java failure. | | CI-only guards | Run affected guards documented above, such as `bun run knip`, `bun run check-kilocode-change`, `bun run script/check-opencode-annotations.ts`, or source link extraction | Never run root `bun test`; the root script prints `do not run tests from root` and exits with code 1. Use package-level tests instead. diff --git a/packages/kilo-jetbrains/AGENTS.md b/packages/kilo-jetbrains/AGENTS.md index 3bb2c6abab0..1016df233c3 100644 --- a/packages/kilo-jetbrains/AGENTS.md +++ b/packages/kilo-jetbrains/AGENTS.md @@ -183,6 +183,7 @@ For blocking I/O in coroutines, move the dispatcher switch inside the callee usi - **Typecheck**: `bun run typecheck` or `./gradlew typecheck` from `packages/kilo-jetbrains/` — compiles all Kotlin sources including the generated API client. Does NOT require CLI binaries. - **Full build**: `bun run build` from `packages/kilo-jetbrains/` (prepares CLI binaries + runs Gradle `buildPlugin`). - **Gradle only**: `./gradlew buildPlugin` from `packages/kilo-jetbrains/` (requires CLI binaries already present in `backend/build/generated/cli/`; run `bun run build --prepare-cli` first). +- **Java checks**: Do not run `java -version` as a routine preflight. Gradle commands already fail clearly when Java is missing or incompatible; check Java only when diagnosing that failure mode. - **Via Turbo**: `bun turbo build --filter=@kilocode/kilo-jetbrains` from repo root. - **Run in sandbox**: `./gradlew runIde` — launches sandboxed IntelliJ with the plugin. Does NOT build CLI binaries. - **Run split backend**: `./gradlew runIdeBackend` — if it exits shortly after startup, check for an orphaned Java process from a previous backend run and kill it before restarting.