Convention based Cake scripts for building IntelliJ plugins.
- Cake.IntelliJ.Recipe
#load nuget:?package=Cake.IntelliJ.Recipe
#load nuget:?package=Cake.IntelliJ.Recipe
Environment.SetVariableNames();
IntelliJBuildParameters.SetParameters(
context: Context,
buildSystem: BuildSystem,
sourceDirectoryPath: "./src",
title: "Best-Plugin-Ever",
repositoryOwner: "nils-a");
IntelliJBuildParameters.PrintParameters(Context);
ToolSettings.SetToolSettings(context: Context);
IntelliJBuild.Run();
Please be aware, that Cake.IntelliJ.Recipe
wraps a gradle
build
and uses tasks from org.jetbrains.intellij
gradle plugin.
It is advised to create the plugin from https://github.com/JetBrains/intellij-platform-plugin-template.
If you have questions, search for an existing one, or create a new discussion on the Cake GitHub repository, using the extension-q-a
category.
The Cake target IntelliJAnalyze
is run on every build and is used to run code analysis.
The default in this target is to run the following gradle tasks:
detekt
, ktlintCheck
, and verifyPlugin
.
If for some reason this should be changed (e.g. for plugins that do not use detekt and ktlint but rather depend on something different),
the tasks to invoke can be configured using the intelliJAnalyzerTasks
setting.
IntelliJBuildParameters.SetParameters(
// ... all other parameters ...
intelliJAnalyzerTasks: new[]{ "check", "verifyPlugin" }
);
Settings with regard to publishing channels are:
pluginReleaseChannel
with a default of"Stable"
pluginPreReleaseChannel
with a default of"Beta"
pluginCiBuildChannel
with a default of"Alpha"
shouldPublishPluginCiBuilds
with a default offalse
pluginChannelGradleProperty
with a default of"marketplaceChannel"
See Releases and PreReleases for their meaning.
The verbosity of running gradle has it's own setting: gradleVerbosity
. (Default is set to GradleLogLevel.Default
)
Keep in mind, that while setting Cake verbosity to diagnostic
, secrets will still be secret.
However, setting gradle verbosity to GradleLogLevel.Debug
will print out all secrets in the logs.
When publishing is automated, Twitter and Gitter messages can be created. To have them link to the plugin-page in the marketplace,
a setting of marketplaceId
is needed. The marketplaceId
can be fetched from the URL in the marketplace, e.g. for https://plugins.jetbrains.com/plugin/15698-test-rider
, the marketplaceId
is 15698-test-rider
.
All other settings for Twitter, Gitter and such follow Cake.Recipe.
The gradle task runPluginVerifier
runs the JetBrains plugin verifier.
This task is "mapped" to the Cake target Run-Plugin-Verifier
which runs on CI
builds.
If running on CI builds is not desired for any reason, the setting shouldRunPluginVerifier
can be used to disable the run.
Example: Do not run the plugin verifier, if the build runs on Linux (e.g. beacuse the GitHub actions Linux runner does not have enough free space for the plugin verifier.)
IntelliJBuildParameters.SetParameters(
// ... all other parameters ...
shouldRunPluginVerifier: !IsRunningOnLinux()
);
To publish the plugin to the JetBrains Marketplace a token is required.
The token must be supplied in an environment variable and is then picked up in the gradle
build.
Default for plugins created from https://github.com/JetBrains/intellij-platform-plugin-template is to use the PUBLISH_TOKEN
variable name.
Also, as with the "normal" gradle-based publishing, the first publish of the plugin must be made manually.
When creating a new recipe for a plugin that was created from https://github.com/JetBrains/intellij-platform-plugin-template the following changes have to be made:
The standard template makes use of the org.jetbrains.intellij
gradle-plugin to update the <description>
of the plugin.xml
automatically from the content of the Readme.md
which is perfectly fine.
If the Readme.md
were to be moved (say one folder up - to fit in with the recipe structure) that would needed fixing.
The default code in build.gradle.kts
is:
// Extract the <!-- Plugin description --> section from README.md and provide for the plugin's manifest
pluginDescription(
closure {
File("./README.md").readText().lines().run {
val start = "<!-- Plugin description -->"
val end = "<!-- Plugin description end -->"
if (!containsAll(listOf(start, end))) {
throw GradleException("Plugin description section not found in README.md:\n$start ... $end")
}
subList(indexOf(start) + 1, indexOf(end))
}.joinToString("\n").run { markdownToHTML(this) }
}
)
This should point to the new location of the Readme.md
, e.g.:
// ...
File("../README.md").readText().lines().run {
// ...
Alternatively, the whole code-block could be removed from build.gradle.kts
and a <description>
manually added to the plugin.xml
.
Be aware, that the description is html
with all entities encoded. (Something like <description><h3>This is the plugin!</h3></description>
).
The standard template makes use of the org.jetbrains.changelog
gradle-plugin to keep the Changelog.md
updated with current version numbers and to copy the latest changes into the <change-notes>
section of the plugin.xml
on build.
Currently Cake.IntellJ.Recipe
does not bridge the gap between release notes in GitHub releases (as preferred and automatically created by Cake.Recipe
) and having the latest changes shown in the plugin (See Issue 12).
The suggestion is to place a link to the GitHub releases page inside the change-notes
of plugin.xml
.
For that, here are two parts inside build.gradle.kts
which should be removed:
in patchPluginXml
:
// Get the latest available change notes from the changelog file
changeNotes(
closure {
changelog.getLatest().toHTML()
}
)
and in publishPlugin
:
dependsOn("patchChangelog")
And the change-notes
in plugin.xml
have to be added manually. Something like
<change-notes>
<a href="https://github.com/nils-a/test-rider/releases"><h3>See GitHub Releases</h3></a>
</change-notes>
is suggested.
Original Cake.Recipe
knows three types of releases:
- Releases (created by adding a tag to the main branch) will publish a release-package to the release package source (typically NuGet).
- Tagged PreReleases (created by adding a tag on a different (i.e. not the main) branch) will publish a preRelease-package to the release package source (typically NuGet).
- PreReleases generated by CI-builds will publish a preRelease-package to the other package sources (e.g. MyGet, Azure, GPR).
JetBrains Marketplace does not have the notion of preReleases per se. Also there are (short of creating a custom plugin repository) no alternatives to the JetBrains Marketplace.
The Marketplace however does have the notions of "channels". Channels are treated as separate repositories for all intents and purposes and only the default channel (named Stable
) is browsable and searchable.
Cake.IntelliJ.Recipe
has the following settings prepared for the above mentioned release-types:
pluginReleaseChannel
(default:"Stable"
) as the name of the channel to publish releases to.pluginPreReleaseChannel
(default"Beta"
) as the name of the channel to publish tagged preReleases to.pluginCiBuildChannel
(default"Alpha"
) as the name of the channel to publish CI-builds to.
Be aware, that publishes to JetBrains marketplace are moderated so they will not be available to the public instantaneously.
Also, for the very same reason publishing CI builds is deactivated in the defaults. Use shouldPublishPluginCiBuilds
(default false
) to enable it.
To make this work, Cake.IntelliJ.Recipe
will pass the selected channel to gradle via a project property.
The name of that property is set in pluginChannelGradleProperty
(default: "marketplaceChannel"
) and it has to be picked up by the gradle task publishPlugin
in build.gradle.kts
.
The code
publishPlugin {
token(System.getenv("PUBLISH_TOKEN"))
// pluginVersion is based on the SemVer (https://semver.org) and supports pre-release labels, like 2.1.7-alpha.3
// Specify pre-release label to publish the plugin in a custom Release Channel automatically. Read more:
// https://jetbrains.org/intellij/sdk/docs/tutorials/build_system/deployment.html#specifying-a-release-channel
channels(pluginVersion.split('-').getOrElse(1) { "default" }.split('.').first())
}
should be replaced with:
publishPlugin {
token(System.getenv("PUBLISH_TOKEN"))
channels(marketplaceChannel)
}
additionally the line
val marketplaceChannel: String by project
has to be added near the text line "// Import variables from gradle.properties file
".
and also, inside the gradle.properties
a default has to be supplied:
marketplaceChannel = development
Generally everything from Cake.Recipe applies here, too.
There are some modifications to be made to get gradle
and/or java
working correctly. Namely:
- Ensuring a
JAVA_HOME
environment variable that points to thejava
version needed for building of the plugin - Caching
~/.gradle/caches
and~/.gradle/wrapper
TODO: Check why building on windows was so slow in the first tests.
To set the correct java
version, use the following:
# Setup Java 1.8 environment which is needed to build
- name: Setup Java
uses: actions/setup-java@v1
with:
java-version: 1.8
(Remark: choose java
version 1.8
, only if version 1.8
is what is needed to build your plugin.)
Additional caching of gradle is advised.
Also, (and only on GitHub Actions) the use of the Gradle Wrapper Validation Action
is advised to ensure only official versions of graldew
are checked in.
# Validates the gradle wrappers and saves us from getting malicious PRs
- name: Gradle Wrapper Validation
uses: gradle/wrapper-validation-action@v1
# Cache Gradle dependencies
- name: Setup Gradle Dependencies Cache
uses: actions/cache@v2
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-caches-${{ hashFiles('**/*.gradle', '**/*.gradle.kts', 'gradle.properties') }}
# Cache Gradle Wrapper
- name: Setup Gradle Wrapper Cache
uses: actions/cache@v2
with:
path: ~/.gradle/wrapper
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
AppVeyor builds on linux currently fail, due to #15
AppVeyor comes with multiple java
versions (all based on openJDK
) preinstalled.
To select between versions on linux, the stack
definition can be used
stack: jdk 8
To select between version on Windows, the JAVA_HOME needs to be manually set correctly. The Paths are documented
environment:
JAVA_HOME="C:\Program Files\Java\jdk1.8.0"
(Remark: choose java
version 1.8
(or 8
), only if version 1.8
is what is needed to build your plugin.)
Additional caching of gradle is advised. Keep in mind though, that gradle
caches can be quite large and that limitations might apply
cache:
- '%USERPROFILE%\.gradle\caches -> **\*.gradle, **\*.gradle.kts, gradle.properties'
- '%USERPROFILE%\.gradle\wrapper -> **\gradle\wrapper\gradle-wrapper.properties'
Cake.IntelliJ.Recipe follows the Contributor Covenant Code of Conduct.
We accept Pull Requests. Please see the contributing file for how to contribute to Cake.IntelliJ.Recipe.
Small note: If editing the Readme, please conform to the standard-readme specification.
This project follows the all-contributors specification. Contributions of any kind welcome!
Thanks goes to these wonderful people (emoji key):
Nils Andresen 💻 |