Skip to content

Commit 462a18c

Browse files
authored
Merge pull request #80 from openjavaformat/jetbrains-marketplace-publishing
Publish the IntelliJ plugin to the JetBrains Marketplace on release
2 parents b48ba48 + c4a837a commit 462a18c

3 files changed

Lines changed: 78 additions & 6 deletions

File tree

‎.github/workflows/release.yml‎

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@ name: Release
99
# fails publishes nothing, and its deployments can be dropped in the Portal.
1010
#
1111
# Once Maven Central serves both, the Gradle plugins go to the Gradle Plugin Portal, signed with the same
12-
# release key. After publishing, a draft GitHub release on the tag collects the runnable jar, the Gradle
12+
# release key, and the IntelliJ plugin goes to the JetBrains Marketplace, verified against the IDEs it
13+
# supports first. After publishing, a draft GitHub release on the tag collects the runnable jar, the Gradle
1314
# and IDE plugins and the native binaries.
1415
#
15-
# Needs six repository secrets: JRELEASER_MAVENCENTRAL_USERNAME and JRELEASER_MAVENCENTRAL_PASSWORD (the
16-
# Central Portal user token), JRELEASER_GPG_SECRET_KEY and JRELEASER_GPG_PASSPHRASE, and
17-
# GRADLE_PUBLISH_KEY and GRADLE_PUBLISH_SECRET (the Gradle Plugin Portal key). The draft release uses the
18-
# workflow's own GITHUB_TOKEN.
16+
# Needs seven repository secrets: JRELEASER_MAVENCENTRAL_USERNAME and JRELEASER_MAVENCENTRAL_PASSWORD (the
17+
# Central Portal user token), JRELEASER_GPG_SECRET_KEY and JRELEASER_GPG_PASSPHRASE, GRADLE_PUBLISH_KEY and
18+
# GRADLE_PUBLISH_SECRET (the Gradle Plugin Portal key) and JETBRAINS_MARKETPLACE_TOKEN (a permanent token of
19+
# the Marketplace account that owns the plugin). The draft release uses the workflow's own GITHUB_TOKEN.
1920
on:
2021
push:
2122
tags:
@@ -270,6 +271,45 @@ jobs:
270271
JRELEASER_GPG_SECRET_KEY: ${{ secrets.JRELEASER_GPG_SECRET_KEY }}
271272
JRELEASER_GPG_PASSPHRASE: ${{ secrets.JRELEASER_GPG_PASSPHRASE }}
272273

274+
# The IntelliJ plugin carries the formatter jars inside its zip, so it does not wait for Maven Central. It
275+
# waits for the publish job like everything else, so that a version whose jars or native images failed
276+
# stays off the Marketplace too. The plugin is checked against the IDEs it declares support for before it
277+
# goes up; a plugin that fails there would fail JetBrains' own check after the upload anyway. The
278+
# Marketplace never takes a version back and rejects a version it already has, so a re-run of this job
279+
# fails once the upload went through.
280+
jetbrains-plugin:
281+
name: JetBrains Marketplace
282+
needs: publish
283+
runs-on: ubuntu-latest
284+
timeout-minutes: 60
285+
steps:
286+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
287+
with:
288+
fetch-depth: 0
289+
290+
- name: Install JDK 21
291+
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
292+
with:
293+
distribution: temurin
294+
java-version: '21'
295+
296+
- name: Verify the plugin against the IDEs it supports
297+
run: ./gradlew :open-java-format-idea-plugin:verifyPlugin
298+
299+
- name: Keep the verifier's report
300+
if: ${{ failure() }}
301+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
302+
with:
303+
name: plugin-verifier-report
304+
path: open-java-format-idea-plugin/build/reports/pluginVerifier
305+
if-no-files-found: ignore
306+
retention-days: 7
307+
308+
- name: Upload the plugin
309+
run: ./gradlew :open-java-format-idea-plugin:publishPlugin
310+
env:
311+
JETBRAINS_MARKETPLACE_TOKEN: ${{ secrets.JETBRAINS_MARKETPLACE_TOKEN }}
312+
273313
# What Maven Central does not carry — the runnable formatter jar, the Gradle plugins' jar, the IntelliJ
274314
# plugin zip, the Eclipse plugin jar, and every platform's native binary as a plain download — goes into
275315
# a draft GitHub release on the tag, each file signed with the release key. Only once both deployments

‎mise.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,5 @@ apply JRELEASER_GPG_SECRET_KEY 'op://Private/open-java-format/GitHub/JRELEASER_G
8484
apply JRELEASER_GPG_PASSPHRASE 'op://Private/open-java-format/GitHub/JRELEASER_GPG_PASSPHRASE' actions
8585
apply GRADLE_PUBLISH_KEY 'op://Private/open-java-format/GitHub/GRADLE_PUBLISH_KEY' actions
8686
apply GRADLE_PUBLISH_SECRET 'op://Private/open-java-format/GitHub/GRADLE_PUBLISH_SECRET' actions
87+
apply JETBRAINS_MARKETPLACE_TOKEN 'op://Private/open-java-format/GitHub/JETBRAINS_MARKETPLACE_TOKEN' actions
8788
"""

‎open-java-format-idea-plugin/build.gradle‎

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import org.jetbrains.intellij.platform.gradle.IntelliJPlatformType
12
import org.jetbrains.intellij.platform.gradle.TestFrameworkType
3+
import org.jetbrains.intellij.platform.gradle.models.ProductRelease
4+
import org.jetbrains.intellij.platform.gradle.tasks.VerifyPluginTask
25

36
/*
47
* Copyright 2017 Google Inc. All Rights Reserved.
@@ -100,10 +103,38 @@ intellijPlatform {
100103
}
101104
}
102105

106+
// publishPlugin uploads the zip to the JetBrains Marketplace, from the release workflow (see
107+
// .github/workflows/release.yml). The token is a permanent token of the Marketplace account that owns
108+
// the plugin; without it the task fails before uploading anything. The Marketplace never takes a version
109+
// back and rejects a version it already has. The zip goes up unsigned, and the Marketplace signs it with
110+
// the JetBrains key; a signature of our own would take a `signing { }` block here and three more secrets.
103111
publishing {
104-
// Inert until the token is present; wiring the marketplace release is Phase 2 work.
105112
token = providers.environmentVariable('JETBRAINS_MARKETPLACE_TOKEN')
106113
}
114+
115+
// verifyPlugin checks the plugin's bytecode against real IDEs, which it downloads. Two of them: the
116+
// IDE the plugin is built against, which is the oldest one sinceBuild admits, and the newest IntelliJ
117+
// IDEA release. The default, recommended(), would take every major version in between, which is a
118+
// gigabyte per IDE for nothing the two ends do not show, and it stops at 2025.2: since 2025.3 IntelliJ
119+
// IDEA is one product (IntellijIdea), no longer split into Community and Ultimate.
120+
pluginVerification {
121+
// Fail on what would break an installation: classes or methods the IDE no longer has, a dependency
122+
// it lacks, an invalid descriptor. Usages of internal, deprecated or experimental API stay warnings
123+
// in the report: 2026.2 marked every way of looking a plugin descriptor up as @ApiStatus.Internal,
124+
// PluginManager.findEnabledPlugin included, and a warning about that must not hold a release.
125+
failureLevel = [
126+
VerifyPluginTask.FailureLevel.COMPATIBILITY_PROBLEMS,
127+
VerifyPluginTask.FailureLevel.MISSING_DEPENDENCIES,
128+
VerifyPluginTask.FailureLevel.INVALID_PLUGIN,
129+
]
130+
ides {
131+
current()
132+
latest {
133+
it.types.set([IntelliJPlatformType.IntellijIdea])
134+
it.channels.set([ProductRelease.Channel.RELEASE])
135+
}
136+
}
137+
}
107138
}
108139

109140
// This task resolves runtimeClasspath without telling Gradle it depends on it, so the dependent

0 commit comments

Comments
 (0)