Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support icons in folders #1258

Merged
merged 14 commits into from
Mar 22, 2023
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// use the stock icon
// see https://github.com/jenkinsci/custom-folder-icon-plugin for custom icons
folder('stock')
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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
}
}
}
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
7 changes: 6 additions & 1 deletion job-dsl-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
<dependency>
<groupId>io.jenkins.tools.bom</groupId>
<artifactId>bom-2.387.x</artifactId>
<version>1887.vda_d0ddb_c15c4</version>
<version>1935.v530f4395930f</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Expand Down Expand Up @@ -139,6 +139,11 @@
<artifactId>test-harness</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.jenkins.plugins</groupId>
<artifactId>custom-folder-icon</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>credentials</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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')
Expand Down Expand Up @@ -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')
Expand Down