├── src
├── main
│ ├── webapp
│ │ ├── css
│ │ │ └── combo-box.css
│ │ ├── help-homeDirVarName.html
│ │ ├── examples
│ │ │ ├── Xcode.groovy
│ │ │ ├── UnityOnWindows.groovy
│ │ │ └── Unity.groovy
│ │ └── help-detectionScript.html
│ ├── resources
│ │ ├── org
│ │ │ └── jenkinsci
│ │ │ │ └── plugins
│ │ │ │ └── appdetector
│ │ │ │ ├── task
│ │ │ │ └── template.groovy.vm
│ │ │ │ ├── Messages.properties
│ │ │ │ ├── AppDetectorParamaterDefinition
│ │ │ │ ├── index.jelly
│ │ │ │ └── config.jelly
│ │ │ │ └── AppDetectorBuildWrapper
│ │ │ │ ├── config.jelly
│ │ │ │ └── global.jelly
│ │ └── index.jelly
│ └── java
│ │ └── org
│ │ └── jenkinsci
│ │ └── plugins
│ │ └── appdetector
│ │ ├── AppUsageSetting.java
│ │ ├── AppLabelAtom.java
│ │ ├── AppDetectionSetting.java
│ │ ├── task
│ │ └── AppDetectionTask.java
│ │ ├── util
│ │ └── Utils.java
│ │ ├── AppLabelSet.java
│ │ ├── AppDetectorHandler.java
│ │ ├── AppDetectorLabelFinder.java
│ │ ├── AppDetectorParamaterDefinition.java
│ │ └── AppDetectorBuildWrapper.java
└── test
│ ├── resources
│ └── org
│ │ └── jenkinsci
│ │ └── plugins
│ │ └── appdetector
│ │ └── task
│ │ └── test.groovy
│ └── java
│ └── org
│ └── jenkinsci
│ └── plugins
│ └── appdetector
│ ├── AppLabelSetTest.java
│ ├── AppDetectorParamaterDefinitionTest.java
│ ├── task
│ └── AppDetectionTaskTest.java
│ ├── util
│ └── UtilsTest.java
│ └── AppDetectorHandlerTest.java
├── readme
├── job_config.png
├── global_config.png
└── build_parameter.png
├── .gitignore
├── release.properties
├── README.md
├── pom.xml
└── config
└── google_checks.xml
/src/main/webapp/css/combo-box.css:
--------------------------------------------------------------------------------
1 | .comboBoxItem {
2 | color: black;
3 | }
4 |
--------------------------------------------------------------------------------
/readme/job_config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/app-detector-plugin/master/readme/job_config.png
--------------------------------------------------------------------------------
/readme/global_config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/app-detector-plugin/master/readme/global_config.png
--------------------------------------------------------------------------------
/readme/build_parameter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/app-detector-plugin/master/readme/build_parameter.png
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/appdetector/task/template.groovy.vm:
--------------------------------------------------------------------------------
1 | import groovy.json.*;
2 |
3 | def platform = "${platform}";
4 |
5 | ${scriptBody}
6 |
--------------------------------------------------------------------------------
/src/main/resources/index.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 | Allow to build on a slave with specific applications and versions present
4 |
5 |
--------------------------------------------------------------------------------
/src/test/resources/org/jenkinsci/plugins/appdetector/task/test.groovy:
--------------------------------------------------------------------------------
1 | def applist = [];
2 |
3 | applist.add([version: "1.0", home: "/hoge"]);
4 |
5 | return JsonOutput.prettyPrint(JsonOutput.toJson(applist));
6 |
--------------------------------------------------------------------------------
/src/main/webapp/help-homeDirVarName.html:
--------------------------------------------------------------------------------
1 |
2 | Environment variable name for binding application home directory.
3 |
4 |
5 | You can refer to the home directory of the application through variables set during execution of the build.
6 |
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 |
3 | # Mobile Tools for Java (J2ME)
4 | .mtj.tmp/
5 |
6 | # Package Files #
7 | *.jar
8 | *.war
9 | *.ear
10 |
11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
12 | hs_err_pid*
13 |
14 | target
15 | work
16 | .DS_Store
17 |
--------------------------------------------------------------------------------
/release.properties:
--------------------------------------------------------------------------------
1 | #release configuration
2 | #Mon Nov 19 19:30:43 JST 2018
3 | scm.tagNameFormat=@{project.artifactId}-@{project.version}
4 | pushChanges=true
5 | scm.url=scm\:git\:git@github.com\:jenkinsci/app-detector-plugin.git
6 | preparationGoals=clean install
7 | remoteTagging=true
8 | projectVersionPolicyId=default
9 | scm.commentPrefix=[maven-release-plugin]
10 | exec.additionalArguments=
11 | exec.snapshotReleasePluginAllowed=false
12 | completedPhase=check-poms
13 |
--------------------------------------------------------------------------------
/src/main/webapp/examples/Xcode.groovy:
--------------------------------------------------------------------------------
1 | def xcodeList = [];
2 |
3 | def xcodePathList = ["/usr/bin/mdfind", "kMDItemCFBundleIdentifier == 'com.apple.dt.Xcode'"].execute().text.split("\n");
4 |
5 | xcodePathList.each {
6 | def version = ["env", "DEVELOPER_DIR=" + it, "/usr/bin/xcodebuild", "-version"].execute().text.split("\n")[0].split(" ")[1];
7 | xcodeList.add([version: version, home: it]);
8 | }
9 |
10 | return JsonOutput.prettyPrint(JsonOutput.toJson(xcodeList));
11 |
--------------------------------------------------------------------------------
/src/main/webapp/examples/UnityOnWindows.groovy:
--------------------------------------------------------------------------------
1 | def unityList = [];
2 | def appDir = "C:\\Program Files";
3 |
4 | def unityPathList = ["cmd", "/c", "dir", appDir, "/A:d", "/b"].execute().text.split("\r\n").findAll { it.startsWith("Unity")};
5 |
6 | unityPathList.each {
7 | def version = it.minus("Unity");
8 | def unityHome = appDir + "\\" + it;
9 | unityList.add([version: version, home: unityHome]);
10 | }
11 |
12 | return JsonOutput.prettyPrint(JsonOutput.toJson(unityList));
13 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/appdetector/AppUsageSetting.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.appdetector;
2 |
3 | public class AppUsageSetting {
4 | private String appName;
5 | private String version;
6 |
7 | public AppUsageSetting(String appName, String version) {
8 | this.appName = appName;
9 | this.version = version;
10 | }
11 |
12 | public String getAppName() {
13 | return appName;
14 | }
15 |
16 | public String getVersion() {
17 | return version;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/appdetector/Messages.properties:
--------------------------------------------------------------------------------
1 | # Job config
2 | AppDetectorParamaterDefinition_DisplayName=Chois Application Version
3 | JOB_DESCRIPTION=build with the specific application and version
4 |
5 | # label finding
6 | CANNOT_GET_HUDSON_INSTANCE=Could not get any hudson instances
7 | DETECTING_SOFTOWARE_INSTLLATION_FAILED=Detecting the {0} installation was failed on {1}.
8 |
9 | # executing
10 | GETTING_NODE_FAILED=Failed to get the node from this build.
11 | APP_NOT_FOUND=Required application ({0}: {1}) was not found on {2}.
12 |
--------------------------------------------------------------------------------
/src/main/webapp/examples/Unity.groovy:
--------------------------------------------------------------------------------
1 | def unityList = [];
2 | def appDir = "/Applications/";
3 |
4 | def unityPathList = ["ls", appDir].execute().text.split("\n").findAll { it.startsWith("Unity")};
5 |
6 | unityPathList.each {
7 | def version = ["/usr/libexec/PlistBuddy", "-c", "Print :CFBundleVersion", appDir + it + "/Unity.app/Contents/Info.plist"].execute().text.split("\n")[0];
8 | def unityHome = appDir + it + "/Unity.app";
9 | unityList.add([version: version, home: unityHome]);
10 | }
11 |
12 | return JsonOutput.prettyPrint(JsonOutput.toJson(unityList));
13 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/appdetector/AppDetectorParamaterDefinition/index.jelly:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | ${value}
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/main/webapp/help-detectionScript.html:
--------------------------------------------------------------------------------
1 |
2 | Groovy script to detect which version of which application is installed for each slave.
3 |
4 |
5 | The script MUST return a JSON string of the form:
6 |
7 |
8 | [
9 | {
10 | "version": "Application Version",
11 | "home": "Home Directory of This Version"
12 | },
13 | ・・・
14 | ]
15 |
16 |
17 | You can use the 'runExternalCommand()' method to execute external commands and get output.
18 | And you can use the 'platform' variable to determine the platform in the script. ("windows", "linux", or "osx" will be stored)
19 |
20 |
21 | Please refer to the example below.
22 |
26 |
27 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/appdetector/AppDetectorBuildWrapper/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/appdetector/AppLabelAtom.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.appdetector;
2 |
3 | import hudson.model.labels.LabelAtom;
4 |
5 | public class AppLabelAtom extends LabelAtom {
6 |
7 | private static final long serialVersionUID = 1L;
8 |
9 | private String application;
10 | private String version;
11 | private String home;
12 |
13 | /**
14 | * Creates new {@link AppLabelAtom} instance.
15 | * @param application The name of application such as "Xcode", "Unity".
16 | * @param version Application version.
17 | * @param home Application home directory.
18 | */
19 | public AppLabelAtom(String application, String version, String home) {
20 | super(application + "-" + version);
21 | this.application = application;
22 | this.version = version;
23 | this.home = home;
24 | }
25 |
26 | public String getApplication() {
27 | return application;
28 | }
29 |
30 | public String getVersion() {
31 | return version;
32 | }
33 |
34 | public String getHome() {
35 | return home;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/appdetector/AppDetectorParamaterDefinition/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/test/java/org/jenkinsci/plugins/appdetector/AppLabelSetTest.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.appdetector;
2 |
3 | import org.junit.*;
4 | import static org.hamcrest.CoreMatchers.*;
5 | import static org.junit.Assert.*;
6 |
7 | import java.util.Set;
8 |
9 | public class AppLabelSetTest {
10 | private AppLabelSet labels;
11 |
12 | @Before
13 | public void init() {
14 | labels = new AppLabelSet();
15 | labels.add(new AppLabelAtom("Xcode", "8.0", "/Applications/Xcode.app"));
16 | labels.add(new AppLabelAtom("Xcode", "7.3.1", "/"));
17 | labels.add(new AppLabelAtom("Unity", "5.4.0b24", "/"));
18 | labels.add(new AppLabelAtom("Unity", "5.3.5f1", "/"));
19 | }
20 |
21 | @Test
22 | public void getAppVersions() throws Exception {
23 | Set xcodeVersions = labels.getAppVersions("Xcode");
24 |
25 | assertThat(xcodeVersions, hasItems("8.0", "7.3.1"));
26 | assertThat(xcodeVersions, not(anyOf(hasItems("5.4.0b24"), hasItems("5.3.5f1"))));
27 | }
28 |
29 | @Test
30 | public void getApplicationLabel() throws Exception {
31 | AppLabelAtom label = labels.getApplicationLabel("Xcode", "8.0");
32 |
33 | assertThat(label, notNullValue());
34 | assertThat(label.getApplication(), is("Xcode"));
35 | assertThat(label.getVersion(), is("8.0"));
36 | assertThat(label.getHome(), is("/Applications/Xcode.app"));
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/test/java/org/jenkinsci/plugins/appdetector/AppDetectorParamaterDefinitionTest.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.appdetector;
2 |
3 | import mockit.Expectations;
4 | import mockit.Mocked;
5 | import org.junit.*;
6 | import static org.hamcrest.CoreMatchers.*;
7 | import static org.hamcrest.Matchers.contains;
8 | import static org.junit.Assert.*;
9 | import org.jenkinsci.plugins.appdetector.util.Utils;
10 |
11 | import java.util.ArrayList;
12 | import java.util.HashSet;
13 | import java.util.List;
14 | import java.util.Set;
15 |
16 |
17 | public class AppDetectorParamaterDefinitionTest {
18 | private AppDetectorParamaterDefinition param;
19 |
20 | @Mocked
21 | final Utils utils = null;
22 |
23 | @Mocked
24 | private AppLabelSet labels;
25 |
26 | @Before
27 | public void init() {
28 | param = new AppDetectorParamaterDefinition("test", "test", "");
29 | }
30 |
31 | @Test
32 | public void getSortedVersionList() throws Exception {
33 |
34 | new Expectations() {{
35 | Utils.getApplicationLabels();
36 | result = labels;
37 |
38 | labels.getSortedAppVersions("test");
39 | result = new ArrayList() {
40 | {
41 | add("8.3.1b");
42 | add("8.3");
43 | add("8");
44 | }
45 | };
46 | }};
47 |
48 | List actual = param.getSortedVersionList();
49 | String[] expected = {"8.3.1b", "8.3", "8"};
50 | assertThat(actual, is(contains(expected)));
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/appdetector/AppDetectionSetting.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.appdetector;
2 |
3 | public class AppDetectionSetting {
4 | private String appName;
5 | private String script;
6 | private boolean onLinux;
7 | private boolean onOsx;
8 | private boolean onWindows;
9 | private String homeDirVarName;
10 |
11 | /**
12 | * Construct an AppDetectionSetting Object.
13 | * @param appName Application name to be detected.
14 | * @param script Groovy script used to detect applications.
15 | * @param onLinux Whether to perform detection on Linux.
16 | * @param onOsx Whether to perform detection on Mac.
17 | * @param onWindows Whether to perform detection on Windows.
18 | * @param homeDirVarName Environment variable name to bind the application's home directory.
19 | */
20 | public AppDetectionSetting(String appName, String script, boolean onLinux, boolean onOsx,
21 | boolean onWindows, String homeDirVarName) {
22 |
23 | this.appName = appName;
24 | this.script = script;
25 | this.onLinux = onLinux;
26 | this.onOsx = onOsx;
27 | this.onWindows = onWindows;
28 | this.homeDirVarName = homeDirVarName;
29 | }
30 |
31 | public String getAppName() {
32 | return appName;
33 | }
34 |
35 | public String getScript() {
36 | return script;
37 | }
38 |
39 | public boolean getOnLinux() {
40 | return onLinux;
41 | }
42 |
43 | public boolean getOnOsx() {
44 | return onOsx;
45 | }
46 |
47 | public boolean getOnWindows() {
48 | return onWindows;
49 | }
50 |
51 | public String getHomeDirVarName() {
52 | return homeDirVarName;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Application Detector Plugin
2 | ====================
3 |
4 | This plugin provides the following functions.
5 |
6 | - Detects an application and its versions installed in slaves, by using a groovy script.
7 | - Enables you to execute builds by specifying the detected application and its version.
8 |
9 | ## Usage
10 | First, You need to register the detection setting on the Jenkins global setting page, as follows.
11 |
12 | 
13 |
14 | And restart Jenkins, or reconnect slaves to reflect the detection setting.
15 |
16 | **NOTE: Detection setting is not reflected until disconnecting the node and connecting it again.**
17 |
18 | Then, you can specfiy some applications and versions at job setting.
19 | 
20 |
21 | Or, Select it runtime by using "Choice Application Version" build parameter.
22 | 
23 |
24 |
25 | ### About Detection Script
26 | - Script MUST return a JSON string of the form:
27 | ```json
28 | [
29 | {
30 | "version": "Application Version",
31 | "home": "Home Directory of This Version"
32 | },
33 | ]
34 | ```
35 | - You can use the '[cmd, arg1, arg2 ...].execute().text' method to execute external commands and get output.
36 | - And you can use the 'platform' variable to determine the platform in the script. ("windows", "linux", or "osx" will be stored)
37 |
38 | #### Sample
39 | Here is some sample scripts.
40 |
41 | - [Unity](/src/main/webapp/examples/Unity.groovy)
42 | - [Unity(on Windows)](/src/main/webapp/examples/UnityOnWindows.groovy)
43 | - [Xcode](/src/main/webapp/examples/Xcode.groovy)
44 |
45 | ## Licence
46 |
47 | [MIT](https://github.com/tcnksm/tool/blob/master/LICENCE)
48 |
49 | ## Author
50 |
51 | [justice3120](https://github.com/justice3120)
52 |
--------------------------------------------------------------------------------
/src/test/java/org/jenkinsci/plugins/appdetector/task/AppDetectionTaskTest.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.appdetector.task;
2 |
3 | import mockit.Expectations;
4 | import mockit.Mocked;
5 | import org.jenkinsci.plugins.appdetector.AppDetectionSetting;
6 | import org.jenkinsci.plugins.appdetector.util.Utils;
7 | import org.junit.*;
8 | import static org.hamcrest.MatcherAssert.*;
9 | import static org.hamcrest.Matchers.*;
10 | import net.sf.json.JSONArray;
11 | import net.sf.json.JSONObject;
12 |
13 | import java.util.Scanner;
14 | import java.util.Set;
15 | import java.io.InputStream;
16 |
17 | public class AppDetectionTaskTest {
18 |
19 | private AppDetectionTask task;
20 |
21 | @Mocked
22 | final Utils utils = null;
23 |
24 | @Before
25 | public void init() {
26 | InputStream in = this.getClass().getResourceAsStream("test.groovy");
27 | String script = new Scanner(in, "UTF-8").useDelimiter("\\A").next();
28 | AppDetectionSetting setting = new AppDetectionSetting("Test", script, false, true, false, "TEST");
29 | task = new AppDetectionTask(setting);
30 | }
31 |
32 | @Test
33 | public void call() throws Exception {
34 |
35 | new Expectations() {{
36 | Utils.runExternalCommand("uname");
37 | result = "Darwin\n";
38 | }};
39 |
40 | JSONArray result = JSONArray.fromObject(task.call());
41 | JSONObject expectedItem = new JSONObject();
42 | expectedItem.put("version", "1.0");
43 | expectedItem.put("home", "/hoge");
44 |
45 | assertThat(result, hasItem(expectedItem));
46 | }
47 |
48 | @Test
49 | public void callWhenPlatformNotMatched() throws Exception {
50 |
51 | new Expectations() {{
52 | Utils.runExternalCommand("uname");
53 | result = "Linux\n";
54 | }};
55 |
56 | JSONArray result = JSONArray.fromObject(task.call());
57 |
58 | assertThat(result, is(empty()));
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/appdetector/AppDetectorBuildWrapper/global.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Note: Detection setting is not reflected until disconnecting the node and connecting it again.
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | Linux
21 |
22 | OSX
23 |
24 | Windows
25 |
26 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/appdetector/task/AppDetectionTask.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.appdetector.task;
2 |
3 | import groovy.lang.GroovyShell;
4 | import jenkins.security.MasterToSlaveCallable;
5 | import org.apache.velocity.VelocityContext;
6 | import org.apache.velocity.app.Velocity;
7 | import org.jenkinsci.plugins.appdetector.AppDetectionSetting;
8 | import org.jenkinsci.plugins.appdetector.util.Utils;
9 |
10 | import java.io.InputStream;
11 | import java.io.StringWriter;
12 | import java.net.URL;
13 | import java.util.Scanner;
14 |
15 | public class AppDetectionTask extends MasterToSlaveCallable {
16 | private String appName;
17 | private String scriptString;
18 | private boolean onLinux;
19 | private boolean onOsx;
20 | private boolean onWindows;
21 |
22 | /**
23 | * Construct an AppDetectionTask Object.
24 | * @param setting Detection method setting.
25 | */
26 | public AppDetectionTask(AppDetectionSetting setting) {
27 | this.appName = setting.getAppName();
28 | this.scriptString = setting.getScript();
29 | this.onLinux = setting.getOnLinux();
30 | this.onOsx = setting.getOnOsx();
31 | this.onWindows = setting.getOnWindows();
32 | }
33 |
34 | @Override
35 | public String call() throws Exception {
36 | String result = "[]";
37 | String platform = getPlatform();
38 |
39 | if ("osx".equals(platform)) {
40 | if (! onOsx) {
41 | return result;
42 | }
43 | } else if ("linux".equals(platform)) {
44 | if (! onLinux) {
45 | return result;
46 | }
47 | } else {
48 | if (! onWindows) {
49 | return result;
50 | }
51 | }
52 |
53 | String templateString = loadTemplateFile();
54 |
55 | StringWriter writer = new StringWriter();
56 | VelocityContext context = new VelocityContext();
57 | context.put("platform", platform);
58 | context.put("scriptBody", scriptString);
59 |
60 | Velocity.evaluate(context, writer, "", templateString);
61 |
62 | GroovyShell shell = new GroovyShell(this.getClass().getClassLoader());
63 | groovy.lang.Script script = shell.parse(writer.toString());
64 | result = (String) script.run();
65 |
66 | return result;
67 | }
68 |
69 | private String getPlatform() {
70 | try {
71 | String uname = Utils.runExternalCommand("uname").replace("\n","");
72 | if ("Darwin".equals(uname)) {
73 | return "osx";
74 | }
75 | return "linux";
76 | } catch (Exception e) {
77 | return "windows";
78 | }
79 | }
80 |
81 | private String loadTemplateFile() throws Exception {
82 | URL templateUrl = AppDetectionTask.class.getResource("template.groovy.vm");
83 | InputStream in = templateUrl.openConnection().getInputStream();
84 | String templateString = new Scanner(in, "UTF-8").useDelimiter("\\A").next();
85 | return templateString;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/test/java/org/jenkinsci/plugins/appdetector/util/UtilsTest.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.appdetector.util;
2 |
3 | import hudson.model.Computer;
4 | import hudson.model.Node;
5 | import mockit.Expectations;
6 | import mockit.Mocked;
7 | import org.junit.*;
8 | import static org.hamcrest.CoreMatchers.*;
9 | import static org.junit.Assert.*;
10 | import org.jenkinsci.plugins.appdetector.AppLabelAtom;
11 | import org.jenkinsci.plugins.appdetector.AppLabelSet;
12 | import jenkins.model.Jenkins;
13 |
14 | import java.util.Arrays;
15 | import java.util.Map;
16 | import java.util.HashMap;
17 | import java.util.HashSet;
18 | import java.util.Set;
19 |
20 | public class UtilsTest {
21 | @Mocked
22 | private Jenkins jenkins;
23 |
24 | @Mocked
25 | private Computer computer;
26 |
27 | @Mocked
28 | private Node node;
29 |
30 | @Mocked
31 | private AppLabelAtom label;
32 |
33 | @Test
34 | public void expandVariablesWithBuildVarToken() throws Exception {
35 | Map buildVers = new HashMap();
36 | buildVers.put("hoge", "8.0");
37 | String token = "${hoge}";
38 |
39 | String expanded = Utils.expandVariables(buildVers, token);
40 |
41 | assertThat(expanded, is("8.0"));
42 | }
43 |
44 | @Test
45 | public void expandVariablesWithPlainToken() {
46 | Map buildVers = new HashMap();
47 | buildVers.put("hoge", "8.0");
48 | String token = "hoge";
49 |
50 | String expanded = Utils.expandVariables(buildVers, token);
51 |
52 | assertThat(expanded, is("hoge"));
53 | }
54 |
55 | @Test
56 | public void getAllComputers() throws Exception {
57 |
58 | new Expectations() {{
59 | Jenkins.getInstance();
60 | result = jenkins;
61 |
62 | jenkins.getComputers();
63 | result = new Computer[] { computer };
64 | }};
65 |
66 | Computer[] allComputers = Utils.getAllComputers();
67 | assertThat(allComputers.length, is(1));
68 | assertThat(Arrays.asList(allComputers), hasItem(computer));
69 | }
70 |
71 | @Test
72 | public void getApplicationLabels() throws Exception {
73 | final Set assignedLabels = new HashSet();
74 | assignedLabels.add(label);
75 |
76 | new Expectations() {{
77 | Jenkins.getInstance();
78 | result = jenkins;
79 |
80 | jenkins.getComputers();
81 | result = new Computer[] { computer };
82 |
83 | computer.getNode();
84 | result = node;
85 |
86 | node.getAssignedLabels();
87 | result = assignedLabels;
88 | }};
89 |
90 | AppLabelSet labels = Utils.getApplicationLabels();
91 | assertThat(labels.size(), is(1));
92 | assertThat(labels, hasItem(label));
93 | }
94 |
95 | @Test
96 | public void getApplicationLabelsWithNode() throws Exception {
97 | final Set assignedLabels = new HashSet();
98 | assignedLabels.add(label);
99 |
100 | new Expectations() {
101 | {
102 | node.getAssignedLabels();
103 | result = assignedLabels;
104 | }
105 | };
106 |
107 | AppLabelSet labels = Utils.getApplicationLabels(node);
108 | assertThat(labels.size(), is(1));
109 | assertThat(labels, hasItem(label));
110 | }
111 |
112 | @Test
113 | public void runExternalCommand() throws Exception {
114 | String output = Utils.runExternalCommand("echo", "HOGE");
115 | assertThat(output, is("HOGE\n"));
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/appdetector/util/Utils.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.appdetector.util;
2 |
3 | import hudson.Util;
4 | import hudson.model.Computer;
5 | import hudson.model.Node;
6 | import hudson.model.labels.LabelAtom;
7 | import jenkins.model.Jenkins;
8 | import org.jenkinsci.plugins.appdetector.AppLabelAtom;
9 | import org.jenkinsci.plugins.appdetector.AppLabelSet;
10 | import org.zeroturnaround.exec.ProcessExecutor;
11 |
12 | import java.util.HashMap;
13 | import java.util.Map;
14 | import java.util.Set;
15 |
16 | public class Utils {
17 |
18 | /**
19 | * Expands the variable in the given string to its value in the variables available to this build.
20 | *
21 | * @param buildVars Map of the build-specific variables.
22 | * @param token The token which may or may not contain variables in the format ${foo} .
23 | * @return The given token, with applicable variable expansions done.
24 | */
25 | public static String expandVariables(Map buildVars, String token) {
26 |
27 | final Map vars = new HashMap();
28 | if (buildVars != null) {
29 | // Build-specific variables, if any, take priority over environment variables
30 | vars.putAll(buildVars);
31 | }
32 |
33 | String result = Util.fixEmptyAndTrim(token);
34 | if (result != null) {
35 | result = Util.replaceMacro(result, vars);
36 | }
37 | return Util.fixEmptyAndTrim(result);
38 | }
39 |
40 | /**
41 | * Get all Computers of Jenkins including master.
42 | * @return List of Computer
43 | */
44 | public static Computer[] getAllComputers() {
45 | Jenkins jenkins = Jenkins.getInstance();
46 |
47 | if (jenkins == null) {
48 | return new Computer[0];
49 | }
50 |
51 | return jenkins.getComputers();
52 | }
53 |
54 | /**
55 | * Getting application labels from all nodes that connected to jenkins.
56 | * @return all of application labels.
57 | */
58 | public static AppLabelSet getApplicationLabels() {
59 | AppLabelSet applicationLabels = new AppLabelSet();
60 |
61 | Computer[] allComputers = getAllComputers();
62 | for (Computer computer: allComputers) {
63 | Node node = computer.getNode();
64 | if (node != null) {
65 | applicationLabels.addAll(getApplicationLabels(node));
66 | }
67 | }
68 | return applicationLabels;
69 | }
70 |
71 | /**
72 | * Getting application labels from specified node.
73 | * @param node The target node.
74 | * @return Application labels that assigned to given node.
75 | */
76 | public static AppLabelSet getApplicationLabels(Node node) {
77 | AppLabelSet applicationLabels = new AppLabelSet();
78 |
79 | Set allLabels = node.getAssignedLabels();
80 | for (LabelAtom label: allLabels) {
81 | if (label instanceof AppLabelAtom) {
82 | applicationLabels.add((AppLabelAtom)label);
83 | }
84 | }
85 |
86 | return applicationLabels;
87 | }
88 |
89 | /**
90 | * Executing external command, and get output.
91 | * @param command The list containing the program and its arguments.
92 | * @return output string.
93 | * @throws Exception Fail to execute command.
94 | */
95 | public static String runExternalCommand(String... command) throws Exception {
96 | String output = new ProcessExecutor().command(command).readOutput(true).execute().outputUTF8();
97 | if (output == null) {
98 | output = "";
99 | }
100 |
101 | return output;
102 | }
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/appdetector/AppLabelSet.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.appdetector;
2 |
3 | import java.io.Serializable;
4 | import java.util.ArrayList;
5 | import java.util.Collections;
6 | import java.util.Comparator;
7 | import java.util.HashSet;
8 | import java.util.List;
9 | import java.util.Set;
10 | import java.util.TreeSet;
11 |
12 | public class AppLabelSet extends HashSet {
13 |
14 | /**
15 | * Returns a Set of the app name strings contained in this set.
16 | * @return app name set.
17 | */
18 | public Set getAppNames() {
19 | Set appNames = new TreeSet();
20 | for (AppLabelAtom label: this) {
21 | appNames.add(label.getApplication());
22 | }
23 | return appNames;
24 | }
25 |
26 | /**
27 | * Returns a Set of the app version strings contained in this set.
28 | * @param appName The application name for which you want to obtain the version list.
29 | * @return app version set.
30 | */
31 | public Set getAppVersions(String appName) {
32 | Set appVersions = new TreeSet();
33 | for (AppLabelAtom label: this) {
34 | if (appName.equals(label.getApplication())) {
35 | appVersions.add(label.getVersion());
36 | }
37 | }
38 | return appVersions;
39 | }
40 |
41 | /**
42 | * Returns the version list sorted in DESC.
43 | * @param appName The application name for which you want to obtain the version list.
44 | * @return The version list sorted in DESC
45 | */
46 | public List getSortedAppVersions(String appName) {
47 | List versionList
48 | = new ArrayList(getAppVersions(appName));
49 | Collections.sort(versionList, new VersionComparator());
50 | Collections.reverse(versionList);
51 | return versionList;
52 | }
53 |
54 | /**
55 | * Returns a application label that matches specified condition,
56 | * or null if all labels not matched.
57 | * @param application The name of application such as "Xcode", "Unity".
58 | * @param version Application version.
59 | * @return The mached application label or null.
60 | */
61 | public AppLabelAtom getApplicationLabel(String application, String version) {
62 | for (AppLabelAtom label: this) {
63 | if (label.getApplication().equals(application) && label.getVersion().equals(version)) {
64 | return label;
65 | }
66 | }
67 | return null;
68 | }
69 |
70 | private static class VersionComparator implements Comparator, Serializable {
71 | private static final long serialVersionUID = 1L;
72 |
73 | public int compare(String verString1, String verString2) {
74 | String[] verArray1 = verString1.split("\\.");
75 | String[] verArray2 = verString2.split("\\.");
76 |
77 | for (int i = 0; i < verArray1.length; i++) {
78 | try {
79 | try {
80 | int num1 = Integer.parseInt(verArray1[i]);
81 | int num2 = Integer.parseInt(verArray2[i]);
82 |
83 | if (! (num1 == num2)) {
84 | return (num1 - num2);
85 | }
86 | } catch (NumberFormatException e) {
87 | String num1 = verArray1[i];
88 | String num2 = verArray2[i];
89 |
90 | if (! num1.equals(num2)) {
91 | return num1.compareTo(num2);
92 | }
93 | }
94 | } catch (ArrayIndexOutOfBoundsException e) {
95 | return 1;
96 | }
97 | }
98 |
99 | return (verArray1.length - verArray2.length);
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/appdetector/AppDetectorHandler.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.appdetector;
2 |
3 | import hudson.Extension;
4 | import hudson.matrix.Combination;
5 | import hudson.matrix.MatrixConfiguration;
6 | import hudson.model.Action;
7 | import hudson.model.Label;
8 | import hudson.model.ParameterValue;
9 | import hudson.model.ParametersAction;
10 | import hudson.model.Project;
11 | import hudson.model.Queue;
12 | import hudson.model.labels.LabelAssignmentAction;
13 | import hudson.model.labels.LabelAtom;
14 | import hudson.model.queue.SubTask;
15 | import hudson.tasks.BuildWrapper;
16 | import org.jenkinsci.plugins.appdetector.util.Utils;
17 |
18 | import java.util.HashMap;
19 | import java.util.List;
20 | import java.util.Map;
21 | import java.util.TreeMap;
22 |
23 | @Extension(ordinal = -100)
24 | public class AppDetectorHandler extends Queue.QueueDecisionHandler {
25 | @Override
26 | public boolean shouldSchedule(Queue.Task task, List actions) {
27 | if (task instanceof Project) {
28 | List buildWapperList = ((Project)task).getBuildWrappersList();
29 |
30 | for (BuildWrapper bw: buildWapperList) {
31 | if (bw instanceof AppDetectorBuildWrapper) {
32 | List settings = ((AppDetectorBuildWrapper)bw).getAppUsageSettings();
33 |
34 | if (settings.isEmpty()) {
35 | return true;
36 | }
37 |
38 | final Map buildVars = new TreeMap();
39 |
40 | if (task instanceof MatrixConfiguration) {
41 | Combination combination = ((MatrixConfiguration)task).getCombination();
42 | buildVars.putAll(combination);
43 | }
44 |
45 | buildVars.putAll(getBuildVariablesFromActions(actions));
46 |
47 | ApplicationLabelAssignmentAction action = new ApplicationLabelAssignmentAction();
48 |
49 | for (AppUsageSetting setting: settings) {
50 | String expandedVersion = Utils.expandVariables(buildVars, setting.getVersion());
51 |
52 | action.add(setting.getAppName() + "-" + expandedVersion);
53 | }
54 | actions.add(action);
55 | }
56 | }
57 | }
58 | return true;
59 | }
60 |
61 | private Map getBuildVariablesFromActions(List actions) {
62 | Map buildVars = new HashMap();
63 | for (Action action: actions) {
64 | if (action instanceof ParametersAction) {
65 | for (ParameterValue param: ((ParametersAction)action).getParameters()) {
66 | if (param.getValue() != null) {
67 | buildVars.put(param.getName(), param.getValue().toString());
68 | }
69 | }
70 | }
71 | }
72 | return buildVars;
73 | }
74 |
75 | private static class ApplicationLabelAssignmentAction implements LabelAssignmentAction {
76 | private Label label;
77 |
78 | public ApplicationLabelAssignmentAction() {
79 | this.label = null;
80 | }
81 |
82 | public Label getAssignedLabel(SubTask task) {
83 | Label taskLabel = task.getAssignedLabel();
84 |
85 | if (taskLabel != null) {
86 | return label.and(taskLabel);
87 | }
88 |
89 | return label;
90 | }
91 |
92 | public void add(String labelString) {
93 | if (label == null) {
94 | label = new LabelAtom(labelString);
95 | } else {
96 | label = label.and(new LabelAtom(labelString));
97 | }
98 | }
99 |
100 | public String getIconFileName() {
101 | return null;
102 | }
103 |
104 | public String getDisplayName() {
105 | return null;
106 | }
107 |
108 | public String getUrlName() {
109 | return null;
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/appdetector/AppDetectorLabelFinder.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.appdetector;
2 |
3 | import hudson.Extension;
4 | import hudson.model.Computer;
5 | import hudson.model.LabelFinder;
6 | import hudson.model.Node;
7 | import hudson.model.TaskListener;
8 | import hudson.model.labels.LabelAtom;
9 | import hudson.slaves.ComputerListener;
10 | import jenkins.model.Jenkins;
11 | import net.sf.json.JSONArray;
12 | import net.sf.json.JSONObject;
13 | import org.jenkinsci.plugins.appdetector.task.AppDetectionTask;
14 |
15 | import java.util.Collection;
16 | import java.util.Collections;
17 | import java.util.HashSet;
18 | import java.util.List;
19 | import java.util.Map;
20 | import java.util.Set;
21 | import java.util.concurrent.ConcurrentHashMap;
22 | import java.util.logging.LogManager;
23 | import java.util.logging.Logger;
24 |
25 | @Extension
26 | public class AppDetectorLabelFinder extends LabelFinder {
27 |
28 | private final Map> cashedLabels
29 | = new ConcurrentHashMap>();
30 |
31 | @Override
32 | public Collection findLabels(Node node) {
33 | Computer computer = node.toComputer();
34 | if (computer == null || node.getChannel() == null) {
35 | return Collections.emptyList();
36 | }
37 |
38 | Set applications = cashedLabels.get(node);
39 | if (applications == null || applications.isEmpty()) {
40 | return Collections.emptyList();
41 | }
42 |
43 | return applications;
44 | }
45 |
46 | @Extension
47 | public static class AppDetectorComputerListener extends ComputerListener {
48 |
49 | @Override
50 | public void onOnline(Computer computer, TaskListener taskListener) {
51 | Set applications = detectInstalledApplications(computer);
52 | if (!applications.isEmpty()) {
53 | finder().cashedLabels.put(computer.getNode(), applications);
54 | } else {
55 | finder().cashedLabels.remove(computer.getNode());
56 | }
57 | }
58 |
59 | @Override
60 | public void onConfigurationChange() {
61 | AppDetectorLabelFinder finder = finder();
62 |
63 | Set cachedNodes = new HashSet(finder.cashedLabels.keySet());
64 |
65 | Jenkins jenkins = Jenkins.getInstance();
66 |
67 | if (jenkins == null) {
68 | return;
69 | }
70 |
71 | List realNodes = jenkins.getNodes();
72 | for (Node node: cachedNodes) {
73 | if (!realNodes.contains(node)) {
74 | finder.cashedLabels.remove(node);
75 | }
76 | }
77 | }
78 |
79 | private AppDetectorLabelFinder finder() {
80 | return LabelFinder.all().get(AppDetectorLabelFinder.class);
81 | }
82 |
83 | private Set detectInstalledApplications(Computer computer) {
84 | Logger logger = LogManager.getLogManager().getLogger("hudson.WebAppMain");
85 |
86 | Set applications = new HashSet();
87 |
88 | Jenkins hudsonInstance = Jenkins.getInstance();
89 | if (hudsonInstance == null) {
90 | logger.warning(Messages.CANNOT_GET_HUDSON_INSTANCE());
91 | return applications;
92 | }
93 |
94 | AppDetectorBuildWrapper.DescriptorImpl descriptor =
95 | hudsonInstance.getDescriptorByType(AppDetectorBuildWrapper.DescriptorImpl.class);
96 |
97 | Boolean isUnix = computer.isUnix();
98 | //This computer seems offline. So, skip detection.
99 | if (isUnix == null) {
100 | return applications;
101 | }
102 |
103 | for (AppDetectionSetting setting: descriptor.getDetectionSettings()) {
104 | try {
105 | AppDetectionTask task = new AppDetectionTask(setting);
106 |
107 | String result = computer.getChannel().call(task);
108 | JSONArray appVersions = JSONArray.fromObject(result);
109 |
110 | for (Object appInfo: appVersions) {
111 | JSONObject info = JSONObject.fromObject(appInfo);
112 | applications.add(
113 | new AppLabelAtom(setting.getAppName(), info.getString("version"),
114 | info.getString("home")));
115 | }
116 | } catch (Exception e) {
117 | logger.warning(
118 | Messages.DETECTING_SOFTOWARE_INSTLLATION_FAILED(setting.getAppName(),
119 | computer.getDisplayName()));
120 | e.printStackTrace();
121 | }
122 | }
123 | return applications;
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/appdetector/AppDetectorParamaterDefinition.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.appdetector;
2 |
3 | import hudson.Extension;
4 | import hudson.model.ParameterDefinition;
5 | import hudson.model.ParameterValue;
6 | import hudson.model.SimpleParameterDefinition;
7 | import hudson.model.StringParameterValue;
8 | import hudson.util.ComboBoxModel;
9 | import hudson.util.ListBoxModel;
10 | import net.sf.json.JSONObject;
11 | import org.jenkinsci.plugins.appdetector.util.Utils;
12 | import org.kohsuke.stapler.DataBoundConstructor;
13 | import org.kohsuke.stapler.QueryParameter;
14 | import org.kohsuke.stapler.StaplerRequest;
15 | import org.kohsuke.stapler.export.Exported;
16 |
17 | import java.util.ArrayList;
18 | import java.util.List;
19 |
20 | public class AppDetectorParamaterDefinition extends SimpleParameterDefinition {
21 |
22 | private final String appName;
23 | private List choices;
24 | private final String defaultValue;
25 |
26 | /**
27 | * Creates new {@link AppDetectorParamaterDefinition} instance.
28 | * @param name Name.
29 | * @param appName The name of application such as "Xcode", "Unity".
30 | * @param description Description.
31 | */
32 | //@DataBoundConstructor
33 | public AppDetectorParamaterDefinition(String name, String appName, String description) {
34 | super(name, description);
35 | this.appName = appName;
36 | this.choices = getSortedVersionList();
37 | defaultValue = null;
38 | }
39 |
40 | /**
41 | * Creates new {@link AppDetectorParamaterDefinition} instance.
42 | * @param name Name.
43 | * @param appName The name of application such as "Xcode", "Unity".
44 | * @param defaultValue Default version of this application.
45 | * @param description Description.
46 | */
47 | @DataBoundConstructor
48 | public AppDetectorParamaterDefinition(String name, String appName, String defaultValue,
49 | String description) {
50 | super(name, description);
51 | this.appName = appName;
52 | this.choices = getSortedVersionList();
53 | this.defaultValue = defaultValue;
54 | }
55 |
56 | @Override
57 | public ParameterDefinition copyWithDefaultValue(ParameterValue defaultValue) {
58 | if (defaultValue instanceof StringParameterValue) {
59 | StringParameterValue value = (StringParameterValue) defaultValue;
60 | return new AppDetectorParamaterDefinition(getName(), getAppName(), value.value,
61 | getDescription());
62 | } else {
63 | return this;
64 | }
65 | }
66 |
67 | @Exported
68 | public String getAppName() {
69 | return appName;
70 | }
71 |
72 | @Exported
73 | public String getDefaultValue() {
74 | return defaultValue;
75 | }
76 |
77 | /**
78 | * Returns the version list sorted in DESC.
79 | * @return The version list sorted in DESC
80 | */
81 | public List getSortedVersionList() {
82 | return Utils.getApplicationLabels().getSortedAppVersions(appName);
83 | }
84 |
85 | @Override
86 | public StringParameterValue getDefaultParameterValue() {
87 | // Update option to current version list
88 | choices = new ArrayList(Utils.getApplicationLabels().getSortedAppVersions(appName));
89 | if (choices.contains(defaultValue)) {
90 | return new StringParameterValue(getName(), defaultValue, getDescription());
91 | } else {
92 | return new StringParameterValue(getName(), choices.get(0), getDescription());
93 | }
94 | }
95 |
96 | private StringParameterValue checkValue(StringParameterValue value) {
97 | // Update option to current version list
98 | choices = new ArrayList(Utils.getApplicationLabels().getSortedAppVersions(appName));
99 | if (!choices.contains(value.value)) {
100 | throw new IllegalArgumentException("Illegal choice for parameter " + getName() + ": "
101 | + value.value);
102 | }
103 | return value;
104 | }
105 |
106 | @Override
107 | public ParameterValue createValue(StaplerRequest req, JSONObject jo) {
108 | StringParameterValue value = req.bindJSON(StringParameterValue.class, jo);
109 | value.setDescription(getDescription());
110 | return checkValue(value);
111 | }
112 |
113 | public StringParameterValue createValue(String value) {
114 | return checkValue(new StringParameterValue(getName(), value, getDescription()));
115 | }
116 |
117 | @Extension
118 | public static class DescriptorImpl extends ParameterDescriptor {
119 | @Override
120 | public String getDisplayName() {
121 | return Messages.AppDetectorParamaterDefinition_DisplayName();
122 | }
123 |
124 | /**
125 | * Fill in the value of the select element of the application name in Jenkins' Web view.
126 | * @return List of application names.
127 | */
128 | public ListBoxModel doFillAppNameItems() {
129 | ListBoxModel items = new ListBoxModel();
130 | AppLabelSet labels = Utils.getApplicationLabels();
131 | for (String appName: labels.getAppNames()) {
132 | items.add(appName);
133 | }
134 | return items;
135 | }
136 |
137 | public ComboBoxModel doFillDefaultValueItems(@QueryParameter("appName") final String appName) {
138 | AppLabelSet labels = Utils.getApplicationLabels();
139 | return new ComboBoxModel(labels.getSortedAppVersions(appName));
140 | }
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/test/java/org/jenkinsci/plugins/appdetector/AppDetectorHandlerTest.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.appdetector;
2 |
3 | import org.junit.*;
4 | import static org.hamcrest.MatcherAssert.*;
5 | import static org.hamcrest.Matchers.*;
6 | import mockit.Expectations;
7 | import mockit.Mocked;
8 | import hudson.matrix.Combination;
9 | import hudson.matrix.MatrixConfiguration;
10 | import hudson.model.Action;
11 | import hudson.model.Descriptor;
12 | import hudson.model.StringParameterValue;
13 | import hudson.model.ParametersAction;
14 | import hudson.model.ParameterValue;
15 | import hudson.model.Project;
16 | import hudson.model.StringParameterValue;
17 | import hudson.model.Label;
18 | import hudson.model.labels.LabelAssignmentAction;
19 | import hudson.model.labels.LabelAtom;
20 | import hudson.tasks.BuildWrapper;
21 | import hudson.util.DescribableList;
22 |
23 | import java.util.Arrays;
24 | import java.util.ArrayList;
25 | import java.util.HashMap;
26 | import java.util.List;
27 | import java.io.PrintStream;
28 |
29 | public class AppDetectorHandlerTest {
30 | private AppDetectorHandler handler;
31 |
32 | @Mocked
33 | private Project task;
34 |
35 | @Mocked
36 | private MatrixConfiguration matrixTask;
37 |
38 | @Mocked
39 | private AppDetectorBuildWrapper bw;
40 |
41 | private List actions;
42 |
43 | @Before
44 | public void init() {
45 | actions = new ArrayList();
46 | handler = new AppDetectorHandler();
47 | }
48 |
49 | @Test
50 | public void shouldSchedule() throws Exception {
51 | new Expectations() {
52 | {
53 | task.getBuildWrappersList();
54 | result = new DescribableList>() {
55 | {
56 | add(bw);
57 | }
58 | };
59 |
60 | task.getAssignedLabel();
61 | result = null;
62 |
63 | bw.getAppUsageSettings();
64 | result = new ArrayList<>(Arrays.asList(new AppUsageSetting("Xcode", "8.0")));
65 | }
66 | };
67 | LabelAssignmentAction actualAction = null;
68 | boolean result = handler.shouldSchedule(task, actions);
69 |
70 | for (Action action: actions) {
71 | if (action instanceof LabelAssignmentAction) {
72 | actualAction = (LabelAssignmentAction) action;
73 | break;
74 | }
75 | }
76 |
77 | assertThat(result, is(true));
78 | assertThat(actualAction, is(notNullValue()));
79 |
80 | Label expectedLabel = new LabelAtom("Xcode-8.0");
81 | assertThat(actualAction.getAssignedLabel(task), is(expectedLabel));
82 | }
83 |
84 | @Test
85 | public void shouldScheduleWhenMultipleActionAssigned() throws Exception {
86 | new Expectations() {
87 | {
88 | task.getBuildWrappersList();
89 | result = new DescribableList>() {
90 | {
91 | add(bw);
92 | }
93 | };
94 |
95 | task.getAssignedLabel();
96 | result = null;
97 |
98 | bw.getAppUsageSettings();
99 | result = new ArrayList<>(Arrays.asList(
100 | new AppUsageSetting("Xcode", "8.0"), new AppUsageSetting("Unity", "5.3.6f1")));
101 | }
102 | };
103 | LabelAssignmentAction actualAction = null;
104 | boolean result = handler.shouldSchedule(task, actions);
105 |
106 | for (Action action: actions) {
107 | if (action instanceof LabelAssignmentAction) {
108 | actualAction = (LabelAssignmentAction) action;
109 | break;
110 | }
111 | }
112 |
113 | assertThat(result, is(true));
114 | assertThat(actualAction, is(notNullValue()));
115 |
116 | Label expectedLabel = new LabelAtom("Xcode-8.0");
117 | expectedLabel = expectedLabel.and(new LabelAtom("Unity-5.3.6f1"));
118 | assertThat(actualAction.getAssignedLabel(task), is(expectedLabel));
119 | }
120 |
121 | @Test
122 | public void shouldScheduleWhenProjectLabelAssigned() throws Exception {
123 | new Expectations() {
124 | {
125 | task.getBuildWrappersList();
126 | result = new DescribableList>() {
127 | {
128 | add(bw);
129 | }
130 | };
131 |
132 | task.getAssignedLabel();
133 | result = new LabelAtom("master");
134 |
135 | bw.getAppUsageSettings();
136 | result = new ArrayList<>(Arrays.asList(new AppUsageSetting("Xcode", "8.0")));
137 | }
138 | };
139 | LabelAssignmentAction actualAction = null;
140 | boolean result = handler.shouldSchedule(task, actions);
141 |
142 | for (Action action: actions) {
143 | if (action instanceof LabelAssignmentAction) {
144 | actualAction = (LabelAssignmentAction) action;
145 | break;
146 | }
147 | }
148 |
149 | assertThat(result, is(true));
150 | assertThat(actualAction, is(notNullValue()));
151 |
152 | Label expectedLabel = new LabelAtom("Xcode-8.0");
153 | expectedLabel = expectedLabel.and(new LabelAtom("master"));
154 | assertThat(actualAction.getAssignedLabel(task), is(expectedLabel));
155 | }
156 |
157 | @Test
158 | public void shouldScheduleWhenBuildParamaterGiven() throws Exception {
159 | new Expectations() {
160 | {
161 | task.getBuildWrappersList();
162 | result = new DescribableList>() {
163 | {
164 | add(bw);
165 | }
166 | };
167 |
168 | task.getAssignedLabel();
169 | result = null;
170 |
171 | bw.getAppUsageSettings();
172 | result = new ArrayList<>(Arrays.asList(new AppUsageSetting("Xcode", "$XCODE_VERSION")));
173 | }
174 | };
175 | actions.add(new ParametersAction(new StringParameterValue("XCODE_VERSION", "8.0")));
176 |
177 | LabelAssignmentAction actualAction = null;
178 | boolean result = handler.shouldSchedule(task, actions);
179 |
180 | for (Action action: actions) {
181 | if (action instanceof LabelAssignmentAction) {
182 | if (! (action instanceof ParametersAction)) {
183 | actualAction = (LabelAssignmentAction) action;
184 | break;
185 | }
186 | }
187 | }
188 |
189 | assertThat(result, is(true));
190 | assertThat(actualAction, is(notNullValue()));
191 |
192 | Label expectedLabel = new LabelAtom("Xcode-8.0");
193 | assertThat(actualAction.getAssignedLabel(task), is(expectedLabel));
194 | }
195 |
196 | @Test
197 | public void shouldScheduleWhenMatrixParamaterGiven() throws Exception {
198 | new Expectations() {
199 | {
200 | matrixTask.getBuildWrappersList();
201 | result = new DescribableList>() {
202 | {
203 | add(bw);
204 | }
205 | };
206 |
207 | matrixTask.getAssignedLabel();
208 | result = null;
209 |
210 | matrixTask.getCombination();
211 | result = new Combination(new HashMap() {
212 | {
213 | put("XCODE_VERSION", "8.0");
214 | }
215 | });
216 |
217 | bw.getAppUsageSettings();
218 | result = new ArrayList<>(Arrays.asList(new AppUsageSetting("Xcode", "$XCODE_VERSION")));
219 | }
220 | };
221 | LabelAssignmentAction actualAction = null;
222 | boolean result = handler.shouldSchedule(matrixTask, actions);
223 |
224 | for (Action action: actions) {
225 | if (action instanceof LabelAssignmentAction) {
226 | actualAction = (LabelAssignmentAction) action;
227 | break;
228 | }
229 | }
230 |
231 | assertThat(result, is(true));
232 | assertThat(actualAction, is(notNullValue()));
233 |
234 | Label expectedLabel = new LabelAtom("Xcode-8.0");
235 | assertThat(actualAction.getAssignedLabel(matrixTask), is(expectedLabel));
236 | }
237 | }
238 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 |
6 | org.jenkins-ci.plugins
7 | plugin
8 | 2.7
9 |
10 |
11 | org.jenkins-ci.plugins
12 | app-detector
13 | 1.0.10-SNAPSHOT
14 | hpi
15 |
16 |
17 |
18 | 1.625.3
19 |
20 | 7
21 |
22 |
23 | 2.14
24 |
28 |
29 |
30 | Application Detector Plugin
31 | Detect application versions installed in slave, And enable to execute builds on the specific slave that has an application and the version you want.
32 | https://wiki.jenkins-ci.org/display/JENKINS/Application+Detector+Plugin
33 |
34 |
35 |
36 |
37 |
38 | MIT License
39 | http://opensource.org/licenses/MIT
40 |
41 |
42 |
43 |
44 |
45 |
46 | org.apache.maven.plugins
47 | maven-javadoc-plugin
48 |
49 | -Xdoclint:none
50 |
51 |
52 |
53 | org.codehaus.mojo
54 | cobertura-maven-plugin
55 |
56 |
57 | html
58 | xml
59 |
60 |
61 |
62 | **/*Test.class
63 | **/Messages.class
64 |
65 |
66 |
67 |
68 | package
69 |
70 | cobertura
71 |
72 |
73 |
74 |
75 |
76 |
77 | org.apache.maven.plugins
78 | maven-surefire-plugin
79 |
80 |
81 | InjectedTest.java
82 |
83 |
84 |
85 |
86 | org.codehaus.mojo
87 | cobertura-maven-plugin
88 | 2.7
89 |
90 |
91 |
92 | **/Messages.class
93 |
94 |
95 |
96 |
97 | package
98 |
99 | cobertura
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | org.apache.maven.plugins
110 | maven-checkstyle-plugin
111 | 2.17
112 |
113 | config/google_checks.xml
114 |
115 |
116 |
117 | org.codehaus.mojo
118 | findbugs-maven-plugin
119 | 3.0.5
120 |
121 |
122 |
123 |
124 |
125 | ${project.basedir}/src/test/resources
126 |
127 |
128 |
129 |
130 |
131 |
132 | justice3120
133 | Masayoshi Sakamoto
134 | masayoshi.sakamoto@dena.jp
135 | Asia/Tokyo
136 |
137 |
138 |
139 |
140 | scm:git:git://github.com/jenkinsci/app-detector-plugin.git
141 | scm:git:git@github.com:jenkinsci/app-detector-plugin.git
142 | http://github.com/jenkinsci/app-detector-plugin
143 | app-detector-1.0.1
144 |
145 |
146 |
147 |
148 | repo.jenkins-ci.org
149 | https://repo.jenkins-ci.org/public/
150 |
151 |
152 |
153 |
154 | repo.jenkins-ci.org
155 | https://repo.jenkins-ci.org/public/
156 |
157 |
158 |
159 |
160 |
161 | org.zeroturnaround
162 | zt-exec
163 | 1.9
164 |
165 |
166 | org.jenkins-ci.plugins
167 | matrix-project
168 | 1.4
169 |
170 |
171 | org.apache.velocity
172 | velocity
173 | 1.7
174 |
175 |
176 | org.jmockit
177 | jmockit
178 | 1.26
179 | test
180 |
181 |
182 | junit
183 | junit
184 | 4.12
185 | test
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 | org.apache.maven.plugins
194 | maven-checkstyle-plugin
195 |
196 | config/google_checks.xml
197 |
198 |
199 |
200 | org.codehaus.mojo
201 | findbugs-maven-plugin
202 |
203 | Max
204 | true
205 |
206 |
207 |
208 | org.codehaus.mojo
209 | cobertura-maven-plugin
210 | 2.7
211 |
212 |
213 |
214 | **/Messages.class
215 |
216 |
217 |
218 |
219 | package
220 |
221 | cobertura
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
--------------------------------------------------------------------------------
/src/main/java/org/jenkinsci/plugins/appdetector/AppDetectorBuildWrapper.java:
--------------------------------------------------------------------------------
1 | package org.jenkinsci.plugins.appdetector;
2 |
3 | import hudson.Extension;
4 | import hudson.Launcher;
5 | import hudson.model.AbstractBuild;
6 | import hudson.model.AbstractProject;
7 | import hudson.model.BuildListener;
8 | import hudson.model.Computer;
9 | import hudson.model.Node;
10 | import hudson.model.Result;
11 | import hudson.tasks.BuildWrapper;
12 | import hudson.tasks.BuildWrapperDescriptor;
13 | import hudson.util.ComboBoxModel;
14 | import hudson.util.FormValidation;
15 | import hudson.util.ListBoxModel;
16 | import jenkins.model.Jenkins;
17 | import net.sf.json.JSONArray;
18 | import net.sf.json.JSONObject;
19 | import org.jenkinsci.plugins.appdetector.task.AppDetectionTask;
20 | import org.jenkinsci.plugins.appdetector.util.Utils;
21 | import org.kohsuke.stapler.DataBoundConstructor;
22 | import org.kohsuke.stapler.QueryParameter;
23 | import org.kohsuke.stapler.StaplerRequest;
24 | import org.kohsuke.stapler.interceptor.RequirePOST;
25 |
26 | import java.io.PrintStream;
27 | import java.util.ArrayList;
28 | import java.util.List;
29 | import java.util.Map;
30 |
31 | @Extension
32 | public class AppDetectorBuildWrapper extends BuildWrapper {
33 |
34 | private List usageSettings;
35 |
36 | public AppDetectorBuildWrapper() {
37 | super();
38 | }
39 |
40 | @DataBoundConstructor
41 | public AppDetectorBuildWrapper(List usageSettings) {
42 | this.usageSettings = usageSettings;
43 | }
44 |
45 | @Override
46 | public Environment setUp(AbstractBuild build, final Launcher launcher, BuildListener listener) {
47 | final PrintStream logger = listener.getLogger();
48 |
49 | final Map buildVars = build.getBuildVariables();
50 |
51 | Node node = build.getBuiltOn();
52 |
53 | if (node == null) {
54 | logger.println(Messages.GETTING_NODE_FAILED());
55 | build.setResult(Result.FAILURE);
56 | return null;
57 | }
58 |
59 | AppLabelSet allLabels = Utils.getApplicationLabels(node);
60 | final List labels = new ArrayList();
61 |
62 | for (AppUsageSetting setting: usageSettings) {
63 | String expandedVersion = Utils.expandVariables(buildVars, setting.getVersion());
64 | AppLabelAtom label = allLabels.getApplicationLabel(setting.getAppName(), expandedVersion);
65 |
66 | if (label == null) {
67 | logger.println(Messages.APP_NOT_FOUND(setting.getAppName(),
68 | expandedVersion, node.getNodeName()));
69 | build.setResult(Result.NOT_BUILT);
70 | return null;
71 | }
72 |
73 | labels.add(label);
74 | }
75 |
76 | return new Environment() {
77 | @Override
78 | public void buildEnvVars(Map env) {
79 | Jenkins hudsonInstance = Jenkins.getInstance();
80 | if (hudsonInstance != null) {
81 | DescriptorImpl descriptor = hudsonInstance.getDescriptorByType(DescriptorImpl.class);
82 | List detectionSettings = descriptor.getDetectionSettings();
83 |
84 | for (AppLabelAtom label: labels) {
85 | String envVarName = null;
86 |
87 | for (AppDetectionSetting setting: detectionSettings) {
88 | if (label.getApplication().equals(setting.getAppName())) {
89 | envVarName = setting.getHomeDirVarName();
90 | break;
91 | }
92 | }
93 |
94 | if (envVarName != null && !("".equals(envVarName))) {
95 | env.put(envVarName, label.getHome());
96 | }
97 | }
98 | }
99 | }
100 | };
101 | }
102 |
103 | public List getAppUsageSettings() {
104 | return usageSettings;
105 | }
106 |
107 | @Extension
108 | public static final class DescriptorImpl extends BuildWrapperDescriptor {
109 |
110 | private List detectionSettings = new ArrayList();
111 |
112 | public DescriptorImpl() {
113 | super(AppDetectorBuildWrapper.class);
114 | load();
115 | }
116 |
117 | @Override
118 | public String getDisplayName() {
119 | return Messages.JOB_DESCRIPTION();
120 | }
121 |
122 | @Override
123 | public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
124 | detectionSettings = new ArrayList();
125 |
126 | JSONArray settingArray = json.optJSONArray("setting");
127 | if (settingArray != null) {
128 | for (Object settingObj: settingArray) {
129 | JSONObject setting = JSONObject.fromObject(settingObj);
130 | detectionSettings
131 | .add(new AppDetectionSetting(
132 | setting.getString("appName"),
133 | setting.getString("script"),
134 | setting.getBoolean("detectOnLinux"),
135 | setting.getBoolean("detectOnOsx"),
136 | setting.getBoolean("detectOnWindows"),
137 | setting.getString("homeDirVarName")
138 | ));
139 | }
140 | } else {
141 | JSONObject setting = json.optJSONObject("setting");
142 | if (setting != null) {
143 | detectionSettings
144 | .add(new AppDetectionSetting(
145 | setting.getString("appName"),
146 | setting.getString("script"),
147 | setting.getBoolean("detectOnLinux"),
148 | setting.getBoolean("detectOnOsx"),
149 | setting.getBoolean("detectOnWindows"),
150 | setting.getString("homeDirVarName")
151 | ));
152 | }
153 | }
154 |
155 | save();
156 | return true;
157 | }
158 |
159 | @Override
160 | public BuildWrapper newInstance(StaplerRequest req, JSONObject json) throws FormException {
161 | List usageSettings = new ArrayList();
162 |
163 | JSONArray settingArray = json.optJSONArray("setting");
164 | if (settingArray != null) {
165 | for (Object settingObj: settingArray) {
166 | JSONObject setting = JSONObject.fromObject(settingObj);
167 | usageSettings
168 | .add(new AppUsageSetting(
169 | setting.getString("appName"),
170 | setting.getString("appVersion")
171 | ));
172 | }
173 | } else {
174 | JSONObject setting = json.optJSONObject("setting");
175 | if (setting != null) {
176 | usageSettings
177 | .add(new AppUsageSetting(
178 | setting.getString("appName"),
179 | setting.getString("appVersion")
180 | ));
181 | }
182 | }
183 |
184 | return new AppDetectorBuildWrapper(usageSettings);
185 | }
186 |
187 | @Override
188 | public boolean isApplicable(AbstractProject, ?> item) {
189 | return true;
190 | }
191 |
192 | /**
193 | * Fill in the value of the select element of the application name in Jenkins' Web view.
194 | * @return List of application names.
195 | */
196 | public ListBoxModel doFillAppNameItems() {
197 | ListBoxModel items = new ListBoxModel();
198 | AppLabelSet labels = Utils.getApplicationLabels();
199 | for (String appName: labels.getAppNames()) {
200 | items.add(appName);
201 | }
202 | return items;
203 | }
204 |
205 | public ComboBoxModel doFillAppVersionItems(@QueryParameter("appName") final String appName) {
206 | AppLabelSet labels = Utils.getApplicationLabels();
207 | return new ComboBoxModel(labels.getSortedAppVersions(appName));
208 | }
209 |
210 | /**
211 | * Fill in the value of select element of node name in Jenkins' Web view.
212 | * @return List of Jenkins node names.
213 | */
214 | public ListBoxModel doFillNodeItems() {
215 | ListBoxModel items = new ListBoxModel();
216 |
217 | Computer[] allComputers = Utils.getAllComputers();
218 | for (Computer computer: allComputers) {
219 | if (computer.isOnline()) {
220 | items.add(computer.getDisplayName(), computer.getName());
221 | } else {
222 | items.add(computer.getDisplayName() + "(offline)", computer.getName());
223 | }
224 | }
225 | return items;
226 | }
227 |
228 | /**
229 | * Test whether Groovy script for application detection is valid.
230 | * This method is called from Jenkins' Web View.
231 | * @param script Groovy script to be tested.
232 | * @param node Jenkins node name to execute script.
233 | * @param onLinux Whether to perform detection on Linux.
234 | * @param onOsx Whether to perform detection on Mac.
235 | * @param onWindows Whether to perform detection on Windows.
236 | * @return test results of the script.
237 | */
238 | @RequirePOST
239 | public FormValidation doTestScript(
240 | @QueryParameter("script") final String script,
241 | @QueryParameter("node") final String node,
242 | @QueryParameter("detectOnLinux") final boolean onLinux,
243 | @QueryParameter("detectOnOsx") final boolean onOsx,
244 | @QueryParameter("detectOnWindows") final boolean onWindows) {
245 |
246 | Jenkins jenkins = Jenkins.getInstance();
247 | if (jenkins == null) {
248 | return FormValidation.error(Messages.CANNOT_GET_HUDSON_INSTANCE());
249 | }
250 |
251 | jenkins.checkPermission(Jenkins.RUN_SCRIPTS);
252 | AppDetectionSetting setting =
253 | new AppDetectionSetting("Test", script, onLinux, onOsx, onWindows, "TEST");
254 |
255 | try {
256 | Computer computer = jenkins.getComputer(node);
257 | String result = computer.getChannel().call(new AppDetectionTask(setting));
258 | return FormValidation.ok(result);
259 | } catch (Exception e) {
260 | return FormValidation.error(e, e.getMessage());
261 | }
262 | }
263 |
264 | public List getDetectionSettings() {
265 | return detectionSettings;
266 | }
267 | }
268 | }
269 |
--------------------------------------------------------------------------------
/config/google_checks.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
67 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
92 |
93 |
94 |
96 |
97 |
98 |
99 |
101 |
102 |
103 |
104 |
106 |
107 |
112 |
113 |
114 |
115 |
116 |
118 |
119 |
120 |
121 |
123 |
124 |
125 |
126 |
128 |
129 |
130 |
131 |
133 |
134 |
135 |
136 |
138 |
140 |
142 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
--------------------------------------------------------------------------------