├── .gitignore
├── src
└── main
│ ├── resources
│ ├── index.jelly
│ └── com
│ │ └── podio
│ │ └── hudson
│ │ └── PodioBuildNotifier
│ │ ├── config.jelly
│ │ └── global.jelly
│ ├── webapp
│ └── help-globalConfig.html
│ └── java
│ └── com
│ └── podio
│ └── hudson
│ └── PodioBuildNotifier.java
├── .settings
├── org.eclipse.jdt.core.prefs
└── org.maven.ide.eclipse.prefs
├── .classpath
├── .project
├── README.md
├── LICENSE
└── pom.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /work
3 |
--------------------------------------------------------------------------------
/src/main/resources/index.jelly:
--------------------------------------------------------------------------------
1 |
2 | Plugin for posting build results to Podio
3 |
--------------------------------------------------------------------------------
/src/main/webapp/help-globalConfig.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | See help-projectConfig.html for more about what these HTMLs do.
4 |
5 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | #Fri Oct 22 17:00:20 CEST 2010
2 | eclipse.preferences.version=1
3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
4 | org.eclipse.jdt.core.compiler.compliance=1.5
5 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
6 | org.eclipse.jdt.core.compiler.source=1.5
7 |
--------------------------------------------------------------------------------
/.settings/org.maven.ide.eclipse.prefs:
--------------------------------------------------------------------------------
1 | #Fri Oct 22 16:54:12 CEST 2010
2 | activeProfiles=
3 | eclipse.preferences.version=1
4 | fullBuildGoals=process-test-resources
5 | includeModules=false
6 | resolveWorkspaceProjects=true
7 | resourceFilterGoals=process-resources resources\:testResources
8 | skipCompilerPlugin=true
9 | version=1
10 |
--------------------------------------------------------------------------------
/src/main/resources/com/podio/hudson/PodioBuildNotifier/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
--------------------------------------------------------------------------------
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | podio-build
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 | org.eclipse.m2e.core.maven2Builder
13 |
14 |
15 |
16 | org.eclipse.jdt.core.javanature
17 | org.eclipse.m2e.core.maven2Nature
18 |
19 |
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Podio Build Notifier
2 |
3 | A [Jenkins](http://jenkins-ci.org/) post-build plugin for notifying a Podio app on the build status of a project.
4 |
5 | ## Requirements
6 |
7 | * [Java 7](http://java.com)
8 | * [Maven](http://maven.apache.org) 2+
9 |
10 | ## Building
11 |
12 | $ mvn
13 |
14 | After the build finishes, the plugin is located in `target/podio-build-notifier.hpi`.
15 |
16 | ## Installation
17 |
18 | The plugin is not yet in any central repository, so head to the *Plugin Manager* in your Jenkins, go to the *Advanced* tab, and upload the `podio-build-notifier.hpi` file.
19 |
20 | ## Setup
21 |
22 | TBD
23 |
24 | ## License
25 |
26 | MIT
27 |
--------------------------------------------------------------------------------
/src/main/resources/com/podio/hudson/PodioBuildNotifier/global.jelly:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This software is released under the MIT license:
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7 | the Software, and to permit persons to whom the Software is furnished to do so,
8 | subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | org.jenkins-ci.plugins
6 | plugin
7 | 1.445
8 |
9 |
10 | com.podio
11 | podio-build-notifier
12 | Podio Build Notifier
13 | 1.2.2
14 | hpi
15 |
16 |
18 |
19 |
20 | repo.jenkins-ci.org
21 | http://repo.jenkins-ci.org/public/
22 |
23 |
24 |
25 |
26 | MIT
27 | http://creativecommons.org/licenses/MIT/
28 |
29 |
30 |
31 |
32 |
33 | repo.jenkins-ci.org
34 | http://repo.jenkins-ci.org/public/
35 |
36 |
37 |
38 |
39 | com.podio
40 | api
41 | 0.7.4
42 | jar
43 | compile
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/main/java/com/podio/hudson/PodioBuildNotifier.java:
--------------------------------------------------------------------------------
1 | package com.podio.hudson;
2 |
3 | import hudson.Extension;
4 | import hudson.Launcher;
5 | import hudson.model.BuildListener;
6 | import hudson.model.Result;
7 | import hudson.model.AbstractBuild;
8 | import hudson.model.AbstractProject;
9 | import hudson.model.Run;
10 | import hudson.model.User;
11 | import hudson.scm.ChangeLogSet;
12 | import hudson.scm.ChangeLogSet.Entry;
13 | import hudson.tasks.BuildStepDescriptor;
14 | import hudson.tasks.BuildStepMonitor;
15 | import hudson.tasks.Notifier;
16 | import hudson.tasks.Publisher;
17 | import hudson.tasks.Mailer;
18 | import hudson.tasks.Mailer.UserProperty;
19 | import hudson.tasks.junit.CaseResult;
20 | import hudson.tasks.test.AbstractTestResultAction;
21 | import hudson.util.FormValidation;
22 |
23 | import java.io.IOException;
24 | import java.util.ArrayList;
25 | import java.util.Collections;
26 | import java.util.HashSet;
27 | import java.util.List;
28 | import java.util.Map;
29 | import java.util.Set;
30 | import java.util.logging.Logger;
31 |
32 | import javax.servlet.ServletException;
33 |
34 | import net.sf.json.JSONObject;
35 |
36 | import org.apache.commons.lang.StringUtils;
37 | import org.joda.time.LocalDate;
38 | import org.kohsuke.stapler.DataBoundConstructor;
39 | import org.kohsuke.stapler.QueryParameter;
40 | import org.kohsuke.stapler.StaplerRequest;
41 |
42 | import com.podio.APIFactory;
43 | import com.podio.ResourceFactory;
44 | import com.podio.app.AppAPI;
45 | import com.podio.app.Application;
46 | import com.podio.common.Reference;
47 | import com.podio.common.ReferenceType;
48 | import com.podio.contact.ContactAPI;
49 | import com.podio.contact.ProfileField;
50 | import com.podio.contact.ProfileMini;
51 | import com.podio.contact.ProfileType;
52 | import com.podio.item.FieldValuesUpdate;
53 | import com.podio.item.ItemAPI;
54 | import com.podio.item.ItemCreate;
55 | import com.podio.item.ItemsResponse;
56 | import com.podio.oauth.OAuthClientCredentials;
57 | import com.podio.oauth.OAuthUsernameCredentials;
58 | import com.podio.task.Task;
59 | import com.podio.task.TaskAPI;
60 | import com.podio.task.TaskCreate;
61 | import com.podio.task.TaskStatus;
62 | import com.podio.user.UserAPI;
63 | import com.sun.jersey.api.client.UniformInterfaceException;
64 |
65 | public class PodioBuildNotifier extends Notifier {
66 |
67 | @SuppressWarnings("unused")
68 | private static final Logger LOGGER = Logger
69 | .getLogger(PodioBuildNotifier.class.getName());
70 |
71 | private final String appId;
72 |
73 | @DataBoundConstructor
74 | public PodioBuildNotifier(String appId) {
75 | this.appId = appId;
76 | }
77 |
78 | public String getAppId() {
79 | return appId;
80 | }
81 |
82 | private APIFactory getBaseAPI() {
83 | DescriptorImpl descriptor = (DescriptorImpl) getDescriptor();
84 |
85 | return new APIFactory(new ResourceFactory(new OAuthClientCredentials(
86 | descriptor.clientId, descriptor.clientSecret),
87 | new OAuthUsernameCredentials(descriptor.username,
88 | descriptor.password)));
89 | }
90 |
91 | public BuildStepMonitor getRequiredMonitorService() {
92 | return BuildStepMonitor.BUILD;
93 | }
94 |
95 | @Override
96 | public boolean perform(AbstractBuild, ?> build, Launcher launcher,
97 | BuildListener listener) throws InterruptedException, IOException {
98 | APIFactory apiFactory = getBaseAPI();
99 |
100 | String result = StringUtils.capitalize(build.getResult().toString()
101 | .toLowerCase());
102 | result = result.replace('_', ' ');
103 | int spaceId = getSpace(apiFactory);
104 | String url = Mailer.descriptor().getUrl() + build.getParent().getUrl()
105 | + build.getNumber();
106 | Set profiles = getProfiles(apiFactory, spaceId, build);
107 |
108 | Integer totalTestCases = null;
109 | Integer failedTestCases = null;
110 | AbstractTestResultAction testResult = build.getTestResultAction();
111 | if (testResult != null) {
112 | totalTestCases = testResult.getTotalCount();
113 | failedTestCases = testResult.getFailCount();
114 | }
115 |
116 | String changes = getChangesText(build);
117 |
118 | int itemId = postBuild(apiFactory, build.getNumber(), result, url,
119 | changes, profiles, totalTestCases, failedTestCases,
120 | build.getDurationString());
121 |
122 | AbstractBuild previousBuild = build.getPreviousBuild();
123 | boolean oldFailed = previousBuild != null
124 | && previousBuild.getResult() != Result.SUCCESS;
125 |
126 | TaskAPI taskAPI = apiFactory.getAPI(TaskAPI.class);
127 | if (oldFailed && build.getResult() == Result.SUCCESS) {
128 | Run firstFailed = getFirstFailure(previousBuild);
129 | Integer firstFailedItemId = getItemId(apiFactory,
130 | firstFailed.getNumber());
131 | if (firstFailedItemId != null) {
132 | List tasks = taskAPI.getTasksWithReference(new Reference(
133 | ReferenceType.ITEM, firstFailedItemId));
134 | for (Task task : tasks) {
135 | if (task.getStatus() == TaskStatus.ACTIVE) {
136 | taskAPI.completeTask(task.getId());
137 | }
138 | }
139 | }
140 | } else if (!oldFailed && build.getResult() != Result.SUCCESS) {
141 | String text = "Build " + build.getNumber() + " "
142 | + build.getResult().toString().toLowerCase() + "";
143 |
144 | String description = null;
145 | if (testResult != null && testResult.getFailCount() > 0) {
146 | description += testResult.getFailCount()
147 | + " testcase(s) failed:\n";
148 |
149 | List failedTests = testResult.getFailedTests();
150 | for (CaseResult caseResult : failedTests) {
151 | description += caseResult.getDisplayName() + "\n";
152 | }
153 | }
154 |
155 | for (ProfileMini profile : profiles) {
156 | taskAPI.createTaskWithReference(
157 | new TaskCreate(text, description, false,
158 | new LocalDate(), profile.getUserId()),
159 | new Reference(ReferenceType.ITEM, itemId), true);
160 | }
161 | }
162 |
163 | return true;
164 | }
165 |
166 | private Run getFirstFailure(Run build) {
167 | Run previousBuild = build.getPreviousBuild();
168 |
169 | if (previousBuild != null) {
170 | if (previousBuild.getResult() == Result.SUCCESS) {
171 | return build;
172 | }
173 |
174 | return getFirstFailure(previousBuild);
175 | } else {
176 | return build;
177 | }
178 | }
179 |
180 | private Integer getItemId(APIFactory apiFactory, int buildNumber) {
181 | ItemsResponse response = apiFactory.getAPI(ItemAPI.class)
182 | .getItemsByExternalId(Integer.parseInt(appId),
183 | Integer.toString(buildNumber));
184 | if (response.getFiltered() != 1) {
185 | return null;
186 | }
187 |
188 | return response.getItems().get(0).getId();
189 | }
190 |
191 | private int postBuild(APIFactory apiFactory, int buildNumber,
192 | String result, String url, String changes,
193 | Set profiles, Integer totalTestCases,
194 | Integer failedTestCases, String duration) {
195 | List fields = new ArrayList();
196 | fields.add(new FieldValuesUpdate("build-number", "value", "Build "
197 | + buildNumber));
198 | fields.add(new FieldValuesUpdate("result", "value", result));
199 | fields.add(new FieldValuesUpdate("url", "value", url));
200 | if (changes != null) {
201 | fields.add(new FieldValuesUpdate("changes", "value", changes));
202 | }
203 | List