Add per-job URL patterns.

Add the option to specify the URL pattern left in Gerrit, per-job.

Example: a job that builds documentation and uploads it to a staging
site can leave the URL of the staging site iff the job succeeds.

Change-Id: I34841e61e9116fd8d4de7ac09d8f9cfe36fe78ec
Reviewed-on: https://review.openstack.org/18264
Reviewed-by: Clark Boylan <clark.boylan@gmail.com>
Approved: James E. Blair <corvus@inaugust.com>
Tested-by: Jenkins
diff --git a/doc/source/zuul.rst b/doc/source/zuul.rst
index 416174d..961c964 100644
--- a/doc/source/zuul.rst
+++ b/doc/source/zuul.rst
@@ -18,6 +18,8 @@
 Examples of each of the three files can be found in the etc/ directory
 of the source distribution.
 
+.. _zuulconf:
+
 zuul.conf
 ~~~~~~~~~
 
@@ -377,6 +379,18 @@
 **success-message (optional)**
   The message that should be reported to Gerrit if the job fails.
 
+**failure-pattern (optional)**
+  The URL that should be reported to Gerrit if the job fails.
+  Defaults to the Jenkins build URL or the url_pattern configured in
+  zuul.conf.  May be supplied as a string pattern with substitutions
+  as described in url_pattern in :ref:`zuulconf`.
+
+**success-pattern (optional)**
+  The URL that should be reported to Gerrit if the job succeeds.
+  Defaults to the Jenkins build URL or the url_pattern configured in
+  zuul.conf.  May be supplied as a string pattern with substitutions
+  as described in url_pattern in :ref:`zuulconf`.
+
 **hold-following-changes (optional)**
   This is a boolean that indicates that changes that follow this
   change in a dependent change pipeline should wait until this job
diff --git a/zuul/model.py b/zuul/model.py
index 9ec4a68..57810b9 100644
--- a/zuul/model.py
+++ b/zuul/model.py
@@ -314,6 +314,8 @@
         self.name = name
         self.failure_message = None
         self.success_message = None
+        self.failure_pattern = None
+        self.success_pattern = None
         self.parameter_function = None
         self.hold_following_changes = False
         self.voting = True
@@ -329,6 +331,8 @@
     def copy(self, other):
         self.failure_message = other.failure_message
         self.success_message = other.success_message
+        self.failure_pattern = other.failure_pattern
+        self.success_pattern = other.success_pattern
         self.parameter_function = other.parameter_function
         self.hold_following_changes = other.hold_following_changes
         self.voting = other.voting
diff --git a/zuul/scheduler.py b/zuul/scheduler.py
index 31a504e..d7fd544 100644
--- a/zuul/scheduler.py
+++ b/zuul/scheduler.py
@@ -114,6 +114,12 @@
             m = config_job.get('success-message', None)
             if m:
                 job.success_message = m
+            m = config_job.get('failure-pattern', None)
+            if m:
+                job.failure_pattern = m
+            m = config_job.get('success-pattern', None)
+            if m:
+                job.success_pattern = m
             m = config_job.get('hold-following-changes', False)
             if m:
                 job.hold_following_changes = True
@@ -706,16 +712,23 @@
                    "rebase your change and upload a new patchset."
         else:
             if self.sched.config.has_option('zuul', 'url_pattern'):
-                pattern = self.sched.config.get('zuul', 'url_pattern')
+                url_pattern = self.sched.config.get('zuul', 'url_pattern')
             else:
-                pattern = None
+                url_pattern = None
             for job in self.pipeline.getJobs(changeish):
                 build = changeish.current_build_set.getBuild(job.name)
                 result = build.result
-                if result == 'SUCCESS' and job.success_message:
-                    result = job.success_message
-                elif result == 'FAILURE' and job.failure_message:
-                    result = job.failure_message
+                pattern = url_pattern
+                if result == 'SUCCESS':
+                    if job.success_message:
+                        result = job.success_message
+                    if job.success_pattern:
+                        pattern = job.success_pattern
+                elif result == 'FAILURE':
+                    if job.failure_message:
+                        result = job.failure_message
+                    if job.failure_pattern:
+                        pattern = job.failure_pattern
                 url = None
                 if build.url:
                     if pattern: