Skip to content

Commit

Permalink
Provide support for corporate repositories.
Browse files Browse the repository at this point in the history
Also, upgrade documentr to newer version, as the old one fails to generate output with cryptic message.
  • Loading branch information
piotrtomiak committed Sep 9, 2020
1 parent ec6edcf commit b3ea92d
Show file tree
Hide file tree
Showing 11 changed files with 265 additions and 29 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ The following attributes are a part of the Setup DSL <kbd>intellij { ... }</kbd>
#### Infrastructure Properties
| Attributes | Values |
| :------------- | :--------- |
| <kbd>intellijRepo</kbd>, <kbd>pluginsRepo</kbd>, <kbd>jreRepo</kbd> - Urls of repositories for downloading IDE distributions, plugin dependencies and JetBrains Java Runtime. <br/><br/>|**Acceptable Values:** <br/><kbd>url</kbd><br/><br/>**Default Value:** <kbd>jetbrains.com/intellij-repository</kbd>, <kbd>plugins.jetbrains.com/maven</kbd>, <kbd>jetbrains.bintray.com/intellij-jdk</kbd>|
| <kbd>intellijRepo</kbd>, <kbd>jreRepo</kbd> - Urls of repositories for downloading IDE distributions and JetBrains Java Runtime. <br/><br/>|**Acceptable Values:** <br/><kbd>url</kbd><br/><br/>**Default Value:** <kbd>jetbrains.com/intellij-repository</kbd>, <kbd>jetbrains.bintray.com/intellij-jdk</kbd>|
| <kbd>pluginsRepo { ... }</kbd> - Configure repositories for downloading plugin dependencies. <br/><br/>|**Configuration:** <br/><kbd>marketplace()</kbd> - use Maven repository with plugins listed in the JetBrains marketplace<br/><kbd>maven(repoUrl)</kbd> - use custom Maven repository with plugins<br/><kbd>custom(pluginsXmlUrl)</kbd> - use [custom plugin repository](https://www.jetbrains.com/help/idea/managing-plugins.html) <br/><br/>**Default Configuration:** <kbd>pluginsRepo { marketplace() }</kbd>|
| <kbd>downloadSources</kbd> - Should plugin download IntelliJ sources while initializing Gradle build? <br/><br/>**Notes:** <ul> <li>Since sources are not needed while testing on CI, you can set it to `false` for a particular environment.</li> </ul>|**Acceptable Values:** <kbd>true</kbd> <kbd>false</kbd><br/><br/>**Default Value:** <kbd>true</kbd> if `CI` environment variable is not set|
| <kbd>ideaDependencyCachePath</kbd> - The absolute path to the local directory that should be used for storing IDE distributions. <br/><br/>**Notes:** <ul> <li>Empty value means the Gradle cache directory will be used.</li> </ul>|**Acceptable Values:** <br/><kbd>path</kbd> - `'<example>'`<br/><br/>**Default Value:** none|

Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
plugins {
groovy
id("com.gradle.plugin-publish") version "0.11.0"
id("synapticloop.documentr") version "2.9.0"
id("synapticloop.documentr") version "3.1.0"
`java-gradle-plugin`
maven
id("com.github.breadmoirai.github-release") version "2.2.9"
Expand Down
17 changes: 15 additions & 2 deletions src/docs/configuration.md.templar
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,24 @@ Please see [Plugin Dependencies](http://www.jetbrains.org/intellij/sdk/docs/basi
{-- --------------------------------------------------------------------------------------------------------------- --}
{-- --------------------------------------------------------------------------------------------------------------- --}

| <kbd>intellijRepo</kbd>, <kbd>pluginsRepo</kbd>, <kbd>jreRepo</kbd> - Urls of repositories for downloading IDE distributions, plugin dependencies and JetBrains Java Runtime. <br/><br/>
| <kbd>intellijRepo</kbd>, <kbd>jreRepo</kbd> - Urls of repositories for downloading IDE distributions and JetBrains Java Runtime. <br/><br/>
|
**Acceptable Values:** <br/>
<kbd>url</kbd><br/><br/>
**Default Value:** <kbd>jetbrains.com/intellij-repository</kbd>, <kbd>plugins.jetbrains.com/maven</kbd>, <kbd>jetbrains.bintray.com/intellij-jdk</kbd>
**Default Value:** <kbd>jetbrains.com/intellij-repository</kbd>, <kbd>jetbrains.bintray.com/intellij-jdk</kbd>
|{\n}

{-- --------------------------------------------------------------------------------------------------------------- --}
{-- --------------------------------------------------------------------------------------------------------------- --}

| <kbd>pluginsRepo {"{"} ... }</kbd> - Configure repositories for downloading plugin dependencies. <br/><br/>
|
**Configuration:** <br/>
<kbd>marketplace()</kbd> - use Maven repository with plugins listed in the JetBrains marketplace<br/>
<kbd>maven(repoUrl)</kbd> - use custom Maven repository with plugins<br/>
<kbd>custom(pluginsXmlUrl)</kbd> - use [custom plugin repository](https://www.jetbrains.com/help/idea/managing-plugins.html) <br/>
<br/>
**Default Configuration:** <kbd>pluginsRepo {"{"} marketplace() }</kbd>
|{\n}

{-- --------------------------------------------------------------------------------------------------------------- --}
Expand Down
10 changes: 3 additions & 7 deletions src/main/groovy/org/jetbrains/intellij/IntelliJPlugin.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,8 @@ class IntelliJPlugin implements Plugin<Project> {
configuration.withDependencies { dependencies ->
Utils.info(project, "Configuring plugin dependencies")
def ideVersion = IdeVersion.createIdeVersion(extension.ideaDependency.buildNumber)
def resolver = new PluginDependencyManager(project.gradle.gradleUserHomeDir.absolutePath, extension.ideaDependency)
boolean repoRegistered = false
def resolver = new PluginDependencyManager(project.gradle.gradleUserHomeDir.absolutePath,
extension.ideaDependency, extension.pluginsRepos)
extension.plugins.each {
Utils.info(project, "Configuring plugin $it")
if (it instanceof Project) {
Expand All @@ -242,10 +242,6 @@ class IntelliJPlugin implements Plugin<Project> {
if (!pluginId) {
throw new BuildException("Failed to resolve plugin $it", null)
}
if (!repoRegistered && (pluginVersion || channel)) {
project.repositories.maven { it.url = extension.pluginsRepo }
repoRegistered = true
}
def plugin = resolver.resolve(project, pluginId, pluginVersion, channel)
if (plugin == null) {
throw new BuildException("Failed to resolve plugin $it", null)
Expand Down Expand Up @@ -644,7 +640,7 @@ class IntelliJPlugin implements Plugin<Project> {
def buildPluginTask = project.tasks.findByName(BUILD_PLUGIN_TASK_NAME) as Zip
def distributionFile =
VersionNumber.parse(project.gradle.gradleVersion) >= VersionNumber.parse("5.1")
? buildPluginTask?.archiveFile?.getOrNull()?.asFile : buildPluginTask.archivePath;
? buildPluginTask?.archiveFile?.getOrNull()?.asFile : buildPluginTask.archivePath
return distributionFile?.exists() ? distributionFile : null
})
dependsOn { project.getTasksByName(BUILD_PLUGIN_TASK_NAME, false) }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package org.jetbrains.intellij

import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.tooling.BuildException
import org.jetbrains.annotations.NotNull
import org.jetbrains.intellij.dependency.IdeaDependency
import org.jetbrains.intellij.dependency.PluginDependency
import org.jetbrains.intellij.dependency.*

/**
* Configuration options for the {@link org.jetbrains.intellij.IntelliJPlugin}.
Expand Down Expand Up @@ -81,9 +81,37 @@ class IntelliJPluginExtension {

/**
* Url of repository for downloading plugin dependencies.
*
* @deprecated Use closure syntax to configure multiple repositories
*/
@Deprecated
String pluginsRepo = IntelliJPlugin.DEFAULT_INTELLIJ_PLUGINS_REPO

/**
* Returns object to configure multiple repositories for downloading plugins.
*/
PluginsRepoConfiguration pluginsRepo() {
if (pluginsRepoConfiguration == null) {
pluginsRepoConfiguration = new PluginsRepoConfigurationImpl(project)
}
return pluginsRepoConfiguration
}

/**
* Configure multiple repositories for downloading plugins.
*/
void pluginsRepo(Closure<?> block) {
this.project.configure(pluginsRepo(), block)
}

/**
* Configure multiple repositories for downloading plugins.
*/
void pluginsRepo(Action<PluginsRepoConfiguration> block) {
block.execute(pluginsRepo())
}


/**
* Url of repository for downloading JetBrains Java Runtime.
*/
Expand Down Expand Up @@ -115,6 +143,7 @@ class IntelliJPluginExtension {
private IdeaDependency ideaDependency
private final Set<PluginDependency> pluginDependencies = new HashSet<>()
private boolean pluginDependenciesConfigured = false
private PluginsRepoConfigurationImpl pluginsRepoConfiguration = null

def setExtensionProject(@NotNull Project project) {
this.project = project
Expand Down Expand Up @@ -156,6 +185,12 @@ class IntelliJPluginExtension {
return version
}

String getBuildVersion() {
def buildNumber = getIdeaDependency().buildNumber
def index = buildNumber.indexOf('-')
return index >= 0 ? buildNumber.substring(index + 1) : buildNumber
}

def addPluginDependency(@NotNull PluginDependency pluginDependency) {
pluginDependencies.add(pluginDependency)
}
Expand All @@ -180,7 +215,7 @@ class IntelliJPluginExtension {
this.ideaDependency = ideaDependency
}

def getIdeaDependency() {
IdeaDependency getIdeaDependency() {
if (ideaDependency == null) {
Utils.debug(project, "IDE dependency is resolved", new Throwable())
project.configurations.getByName(IntelliJPlugin.IDEA_CONFIGURATION_NAME).resolve()
Expand All @@ -190,4 +225,34 @@ class IntelliJPluginExtension {
}
return ideaDependency
}

@NotNull
List<PluginsRepository> getPluginsRepos() {
if (pluginsRepoConfiguration == null) {
//noinspection GrDeprecatedAPIUsage
return Collections.singletonList(new MavenPluginsRepository(project, this.pluginsRepo))
}
return pluginsRepoConfiguration.getRepositories()
}

interface PluginsRepoConfiguration {

/**
* Use default marketplace repository
*/
void marketplace()

/**
* Use a Maven repository with plugin artifacts
*/
void maven(@NotNull String url)

/**
* Use custom plugin repository. The URL should point to the `plugins.xml` or `updatePlugins.xml` file.
*/
void custom(@NotNull String url)

List<PluginsRepository> getRepositories()
}

}
32 changes: 31 additions & 1 deletion src/main/groovy/org/jetbrains/intellij/Utils.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ class Utils {
}

static Node parseXml(File file) {
return parseXml(new FileInputStream(file))
}

static Node parseXml(InputStream inputStream) {
def parser = new XmlParser(false, true, true)
parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
parser.setErrorHandler(new ErrorHandler() {
Expand All @@ -163,7 +167,6 @@ class Utils {
throw e
}
})
InputStream inputStream = new FileInputStream(file)
InputSource input = new InputSource(new InputStreamReader(inputStream, "UTF-8"))
input.setEncoding("UTF-8")
try {
Expand Down Expand Up @@ -332,4 +335,31 @@ class Utils {
}
return null
}

@NotNull
static File downloadZipArtifact(@NotNull Project project, @NotNull String repoUrl, @NotNull String repoPattern,
@NotNull String dependency) {
debug(project, "Adding pseudo-Ivy repository to download $dependency - $repoUrl/$repoPattern")
def pseudoRepo = project.repositories.ivy {
url repoUrl
patternLayout {
artifact repoPattern
}
metadataSources {
artifact()
}
}
def projectDependency = project.dependencies.create("$dependency@zip")
def zipFile = null
try {
def configuration = project.configurations.detachedConfiguration(projectDependency)
zipFile = configuration.singleFile
debug(project, "Downloaded zip artifact: $zipFile")
} catch (Exception ignored) {

}
debug(project, "Removing pseudo-Ivy repository $repoUrl/$repoPattern")
project.repositories.remove(pseudoRepo)
return zipFile
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.jetbrains.intellij.dependency

import org.gradle.api.Project
import org.jetbrains.annotations.NotNull
import org.jetbrains.annotations.Nullable
import org.jetbrains.intellij.Utils

class CustomPluginsRepository implements PluginsRepository {

private final Project project
private final String repoUrl
private final Node pluginsXml

CustomPluginsRepository(@NotNull Project project, @NotNull String repoUrl) {
this.project = project
def pluginsXmlUrl
if (repoUrl.endsWith(".xml")) {
this.repoUrl = repoUrl.substring(0, repoUrl.lastIndexOf('/'))
pluginsXmlUrl = repoUrl
} else {
this.repoUrl = repoUrl
pluginsXmlUrl = repoUrl + "/updatePlugins.xml"
}
Utils.debug(project, "Loading list of plugins from: ${pluginsXmlUrl}")
pluginsXml = Utils.parseXml(new URI("${pluginsXmlUrl}").toURL().openStream())
}

@Nullable
File resolve(@NotNull String id, @NotNull String version, @Nullable String channel) {
String downloadUrl = pluginsXml.category."idea-plugin"
.find { it.id.text().equalsIgnoreCase(id) && it.version.text() == version }
?."download-url"?.text()
if (downloadUrl == null) return null

return Utils.downloadZipArtifact(project, repoUrl,
downloadUrl.replace("${version}.zip", "[revision].[ext]"),
"com.jetbrains.plugins:$id:$version")
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.jetbrains.intellij.dependency

import org.gradle.api.Project
import org.jetbrains.annotations.NotNull
import org.jetbrains.annotations.Nullable
import org.jetbrains.intellij.Utils

class MavenPluginsRepository implements PluginsRepository {

private final Project project
private final String repoUrl

MavenPluginsRepository(@NotNull Project project, @NotNull String repoUrl) {
this.repoUrl = repoUrl
this.project = project
}

@Nullable
File resolve(@NotNull String id, @NotNull String version, @Nullable String channel) {
def dependency = project.dependencies.create(PluginDependencyManager.pluginDependency(id, version, channel))

Utils.debug(project, "Adding Maven repository to download $dependency - $repoUrl")
def mavenRepo = project.repositories.maven { it.url = repoUrl }

def pluginFile = null
try {
def configuration = project.configurations.detachedConfiguration(dependency)
pluginFile = configuration.singleFile
} catch (Exception ignored) {
}

Utils.debug(project, "Removing Maven repository $repoUrl")
project.repositories.remove(mavenRepo)

return pluginFile
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ class PluginDependencyManager {
private final String cacheDirectoryPath
private final String mavenCacheDirectoryPath
private final IdeaDependency ideaDependency
private final List<PluginsRepository> pluginsRepositories

private Set<String> pluginSources = new HashSet<>()
private IvyArtifactRepository ivyArtifactRepository

PluginDependencyManager(@NotNull String gradleHomePath, @Nullable IdeaDependency ideaDependency) {
PluginDependencyManager(@NotNull String gradleHomePath, @Nullable IdeaDependency ideaDependency,
@NotNull List<PluginsRepository> pluginsRepositories) {
this.ideaDependency = ideaDependency
this.pluginsRepositories = pluginsRepositories
// todo: a better way to define cache directory
mavenCacheDirectoryPath = Paths.get(gradleHomePath, 'caches/modules-2/files-2.1').toString()
cacheDirectoryPath = Paths.get(mavenCacheDirectoryPath, 'com.jetbrains.intellij.idea').toString()
Expand All @@ -44,7 +47,18 @@ class PluginDependencyManager {
}
throw new BuildException("Cannot find builtin plugin $id for IDE: $ideaDependency.classes.absolutePath", null)
}
return resolveRemote(project, id, version, channel)
for (def repo in this.pluginsRepositories) {
def pluginFile = repo.resolve(id, version, channel)
if (pluginFile != null) {
if (pluginFile != null && Utils.isZipFile(pluginFile)) {
return zippedPluginDependency(project, pluginFile, null)
} else if (Utils.isJarFile(pluginFile)) {
return externalPluginDependency(project, pluginFile, channel, true)
}
throw new BuildException("Invalid type of downloaded plugin: $pluginFile.name", null)
}
}
throw new BuildException("Cannot resolve plugin $id version $version ${channel != null ? "from channel $channel" : ""}", null)
}

void register(@NotNull Project project, @NotNull PluginDependency plugin, @NotNull DependencySet dependencies) {
Expand All @@ -60,17 +74,9 @@ class PluginDependencyManager {
}

@NotNull
private PluginDependency resolveRemote(@NotNull Project project, @NotNull String id, @NotNull String version, @Nullable String channel) {
def dependency = project.dependencies.create(pluginDependency(id, version, channel))
def configuration = project.configurations.detachedConfiguration(dependency)
def pluginFile = configuration.singleFile
if (Utils.isZipFile(pluginFile)) {
def pluginDir = findSingleDirectory(Utils.unzip(pluginFile, new File(cacheDirectoryPath, groupId(channel)), project, null, null))
return externalPluginDependency(project, pluginDir, channel, true)
} else if (Utils.isJarFile(pluginFile)) {
return externalPluginDependency(project, pluginFile, channel, true)
}
throw new BuildException("Invalid type of downloaded plugin: $pluginFile.name", null)
private PluginDependency zippedPluginDependency(Project project, File pluginFile, String channel) {
def pluginDir = findSingleDirectory(Utils.unzip(pluginFile, new File(cacheDirectoryPath, groupId(channel)), project, null, null))
return externalPluginDependency(project, pluginDir, channel, true)
}

private static String groupId(@Nullable String channel) {
Expand Down Expand Up @@ -158,7 +164,7 @@ class PluginDependencyManager {
return null
}

private static def pluginDependency(@NotNull String id, @NotNull String version, @Nullable String channel) {
protected static def pluginDependency(@NotNull String id, @NotNull String version, @Nullable String channel) {
def groupPrefix = channel ? "$channel." : ""
return "${groupPrefix}com.jetbrains.plugins:$id:$version"
}
Expand Down
Loading

0 comments on commit b3ea92d

Please sign in to comment.