diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index 3a044f5..69129ce 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -2,7 +2,13 @@
```bash
mvn compile && rm -rf work/ && mvn hpi:run
+// configure GitHub Secret to `foobar23` via webui
+```
+
+
+**Testcase: Push Event**
+```bash
curl -X POST \
-H "Content-Type: application/json" \
-H "x-hub-signature: sha1=5b8a54ee48efb1fbe06e3e4fc5d120680eaa2a22" \
@@ -10,7 +16,31 @@ curl -X POST \
http://localhost:8080/jenkins/github-webhook-build-trigger/receive
```
- * With GitHub Secret being: `foobar23`
+
+
+
+**Testcase: Ping Event**
+
+Initial webhook created request.
+Only fired once when you setup the webhook on github. See: https://developer.github.com/webhooks/#ping-event
+
+```bash
+curl -X POST \
+ -H "Content-Type: application/json" \
+ -H "x-hub-signature: sha1=c203f51e9317264c6716ee0c6fed59d604674885" \
+ -d @test-webhook-init-payload.json \
+ http://localhost:8080/jenkins/github-webhook-build-trigger/receive
+```
+
+
+
+**x-hub-signature**
+
+Create an sha1 hash for a payload.json:
+
+```bash
+cat test-webhook-init-payload.json | openssl dgst -sha1 -hmac "foobar23"
+```
### Build hpi
diff --git a/README.md b/README.md
index 4d0b00d..b50ba07 100644
--- a/README.md
+++ b/README.md
@@ -195,6 +195,37 @@ git clone --single-branch \
source
```
+
+
+**Usage with Pipeline Jobs**
+
+Since version 1.1.0 [Pipeline Job Types](https://jenkins.io/doc/book/pipeline/) (NOT MultiBranch Pipeline) are supported.
+
+Make sure you have at least the following Plugins installed
+
+```
+Pipeline: Groovy
+Pipeline: Job
+Pipeline: API
+Pipeline: Step API
+Pipeline: Stage Step
+Pipeline: Basic Steps
+Pipeline: Model API
+```
+
+A simple **`Jenkinsfile`** could look like this:
+
+```
+node {
+ stage('foo') {
+ sh 'git clone --single-branch --branch ${env.GWBT_BRANCH} https://github.com/${env.GWBT_REPO_FULL_NAME}.git source'
+ dir('source') {
+ sh 'npm install'
+ }
+ }
+}
+```
+
-----
diff --git a/pom.xml b/pom.xml
index ce37e8b..32b2e39 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,12 +6,12 @@
org.jenkins-ci.plugins
plugin
- 2.25
+ 2.33
io.codeclou.jenkins.github.webhook.notifier.plugin
github-webhook-notifier-plugin
- 1.0.1
+ 1.1.0
hpi
github-webhook-notifier-plugin
@@ -52,5 +52,15 @@
commons-io
2.5
+
+ org.jenkins-ci.plugins.workflow
+ workflow-job
+ 2.5
+ true
+
+
+
+ 2.73
+
diff --git a/src/main/java/io/codeclou/jenkins/githubwebhookbuildtriggerplugin/EnvironmentContributionAction.java b/src/main/java/io/codeclou/jenkins/githubwebhookbuildtriggerplugin/EnvironmentContributionAction.java
index 7d4378b..16d96a7 100644
--- a/src/main/java/io/codeclou/jenkins/githubwebhookbuildtriggerplugin/EnvironmentContributionAction.java
+++ b/src/main/java/io/codeclou/jenkins/githubwebhookbuildtriggerplugin/EnvironmentContributionAction.java
@@ -5,10 +5,11 @@
package io.codeclou.jenkins.githubwebhookbuildtriggerplugin;
import hudson.EnvVars;
-import hudson.model.AbstractBuild;
-import hudson.model.EnvironmentContributingAction;
+import hudson.model.*;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/*
@@ -48,7 +49,7 @@ public EnvironmentContributionAction(GithubWebhookPayload payload) {
* converts "refs/heads/develop" to "develop"
*/
private String normalizeBranchNameOrEmptyString(String branchname) {
- if (branchname.startsWith("refs/heads/")) {
+ if (branchname != null && branchname.startsWith("refs/heads/")) {
return branchname.replace("refs/heads/", "");
}
return "";
@@ -58,7 +59,7 @@ private String normalizeBranchNameOrEmptyString(String branchname) {
* converts "refs/tags/1.0.0" to "1.0.0"
*/
private String normalizeTagNameOrEmptyString(String tagname) {
- if (tagname.startsWith("refs/tags/")) {
+ if (tagname != null && tagname.startsWith("refs/tags/")) {
return tagname.replace("refs/tags/", "");
}
return "";
@@ -90,4 +91,19 @@ public void buildEnvVars(AbstractBuild, ?> build, EnvVars env) {
env.putAll(environmentVariables);
}
}
+
+ /**
+ * Since WorkflowJob does not support EnvironmentContributionAction yet,
+ * we need a ParametersAction filled with List ParameterValue
+ * See: https://github.com/jenkinsci/workflow-job-plugin/blob/124b171b76394728f9c8504829cf6857abc8bdb5/src/main/java/org/jenkinsci/plugins/workflow/job/WorkflowRun.java#L435
+ */
+ public ParametersAction transform() {
+ List paramValues = new ArrayList<>();
+ List safeParams = new ArrayList<>();
+ for (Map.Entry envVar : environmentVariables.entrySet()) {
+ paramValues.add(new StringParameterValue(envVar.getKey(), envVar.getValue(), envVar.getValue()));
+ safeParams.add(envVar.getKey());
+ }
+ return new ParametersAction(paramValues, safeParams);
+ }
}
diff --git a/src/main/java/io/codeclou/jenkins/githubwebhookbuildtriggerplugin/GithubWebhookBuildTriggerAction.java b/src/main/java/io/codeclou/jenkins/githubwebhookbuildtriggerplugin/GithubWebhookBuildTriggerAction.java
index 79a2f1b..51647bf 100644
--- a/src/main/java/io/codeclou/jenkins/githubwebhookbuildtriggerplugin/GithubWebhookBuildTriggerAction.java
+++ b/src/main/java/io/codeclou/jenkins/githubwebhookbuildtriggerplugin/GithubWebhookBuildTriggerAction.java
@@ -6,27 +6,30 @@
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
+import hudson.EnvVars;
import hudson.Extension;
import hudson.model.*;
+import hudson.model.queue.QueueTaskFuture;
import hudson.util.HttpResponses;
import io.codeclou.jenkins.githubwebhookbuildtriggerplugin.config.GithubWebhookBuildTriggerPluginBuilder;
import io.codeclou.jenkins.githubwebhookbuildtriggerplugin.webhooksecret.GitHubWebhookUtility;
import jenkins.model.Jenkins;
import org.apache.commons.io.IOUtils;
+import org.jenkinsci.plugins.workflow.job.WorkflowJob;
+import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.interceptor.RequirePOST;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
-import javax.swing.plaf.basic.BasicToolTipUI;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
@Extension
-public class GithubWebhookBuildTriggerAction implements UnprotectedRootAction {
+public class GithubWebhookBuildTriggerAction implements UnprotectedRootAction, EnvironmentContributingAction {
@Override
public String getUrlName() {
@@ -65,7 +68,7 @@ public HttpResponse doReceive(HttpServletRequest request, StaplerRequest stapler
String webhookSecretAsConfiguredByUser = GithubWebhookBuildTriggerPluginBuilder.DescriptorImpl.getDescriptor().getWebhookSecret();
String webhookSecretMessage ="validating webhook payload against wevhook secret.";
info.append(">> webhook secret validation").append("\n");
- if (webhookSecretAsConfiguredByUser == null) {
+ if (webhookSecretAsConfiguredByUser == null || webhookSecretAsConfiguredByUser.isEmpty()) {
webhookSecretMessage = " skipping validation since no webhook secret is configured in \n" +
" 'Jenkins' -> 'Configure' tab under 'Github Webhook Build Trigger' section.";
} else {
@@ -77,11 +80,23 @@ public HttpResponse doReceive(HttpServletRequest request, StaplerRequest stapler
webhookSecretMessage = " ok. Webhook secret validates against " + githubSignature + "\n";
}
info.append(webhookSecretMessage).append("\n\n");
+
//
- // PAYLOAD TO ENVVARS
+ // CHECK IF INITIAL REQUEST (see test-webhook-init-payload.json)
+ // See: https://developer.github.com/webhooks/#ping-event
//
+ if (githubWebhookPayload.getHook_id() != null) {
+ info.append(">> ping request received: your webhook with ID ");
+ info.append(githubWebhookPayload.getHook_id());
+ info.append(" is working :)\n");
+ return HttpResponses.plainText(this.getTextEnvelopedInBanner(info.toString()));
+ }
+ //
+ // PAYLOAD TO ENVVARS
+ //
EnvironmentContributionAction environmentContributionAction = new EnvironmentContributionAction(githubWebhookPayload);
+
//
// TRIGGER JOBS
//
@@ -103,10 +118,25 @@ public HttpResponse doReceive(HttpServletRequest request, StaplerRequest stapler
}
for (Job job: jobs) {
if (job.getName().startsWith(jobNamePrefix) && ! jobsAlreadyTriggered.contains(job.getName())) {
- jobsTriggered.append(" ").append(job.getName()).append("\n");
jobsAlreadyTriggered.add(job.getName());
- AbstractProject projectScheduable = (AbstractProject) job;
- projectScheduable.scheduleBuild(0, cause, environmentContributionAction);
+ if (job instanceof WorkflowJob) {
+ WorkflowJob wjob = (WorkflowJob) job;
+ if (wjob.isBuildable()) {
+ jobsTriggered.append(" WORKFLOWJOB> ").append(job.getName()).append(" TRIGGERED\n");
+ wjob.addAction(environmentContributionAction.transform());
+ wjob.scheduleBuild(0, cause);
+ } else {
+ jobsTriggered.append(" WORKFLOWJOB> ").append(job.getName()).append(" NOT BUILDABLE. SKIPPING.\n");
+ }
+ } else {
+ AbstractProject projectScheduable = (AbstractProject) job;
+ if (job.isBuildable()) {
+ jobsTriggered.append(" CLASSICJOB> ").append(job.getName()).append(" TRIGGERED\n");
+ projectScheduable.scheduleBuild(0, cause, environmentContributionAction);
+ } else {
+ jobsTriggered.append(" CLASSICJOB> ").append(job.getName()).append(" NOT BUILDABLE. SKIPPING.\n");
+ }
+ }
}
}
//
@@ -139,4 +169,9 @@ private String getTextEnvelopedInBanner(String text) {
banner.append("\n----------------------------------------------------------------------------------\n");
return banner.toString();
}
+
+ @Override
+ public void buildEnvVars(AbstractBuild, ?> abstractBuild, EnvVars envVars) {
+
+ }
}
diff --git a/src/main/java/io/codeclou/jenkins/githubwebhookbuildtriggerplugin/GithubWebhookPayload.java b/src/main/java/io/codeclou/jenkins/githubwebhookbuildtriggerplugin/GithubWebhookPayload.java
index 14d29fa..f9dddc4 100644
--- a/src/main/java/io/codeclou/jenkins/githubwebhookbuildtriggerplugin/GithubWebhookPayload.java
+++ b/src/main/java/io/codeclou/jenkins/githubwebhookbuildtriggerplugin/GithubWebhookPayload.java
@@ -10,6 +10,11 @@
*/
public class GithubWebhookPayload {
+ /*
+ * hook_id is only set on initial request when the webhook is created.
+ * See: https://developer.github.com/webhooks/#ping-event
+ */
+ private Long hook_id;
private String ref;
private String before;
private String after;
@@ -51,6 +56,14 @@ public void setRepository(GithubWebhookPayloadRepository repository) {
this.repository = repository;
}
+ public Long getHook_id() {
+ return hook_id;
+ }
+
+ public void setHook_id(Long hook_id) {
+ this.hook_id = hook_id;
+ }
+
public class GithubWebhookPayloadRepository {
private String clone_url;
private String html_url;
diff --git a/test-webhook-init-payload.json b/test-webhook-init-payload.json
new file mode 100644
index 0000000..29d3402
--- /dev/null
+++ b/test-webhook-init-payload.json
@@ -0,0 +1,137 @@
+{
+ "zen": "Half measures are as bad as nothing at all.",
+ "hook_id": 15715749,
+ "hook": {
+ "type": "Repository",
+ "id": 15715749,
+ "name": "web",
+ "active": true,
+ "events": [
+ "push"
+ ],
+ "config": {
+ "content_type": "json",
+ "insecure_ssl": "0",
+ "secret": "********",
+ "url": "https://foo.bar/jenkins-github-webhook-build-trigger/receive"
+ },
+ "updated_at": "2017-08-24T11:53:44Z",
+ "created_at": "2017-08-24T11:53:44Z",
+ "url": "https://api.github.com/repos/clouless/test123/hooks/15715749",
+ "test_url": "https://api.github.com/repos/clouless/test123/hooks/15715749/test",
+ "ping_url": "https://api.github.com/repos/clouless/test123/hooks/15715749/pings",
+ "last_response": {
+ "code": null,
+ "status": "unused",
+ "message": null
+ }
+ },
+ "repository": {
+ "id": 101287239,
+ "name": "test123",
+ "full_name": "clouless/test123",
+ "owner": {
+ "login": "clouless",
+ "id": 12599965,
+ "avatar_url": "https://avatars0.githubusercontent.com/u/12599965?v=4",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/clouless",
+ "html_url": "https://github.com/clouless",
+ "followers_url": "https://api.github.com/users/clouless/followers",
+ "following_url": "https://api.github.com/users/clouless/following{/other_user}",
+ "gists_url": "https://api.github.com/users/clouless/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/clouless/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/clouless/subscriptions",
+ "organizations_url": "https://api.github.com/users/clouless/orgs",
+ "repos_url": "https://api.github.com/users/clouless/repos",
+ "events_url": "https://api.github.com/users/clouless/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/clouless/received_events",
+ "type": "User",
+ "site_admin": false
+ },
+ "private": false,
+ "html_url": "https://github.com/clouless/test123",
+ "description": null,
+ "fork": false,
+ "url": "https://api.github.com/repos/clouless/test123",
+ "forks_url": "https://api.github.com/repos/clouless/test123/forks",
+ "keys_url": "https://api.github.com/repos/clouless/test123/keys{/key_id}",
+ "collaborators_url": "https://api.github.com/repos/clouless/test123/collaborators{/collaborator}",
+ "teams_url": "https://api.github.com/repos/clouless/test123/teams",
+ "hooks_url": "https://api.github.com/repos/clouless/test123/hooks",
+ "issue_events_url": "https://api.github.com/repos/clouless/test123/issues/events{/number}",
+ "events_url": "https://api.github.com/repos/clouless/test123/events",
+ "assignees_url": "https://api.github.com/repos/clouless/test123/assignees{/user}",
+ "branches_url": "https://api.github.com/repos/clouless/test123/branches{/branch}",
+ "tags_url": "https://api.github.com/repos/clouless/test123/tags",
+ "blobs_url": "https://api.github.com/repos/clouless/test123/git/blobs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/clouless/test123/git/tags{/sha}",
+ "git_refs_url": "https://api.github.com/repos/clouless/test123/git/refs{/sha}",
+ "trees_url": "https://api.github.com/repos/clouless/test123/git/trees{/sha}",
+ "statuses_url": "https://api.github.com/repos/clouless/test123/statuses/{sha}",
+ "languages_url": "https://api.github.com/repos/clouless/test123/languages",
+ "stargazers_url": "https://api.github.com/repos/clouless/test123/stargazers",
+ "contributors_url": "https://api.github.com/repos/clouless/test123/contributors",
+ "subscribers_url": "https://api.github.com/repos/clouless/test123/subscribers",
+ "subscription_url": "https://api.github.com/repos/clouless/test123/subscription",
+ "commits_url": "https://api.github.com/repos/clouless/test123/commits{/sha}",
+ "git_commits_url": "https://api.github.com/repos/clouless/test123/git/commits{/sha}",
+ "comments_url": "https://api.github.com/repos/clouless/test123/comments{/number}",
+ "issue_comment_url": "https://api.github.com/repos/clouless/test123/issues/comments{/number}",
+ "contents_url": "https://api.github.com/repos/clouless/test123/contents/{+path}",
+ "compare_url": "https://api.github.com/repos/clouless/test123/compare/{base}...{head}",
+ "merges_url": "https://api.github.com/repos/clouless/test123/merges",
+ "archive_url": "https://api.github.com/repos/clouless/test123/{archive_format}{/ref}",
+ "downloads_url": "https://api.github.com/repos/clouless/test123/downloads",
+ "issues_url": "https://api.github.com/repos/clouless/test123/issues{/number}",
+ "pulls_url": "https://api.github.com/repos/clouless/test123/pulls{/number}",
+ "milestones_url": "https://api.github.com/repos/clouless/test123/milestones{/number}",
+ "notifications_url": "https://api.github.com/repos/clouless/test123/notifications{?since,all,participating}",
+ "labels_url": "https://api.github.com/repos/clouless/test123/labels{/name}",
+ "releases_url": "https://api.github.com/repos/clouless/test123/releases{/id}",
+ "deployments_url": "https://api.github.com/repos/clouless/test123/deployments",
+ "created_at": "2017-08-24T11:22:34Z",
+ "updated_at": "2017-08-24T11:22:34Z",
+ "pushed_at": "2017-08-24T11:22:35Z",
+ "git_url": "git://github.com/clouless/test123.git",
+ "ssh_url": "git@github.com:clouless/test123.git",
+ "clone_url": "https://github.com/clouless/test123.git",
+ "svn_url": "https://github.com/clouless/test123",
+ "homepage": null,
+ "size": 0,
+ "stargazers_count": 0,
+ "watchers_count": 0,
+ "language": null,
+ "has_issues": true,
+ "has_projects": true,
+ "has_downloads": true,
+ "has_wiki": true,
+ "has_pages": false,
+ "forks_count": 0,
+ "mirror_url": null,
+ "open_issues_count": 0,
+ "forks": 0,
+ "open_issues": 0,
+ "watchers": 0,
+ "default_branch": "master"
+ },
+ "sender": {
+ "login": "clouless",
+ "id": 12599965,
+ "avatar_url": "https://avatars0.githubusercontent.com/u/12599965?v=4",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/clouless",
+ "html_url": "https://github.com/clouless",
+ "followers_url": "https://api.github.com/users/clouless/followers",
+ "following_url": "https://api.github.com/users/clouless/following{/other_user}",
+ "gists_url": "https://api.github.com/users/clouless/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/clouless/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/clouless/subscriptions",
+ "organizations_url": "https://api.github.com/users/clouless/orgs",
+ "repos_url": "https://api.github.com/users/clouless/repos",
+ "events_url": "https://api.github.com/users/clouless/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/clouless/received_events",
+ "type": "User",
+ "site_admin": false
+ }
+}