diff --git a/job-dsl-core/src/main/docs/examples/javaposse/jobdsl/dsl/Folder/icon.groovy b/job-dsl-core/src/main/docs/examples/javaposse/jobdsl/dsl/Folder/icon.groovy
new file mode 100644
index 000000000..899e5fbd7
--- /dev/null
+++ b/job-dsl-core/src/main/docs/examples/javaposse/jobdsl/dsl/Folder/icon.groovy
@@ -0,0 +1,3 @@
+// use the stock icon
+// see https://github.com/jenkinsci/custom-folder-icon-plugin for custom icons
+folder('stock')
diff --git a/job-dsl-core/src/main/groovy/javaposse/jobdsl/dsl/AbstractFolder.groovy b/job-dsl-core/src/main/groovy/javaposse/jobdsl/dsl/AbstractFolder.groovy
index ae2bfe88e..a788bfe2a 100644
--- a/job-dsl-core/src/main/groovy/javaposse/jobdsl/dsl/AbstractFolder.groovy
+++ b/job-dsl-core/src/main/groovy/javaposse/jobdsl/dsl/AbstractFolder.groovy
@@ -1,6 +1,7 @@
package javaposse.jobdsl.dsl
import javaposse.jobdsl.dsl.helpers.AuthorizationContext
+import javaposse.jobdsl.dsl.helpers.icon.FolderIconContext
import javaposse.jobdsl.dsl.helpers.properties.FolderPropertiesContext
abstract class AbstractFolder extends Item {
@@ -54,4 +55,22 @@ abstract class AbstractFolder extends Item {
}
}
}
+
+ /**
+ * Sets the icon of the folder.
+ *
+ * @since 1.83
+ */
+ void icon(@DslContext(FolderIconContext) Closure closure) {
+ FolderIconContext context = new FolderIconContext(jobManagement, this)
+ ContextHelper.executeInContext(closure, context)
+
+ configure { Node folder ->
+ Node icon = folder / icon
+ if (icon) {
+ folder.remove(icon)
+ }
+ folder << context.icon
+ }
+ }
}
diff --git a/job-dsl-core/src/main/groovy/javaposse/jobdsl/dsl/helpers/icon/FolderIconContext.groovy b/job-dsl-core/src/main/groovy/javaposse/jobdsl/dsl/helpers/icon/FolderIconContext.groovy
new file mode 100644
index 000000000..2321243a0
--- /dev/null
+++ b/job-dsl-core/src/main/groovy/javaposse/jobdsl/dsl/helpers/icon/FolderIconContext.groovy
@@ -0,0 +1,21 @@
+package javaposse.jobdsl.dsl.helpers.icon
+
+import javaposse.jobdsl.dsl.AbstractExtensibleContext
+import javaposse.jobdsl.dsl.ContextHelper
+import javaposse.jobdsl.dsl.ContextType
+import javaposse.jobdsl.dsl.Item
+import javaposse.jobdsl.dsl.JobManagement
+
+@ContextType('com.cloudbees.hudson.plugins.folder.FolderIcon')
+class FolderIconContext extends AbstractExtensibleContext {
+ Node icon
+
+ FolderIconContext(JobManagement jobManagement, Item item) {
+ super(jobManagement, item)
+ }
+
+ @Override
+ protected void addExtensionNode(Node node) {
+ icon = ContextHelper.toNamedNode('icon', node)
+ }
+}
diff --git a/job-dsl-core/src/test/groovy/javaposse/jobdsl/dsl/FolderSpec.groovy b/job-dsl-core/src/test/groovy/javaposse/jobdsl/dsl/FolderSpec.groovy
index 1065340db..907cda85d 100644
--- a/job-dsl-core/src/test/groovy/javaposse/jobdsl/dsl/FolderSpec.groovy
+++ b/job-dsl-core/src/test/groovy/javaposse/jobdsl/dsl/FolderSpec.groovy
@@ -127,6 +127,23 @@ class FolderSpec extends Specification {
folder.node.properties[0].children()[0].name() == 'hack'
}
+ def 'call icon'() {
+ when:
+ folder.icon {
+ icon = new Node(null,
+ 'icon', ['class': 'jenkins.plugins.foldericon.CustomFolderIcon', 'plugin': 'custom-folder-icon'],
+ new Node(null,
+ 'customFolderIcon', 'test.png'))
+ }
+
+ then:
+ folder.node.icon[0].name() == 'icon'
+ folder.node.icon[0].attribute('class') == 'jenkins.plugins.foldericon.CustomFolderIcon'
+ folder.node.icon[0].attribute('plugin') == 'custom-folder-icon'
+ folder.node.icon[0].children()[0].name() == 'customFolderIcon'
+ folder.node.icon[0].children()[0].value() == 'test.png'
+ }
+
def 'configure'() {
when:
folder.configure {
diff --git a/job-dsl-plugin/pom.xml b/job-dsl-plugin/pom.xml
index 4de7c6563..503e94458 100644
--- a/job-dsl-plugin/pom.xml
+++ b/job-dsl-plugin/pom.xml
@@ -54,7 +54,7 @@
io.jenkins.tools.bom
bom-2.387.x
- 1887.vda_d0ddb_c15c4
+ 1935.v530f4395930f
pom
import
@@ -139,6 +139,11 @@
test-harness
test
+
+ io.jenkins.plugins
+ custom-folder-icon
+ test
+
org.jenkins-ci.plugins
credentials
diff --git a/job-dsl-plugin/src/test/groovy/javaposse/jobdsl/plugin/ExecuteDslScriptsSpec.groovy b/job-dsl-plugin/src/test/groovy/javaposse/jobdsl/plugin/ExecuteDslScriptsSpec.groovy
index 11ff191ee..024953582 100644
--- a/job-dsl-plugin/src/test/groovy/javaposse/jobdsl/plugin/ExecuteDslScriptsSpec.groovy
+++ b/job-dsl-plugin/src/test/groovy/javaposse/jobdsl/plugin/ExecuteDslScriptsSpec.groovy
@@ -1,9 +1,12 @@
package javaposse.jobdsl.plugin
import com.cloudbees.hudson.plugins.folder.Folder
+import com.cloudbees.hudson.plugins.folder.FolderIcon
+import com.cloudbees.hudson.plugins.folder.icons.StockFolderIcon
import hudson.FilePath
import hudson.model.AbstractItem
import hudson.model.AbstractProject
+import hudson.model.BallColor
import hudson.model.Computer
import hudson.model.FreeStyleBuild
import hudson.model.FreeStyleProject
@@ -25,13 +28,19 @@ import javaposse.jobdsl.plugin.actions.GeneratedViewsAction
import javaposse.jobdsl.plugin.actions.GeneratedViewsBuildAction
import javaposse.jobdsl.plugin.actions.SeedJobAction
import javaposse.jobdsl.plugin.fixtures.ExampleJobDslExtension
+import jenkins.branch.MetadataActionFolderIcon
import jenkins.model.Jenkins
+import jenkins.plugins.foldericon.BuildStatusFolderIcon
+import jenkins.plugins.foldericon.CustomFolderIcon
+import jenkins.plugins.foldericon.EmojiFolderIcon
+import jenkins.plugins.foldericon.IoniconFolderIcon
import jenkins.security.QueueItemAuthenticator
import jenkins.security.QueueItemAuthenticatorConfiguration
import org.acegisecurity.Authentication
import org.jenkinsci.plugins.configfiles.GlobalConfigFiles
import org.jenkinsci.plugins.configfiles.custom.CustomConfig
import org.jenkinsci.plugins.managedscripts.PowerShellConfig
+import org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval
import org.jenkinsci.plugins.scriptsecurity.scripts.languages.GroovyLanguage
import org.junit.ClassRule
import org.junit.Rule
@@ -43,7 +52,6 @@ import org.jvnet.hudson.test.WithoutJenkins
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Unroll
-import org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval
import static hudson.model.Result.FAILURE
import static hudson.model.Result.SUCCESS
@@ -104,6 +112,48 @@ folder('folder-a/folder-b') {
description('lorem ipsum')
}"""
+ private static final String FOLDER_WITH_STOCK_ICON_SCRIPT = """folder('stock') {
+ icon {
+ stockFolderIcon()
+ }
+}"""
+
+ private static final String FOLDER_WITH_METADATA_ICON_SCRIPT = """folder('metadata') {
+ icon {
+ metadataActionFolderIcon()
+ }
+}"""
+
+ private static final String FOLDER_WITH_CUSTOM_ICON_SCRIPT = """folder('custom') {
+ icon {
+ customFolderIcon {
+ foldericon('custom.png')
+ }
+ }
+}"""
+
+ private static final String FOLDER_WITH_IONICON_ICON_SCRIPT = """folder('ionicon') {
+ icon {
+ ioniconFolderIcon {
+ ionicon('jenkins')
+ }
+ }
+}"""
+
+ private static final String FOLDER_WITH_BUILD_STATUS_ICON_SCRIPT = """folder('build-status') {
+ icon {
+ buildStatusFolderIcon()
+ }
+}"""
+
+ private static final String FOLDER_WITH_EMOJI_ICON_SCRIPT = """folder('emoji') {
+ icon {
+ emojiFolderIcon {
+ emoji('sloth')
+ }
+ }
+}"""
+
@Shared
@ClassRule
@SuppressWarnings('JUnitPublicField')
@@ -877,6 +927,106 @@ folder('folder-a/folder-b') {
SeedJobAction).isConfigChanged()
}
+ def createFolderWithStockIcon() {
+ setup:
+ FreeStyleProject job = jenkinsRule.createFreeStyleProject('seed')
+ job.buildersList.add(new ExecuteDslScripts(FOLDER_WITH_STOCK_ICON_SCRIPT))
+
+ when:
+ FreeStyleBuild freeStyleBuild = job.scheduleBuild2(0).get()
+
+ then:
+ freeStyleBuild.result == SUCCESS
+ Item item = jenkinsRule.instance.getItemByFullName('stock')
+ item instanceof Folder
+ FolderIcon icon = ((Folder) item).getIcon()
+ icon instanceof StockFolderIcon
+ }
+
+ def createFolderWithMetadataIcon() {
+ setup:
+ FreeStyleProject job = jenkinsRule.createFreeStyleProject('seed')
+ job.buildersList.add(new ExecuteDslScripts(FOLDER_WITH_METADATA_ICON_SCRIPT))
+
+ when:
+ FreeStyleBuild freeStyleBuild = job.scheduleBuild2(0).get()
+
+ then:
+ freeStyleBuild.result == SUCCESS
+ Item item = jenkinsRule.instance.getItemByFullName('metadata')
+ item instanceof Folder
+ FolderIcon icon = ((Folder) item).getIcon()
+ icon instanceof MetadataActionFolderIcon
+ }
+
+ def createFolderWithCustomIcon() {
+ setup:
+ FreeStyleProject job = jenkinsRule.createFreeStyleProject('seed')
+ job.buildersList.add(new ExecuteDslScripts(FOLDER_WITH_CUSTOM_ICON_SCRIPT))
+
+ when:
+ FreeStyleBuild freeStyleBuild = job.scheduleBuild2(0).get()
+
+ then:
+ freeStyleBuild.result == SUCCESS
+ Item item = jenkinsRule.instance.getItemByFullName('custom')
+ item instanceof Folder
+ FolderIcon icon = ((Folder) item).getIcon()
+ icon instanceof CustomFolderIcon
+ ((CustomFolderIcon) icon).getFoldericon() == 'custom.png'
+ }
+
+ def createFolderWithIoniconIcon() {
+ setup:
+ FreeStyleProject job = jenkinsRule.createFreeStyleProject('seed')
+ job.buildersList.add(new ExecuteDslScripts(FOLDER_WITH_IONICON_ICON_SCRIPT))
+
+ when:
+ FreeStyleBuild freeStyleBuild = job.scheduleBuild2(0).get()
+
+ then:
+ freeStyleBuild.result == SUCCESS
+ Item item = jenkinsRule.instance.getItemByFullName('ionicon')
+ item instanceof Folder
+ FolderIcon icon = ((Folder) item).getIcon()
+ icon instanceof IoniconFolderIcon
+ ((IoniconFolderIcon) icon).getIonicon() == 'jenkins'
+ }
+
+ def createFolderWithBuildStatusIcon() {
+ setup:
+ FreeStyleProject job = jenkinsRule.createFreeStyleProject('seed')
+ job.buildersList.add(new ExecuteDslScripts(FOLDER_WITH_BUILD_STATUS_ICON_SCRIPT))
+
+ when:
+ FreeStyleBuild freeStyleBuild = job.scheduleBuild2(0).get()
+
+ then:
+ freeStyleBuild.result == SUCCESS
+ Item item = jenkinsRule.instance.getItemByFullName('build-status')
+ item instanceof Folder
+ FolderIcon icon = ((Folder) item).getIcon()
+ icon instanceof BuildStatusFolderIcon
+ ((BuildStatusFolderIcon) icon).getIconClassName() == BallColor.NOTBUILT.getIconClassName()
+ }
+
+ def createFolderWithEmojiIcon() {
+ setup:
+ FreeStyleProject job = jenkinsRule.createFreeStyleProject('seed')
+ job.buildersList.add(new ExecuteDslScripts(FOLDER_WITH_EMOJI_ICON_SCRIPT))
+
+ when:
+ FreeStyleBuild freeStyleBuild = job.scheduleBuild2(0).get()
+
+ then:
+ freeStyleBuild.result == SUCCESS
+ Item item = jenkinsRule.instance.getItemByFullName('emoji')
+ item instanceof Folder
+ FolderIcon icon = ((Folder) item).getIcon()
+ icon instanceof EmojiFolderIcon
+ ((EmojiFolderIcon) icon).getEmoji() == 'sloth'
+ }
+
def createJobInFolder() {
setup:
FreeStyleProject job = jenkinsRule.createFreeStyleProject('seed')