mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-24 16:02:55 +08:00
ci(jetbrains): publish marketplace rc builds
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
name: publish-jetbrains
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "jetbrains/*"
|
||||
|
||||
concurrency:
|
||||
group: publish-jetbrains-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
if: github.repository == 'Kilo-Org/kilocode'
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "24"
|
||||
|
||||
- name: Setup Bun
|
||||
uses: ./.github/actions/setup-bun
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: "21"
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Install build tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y patchelf zip
|
||||
|
||||
- name: Validate version tag
|
||||
id: version
|
||||
run: |
|
||||
tag="$GITHUB_REF_NAME"
|
||||
if [[ "$tag" != jetbrains/* ]]; then
|
||||
echo "Unsupported tag '$tag'. Expected jetbrains/<version>." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
version="${tag#jetbrains/}"
|
||||
if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+$ ]]; then
|
||||
{
|
||||
echo "version=$version"
|
||||
echo "kind=rc"
|
||||
echo "marketplace_channel=eap"
|
||||
echo "cli_channel=rc"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
{
|
||||
echo "version=$version"
|
||||
echo "kind=stable"
|
||||
echo "marketplace_channel=default"
|
||||
echo "cli_channel=latest"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
echo "Stable JetBrains Marketplace publishing is implemented but intentionally disabled; use an rc tag such as jetbrains/7.0.1-rc.1." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Unsupported JetBrains plugin version '$version'. Expected x.y.z-rc.n or x.y.z." >&2
|
||||
exit 1
|
||||
|
||||
- name: Validate publishing secrets
|
||||
run: |
|
||||
missing=0
|
||||
for name in JETBRAINS_MARKETPLACE_TOKEN JETBRAINS_CERTIFICATE_CHAIN JETBRAINS_PRIVATE_KEY JETBRAINS_PRIVATE_KEY_PASSWORD; do
|
||||
if [[ -z "${!name}" ]]; then
|
||||
echo "Missing required secret: $name" >&2
|
||||
missing=1
|
||||
fi
|
||||
done
|
||||
exit "$missing"
|
||||
env:
|
||||
JETBRAINS_MARKETPLACE_TOKEN: ${{ secrets.JETBRAINS_MARKETPLACE_TOKEN }}
|
||||
JETBRAINS_CERTIFICATE_CHAIN: ${{ secrets.JETBRAINS_CERTIFICATE_CHAIN }}
|
||||
JETBRAINS_PRIVATE_KEY: ${{ secrets.JETBRAINS_PRIVATE_KEY }}
|
||||
JETBRAINS_PRIVATE_KEY_PASSWORD: ${{ secrets.JETBRAINS_PRIVATE_KEY_PASSWORD }}
|
||||
|
||||
- name: Prepare CLI resources
|
||||
working-directory: packages/kilo-jetbrains
|
||||
run: bun script/build.ts --production --prepare-cli
|
||||
env:
|
||||
KILO_VERSION: ${{ steps.version.outputs.version }}
|
||||
KILO_CHANNEL: ${{ steps.version.outputs.cli_channel }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
|
||||
- name: Verify plugin
|
||||
working-directory: packages/kilo-jetbrains
|
||||
run: ./gradlew verifyPlugin -Pproduction=true -Pkilo.channel="$CHANNEL"
|
||||
env:
|
||||
CHANNEL: ${{ steps.version.outputs.marketplace_channel }}
|
||||
|
||||
- name: Publish to JetBrains Marketplace
|
||||
working-directory: packages/kilo-jetbrains
|
||||
run: ./gradlew publishPlugin -Pproduction=true -Pkilo.channel="$CHANNEL"
|
||||
env:
|
||||
CHANNEL: ${{ steps.version.outputs.marketplace_channel }}
|
||||
JETBRAINS_MARKETPLACE_TOKEN: ${{ secrets.JETBRAINS_MARKETPLACE_TOKEN }}
|
||||
JETBRAINS_CERTIFICATE_CHAIN: ${{ secrets.JETBRAINS_CERTIFICATE_CHAIN }}
|
||||
JETBRAINS_PRIVATE_KEY: ${{ secrets.JETBRAINS_PRIVATE_KEY }}
|
||||
JETBRAINS_PRIVATE_KEY_PASSWORD: ${{ secrets.JETBRAINS_PRIVATE_KEY_PASSWORD }}
|
||||
|
||||
- name: Resolve plugin archive
|
||||
id: archive
|
||||
run: |
|
||||
mapfile -t signed < <(compgen -G "packages/kilo-jetbrains/build/distributions/*-signed.zip")
|
||||
if [[ "${#signed[@]}" -eq 1 ]]; then
|
||||
echo "path=${signed[0]}" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "${#signed[@]}" -gt 1 ]]; then
|
||||
echo "Expected exactly one signed JetBrains plugin ZIP, found ${#signed[@]}." >&2
|
||||
printf '%s\n' "${signed[@]}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mapfile -t files < <(compgen -G "packages/kilo-jetbrains/build/distributions/*.zip")
|
||||
if [[ "${#files[@]}" -ne 1 ]]; then
|
||||
echo "Expected exactly one JetBrains plugin ZIP, found ${#files[@]}." >&2
|
||||
printf '%s\n' "${files[@]}" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "path=${files[0]}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload to GitHub Release
|
||||
run: |
|
||||
tag="$GITHUB_REF_NAME"
|
||||
title="JetBrains $VERSION"
|
||||
notes="JetBrains plugin $VERSION."
|
||||
if gh release view "$tag" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
|
||||
gh release upload "$tag" "$ARCHIVE" --clobber --repo "$GITHUB_REPOSITORY"
|
||||
exit 0
|
||||
fi
|
||||
gh release create "$tag" "$ARCHIVE" --title "$title" --notes "$notes" --prerelease --repo "$GITHUB_REPOSITORY"
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
ARCHIVE: ${{ steps.archive.outputs.path }}
|
||||
|
||||
- name: Upload workflow artifact
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kilo-jetbrains-${{ steps.version.outputs.version }}
|
||||
path: packages/kilo-jetbrains/build/distributions/*.zip
|
||||
if-no-files-found: ignore
|
||||
@@ -52,6 +52,29 @@ The built plugin archive is at `build/distributions/kilo.jetbrains-<version>.zip
|
||||
|
||||
---
|
||||
|
||||
## Publish release candidates
|
||||
|
||||
JetBrains release-candidate builds publish from tags matching `jetbrains/x.y.z-rc.n`, for example `jetbrains/7.0.1-rc.1`. The git tag is the source of truth for the Marketplace plugin version; production Gradle builds ignore manually supplied version properties and fail if `HEAD` is not tagged with `jetbrains/<version>`. Local dev builds can use the placeholder `0.0.0-dev` version.
|
||||
|
||||
The `publish-jetbrains` workflow publishes RC builds to the JetBrains Marketplace `eap` channel and uploads the same plugin ZIP to a GitHub prerelease for the tag. Stable tags like `jetbrains/x.y.z` are recognized by the workflow but intentionally rejected until default-channel publishing is enabled.
|
||||
|
||||
Testers can install or update EAP builds from this custom plugin repository:
|
||||
|
||||
```
|
||||
https://plugins.jetbrains.com/plugins/eap/list
|
||||
```
|
||||
|
||||
Required GitHub Actions secrets:
|
||||
|
||||
- `JETBRAINS_MARKETPLACE_TOKEN`
|
||||
- `JETBRAINS_CERTIFICATE_CHAIN`
|
||||
- `JETBRAINS_PRIVATE_KEY`
|
||||
- `JETBRAINS_PRIVATE_KEY_PASSWORD`
|
||||
|
||||
Before the first publish, complete `RELEASE_TODO.md`.
|
||||
|
||||
---
|
||||
|
||||
## Run the plugin
|
||||
|
||||
Use the `runIde` Gradle task (available in the Gradle tool window or via the "Run JetBrains Plugin" run configuration) to launch a sandboxed IntelliJ instance with the plugin installed.
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# JetBrains Release Todo
|
||||
|
||||
## One-Time Marketplace Setup
|
||||
|
||||
- Confirm `ai.kilocode.jetbrains` is the final permanent Marketplace plugin ID before the first upload.
|
||||
- Build a production ZIP locally or in CI for the first Marketplace version.
|
||||
- Manually upload the first plugin version in JetBrains Marketplace if the plugin has not been published before.
|
||||
- Confirm the plugin is owned by the correct Kilo vendor or organization.
|
||||
- Confirm the `eap` custom channel exists or is accepted on the first RC upload.
|
||||
- Confirm Marketplace signing requirements before publishing RC builds.
|
||||
|
||||
## One-Time GitHub Setup
|
||||
|
||||
- Create a JetBrains Marketplace permanent token from Marketplace `My Tokens`.
|
||||
- Add `JETBRAINS_MARKETPLACE_TOKEN` to GitHub Actions secrets or the protected environment.
|
||||
- Confirm `GITHUB_TOKEN` has `contents: write` permission for creating and updating GitHub Releases from `jetbrains/*` tags.
|
||||
- Optionally create a protected `jetbrains-marketplace` GitHub Environment with required reviewers.
|
||||
- If using an environment, move the Marketplace and signing secrets there and set the workflow job environment.
|
||||
|
||||
## One-Time Signing Certificate Setup
|
||||
|
||||
- Generate or locate the JetBrains plugin signing private key and certificate chain.
|
||||
- Add `JETBRAINS_CERTIFICATE_CHAIN` with the full certificate chain PEM content.
|
||||
- Add `JETBRAINS_PRIVATE_KEY` with the private key PEM content.
|
||||
- Add `JETBRAINS_PRIVATE_KEY_PASSWORD` with the private key password.
|
||||
- Keep PEM files out of git, local logs, and workflow output.
|
||||
- If GitHub secret input mangles multiline PEM values, store base64-encoded values and verify the Gradle signing task decodes them correctly.
|
||||
|
||||
## Per-RC Release
|
||||
|
||||
- Choose an RC version in the form `x.y.z-rc.n`.
|
||||
- Push tag `jetbrains/x.y.z-rc.n`.
|
||||
- Watch the `publish-jetbrains` workflow.
|
||||
- Download and retain the workflow artifact if needed.
|
||||
- Confirm the update appears on the JetBrains Marketplace `eap` channel.
|
||||
- Confirm the GitHub Release for the `jetbrains/x.y.z-rc.n` tag exists and contains the JetBrains plugin ZIP asset.
|
||||
- Share `https://plugins.jetbrains.com/plugins/eap/list` with testers.
|
||||
|
||||
## Stable Release Guard
|
||||
|
||||
- Stable tags like `jetbrains/x.y.z` are intentionally rejected for now.
|
||||
- Before enabling stable releases, remove the workflow stable guard.
|
||||
- Verify `kilo.channel=default` publishes to the default Marketplace channel.
|
||||
- Update this checklist before stable releases are enabled.
|
||||
@@ -4,7 +4,32 @@ import org.jetbrains.intellij.platform.gradle.tasks.RunIdeTask
|
||||
import org.jetbrains.intellij.platform.gradle.tasks.aware.SplitModeAware.SplitModeTarget
|
||||
|
||||
group = "ai.kilocode.jetbrains"
|
||||
version = "7.0.1"
|
||||
|
||||
fun checked(value: String): String {
|
||||
if (value == "0.0.0-dev") return value
|
||||
require(Regex("^[0-9]+\\.[0-9]+\\.[0-9]+(-rc\\.[0-9]+)?$").matches(value)) {
|
||||
"Invalid JetBrains plugin version: $value"
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
fun gitTag(): String? {
|
||||
val text = providers.exec {
|
||||
commandLine("git", "tag", "--points-at", "HEAD")
|
||||
}.standardOutput.asText.get()
|
||||
return text.lineSequence().map { it.trim() }.firstOrNull { it.startsWith("jetbrains/") }
|
||||
}
|
||||
|
||||
val release = providers.gradleProperty("production").map { it.toBoolean() }.orElse(false).get()
|
||||
val ver = if (release) checked(
|
||||
gitTag()?.removePrefix("jetbrains/")
|
||||
?: error("Missing JetBrains plugin version. Publish builds must run from a jetbrains/<version> tag."),
|
||||
) else checked(gitTag()?.removePrefix("jetbrains/") ?: "0.0.0-dev")
|
||||
|
||||
val notes = providers.gradleProperty("kilo.changeNotes").orElse("Release candidate build.")
|
||||
val channel = providers.gradleProperty("kilo.channel").map { it.trim() }.orElse("default")
|
||||
|
||||
version = ver
|
||||
|
||||
plugins {
|
||||
application
|
||||
@@ -59,9 +84,39 @@ intellijPlatform {
|
||||
splitMode = true
|
||||
splitModeTarget = SplitModeTarget.BOTH
|
||||
|
||||
pluginConfiguration {
|
||||
id = "ai.kilocode.jetbrains"
|
||||
name = "Kilo Code"
|
||||
version = provider { ver }
|
||||
changeNotes = notes
|
||||
|
||||
ideaVersion {
|
||||
untilBuild = provider { null }
|
||||
}
|
||||
|
||||
vendor {
|
||||
name = "Kilo Code"
|
||||
url = "https://kilo.ai"
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
token = providers.environmentVariable("JETBRAINS_MARKETPLACE_TOKEN")
|
||||
channels = channel.map { value ->
|
||||
if (value.isBlank() || value == "default") return@map listOf("default")
|
||||
listOf(value)
|
||||
}
|
||||
}
|
||||
|
||||
signing {
|
||||
certificateChain = providers.environmentVariable("JETBRAINS_CERTIFICATE_CHAIN")
|
||||
privateKey = providers.environmentVariable("JETBRAINS_PRIVATE_KEY")
|
||||
password = providers.environmentVariable("JETBRAINS_PRIVATE_KEY_PASSWORD")
|
||||
}
|
||||
|
||||
pluginVerification {
|
||||
ides {
|
||||
create(IntelliJPlatformType.IntellijIdeaCommunity, libs.versions.intellij.platform)
|
||||
create(IntelliJPlatformType.IntellijIdea, libs.versions.intellij.platform)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user