Use scheduling in scripts
A scheduled script can be used within a custom task script to check for the availability of a resource or to do something when conditions are satisfied, see Create custom task types.
Release will schedule the execution of the scripts and poll for the availability of the resource according to a configurable interval. If the server is stopped while the polling script is running, Release will restart the script when the server is started again.
This example shows how the Jenkins Build task is implemented using different scripts for different stages of the Jenkins build lifecycle.
This is the definition of the Jenkins Build task in the
... <type type="jenkins.Build" extends="xlrelease.PythonScript"> <property name="scriptLocation" default="jenkins/Build.py" hidden="true" /> <property name="iconLocation" default="jenkins/jenkins.png" hidden="true" /> <property category="input" name="jenkinsServer" label="Server" kind="ci" referenced-type="jenkins.Server" description="Jenkins server to connect to"/> <property category="input" name="username" required="false" description="Overrides the password used to connect to the server"/> <property category="input" name="password" password="true" required="false" description="Overrides the password used to connect to the server"/> <property category="input" name="jobName" description="Name of the job to trigger."/> <property category="input" name="jobParameters" size="large" required="false" description="Jenkins job parameters, one parameter per line."/> <property category="output" name="buildNumber" required="false" description="Build number of the triggered job"/> <property category="output" name="buildStatus" required="false" description="Build status of the triggered job"/> <property category="script" name="location" required="false" description="Location header returned by jenkins"/> </type> ...
location property is of category
script and is similar to an
output property, the only difference is that it is not shown in the UI. The
script properties can be used to send information between Python scripts in the same task.
The Python script that is configured on the Jenkins Build task is
jenkins/Build.py and this is the first script that will be run.
... buildResponse = request.post(buildContext, '', contentType = 'application/json') if buildResponse.isSuccessful(): # Query the location header which gives a queue item position location = None if 'Location' in buildResponse.getHeaders() and '/queue/item/' in buildResponse.getHeaders()['Location']: location = '/queue/item/' + filter(None, buildResponse.getHeaders()['Location'].split('/'))[-1] + '/' task.setStatusLine("Build queued") task.schedule("jenkins/Build.wait-for-queue.py") else: print "Failed to connect at %s." % buildUrl buildResponse.errorDump() sys.exit(1)
The status line provided in
task.setStatusLine("Build queued") will appear in the UI:
task.schedule(pollingScriptPath) call lets Release know that after the current script is finished the task does not finish yet. Instead another script should be executed after a delay. You must pass the path to the script in the method argument. You can also specify a wait interval in seconds as a second parameter:
task.schedule(pollingScriptPath, 2). If not specified, the server will wait 5 seconds before invoking the script.
To fail the script, use
If the response was successful, the next script that is executed will be:
We have set the
location variable in the
Build.py script. Since we have defined in the
synthetic.xml as a variable of category
script, it will also be available in the
... if location: # Check the response to make sure we have an item response = request.get(location + 'api/json', contentType='application/json') if response.isSuccessful(): buildNumber = JsonPathResult(response.response, 'executable.number').get() if not buildNumber: # if there is no build number yet then we continue waiting in the queue task.schedule("jenkins/Build.wait-for-queue.py") else: # if we have been given a build number this item is no longer in the queue but is being built task.setStatusLine("Running build #%s" % jobBuildNumber) task.schedule("jenkins/Build.wait-for-build.py") else: print "Could not determine build number for queued build at %s." % (jenkinsURL + location + 'api/json') sys.exit(1) else: ...
If there is no build number yet, we need to keep waiting. We do this by scheduling the same script again. This is how you implement a polling loop using the
When the build is running, the request to the Jenkins server will return a build number and we can wait for the build to complete. The script changes the status line to tell that Jenkins is now running the build and script that will wait for build completion is now scheduled:
... response = request.get(jobContext + str(buildNumber) + '/api/json', contentType='application/json') if response.isSuccessful(): buildStatus = JsonPathResult(response.response, 'result').get() duration = JsonPathResult(response.response, 'duration').get() if buildStatus and duration != 0: print "\nFinished: %s" % buildStatus if buildStatus != 'SUCCESS': sys.exit(1) else: # Continue waiting for the build to be finished task.schedule("jenkins/Build.wait-for-build.py") else: ...
When this script finishes without error and no other script is scheduled, the task completes and Release moves on to the next task in the release flow.
This is a summary of what can happen during the execution of the script of a custom task and how it influences the state of the running task.
sys.exit()is called: the script stops executing and the task fails.
- An exception is raised: the script stops executing and the task fails.
task.schedule()is called: the script continues to execute, but when the script finishes, the task remains in progress and the next script is scheduled for execution.
- The script finishes without errors: unless
task.schedule()was called, the custom task completes and Release moves on to the next task in the release flow.
By default, the wait interval between scripts is 5 seconds, if not specified on the
task.schedule() call. You can configure this interval in the
xl.durations.customScriptTaskScheduleInterval = 10 seconds
The interval may not be less than 1 second.