Last Updated Dec 13, 2015 — DevOps Expert
Tracking the Dev/QA Cycle with XL Release
DevOps
Let's take some inspiration from this diagram from Gene Kim, author of The Phoenix Project, and consider how DevOps tooling can help you automate "The Third Way": the continual experimentation and learning cycle.
We also provide this short script in demo/restartMarker.py so the task saves its own task id:<type type="demo.RestartMarker" extends="xlrelease.PythonScript">
<property name="scriptLocation" default="demo/restartMarker.py" />
<property name="restartTaskId" category="output" />
</type>
restartTaskId = '-'.join(getCurrentTask().id.split('/')[1:])
And we provide a variable denoted by the ${...} notation when we place this task in the release template, so our task id is accessible to any subsequent task within this release.git commit -am "Release1234567-Phase1234567-Task1234567 Commit message here"
Finally, to close the loop, a post-commit Git hook invokes a short Python script to parse the commit message and take the appropriate action: either begin a new release or restart another cycle if a task id is present.And a Python script handles the plumbing:#!/bin/sh
python /usr/local/demo/gitUtilities/parseCommitAndCallXLR.py `git log -1 HEAD | tail -1 | awk '{print $1}'`
And now we arrive at the bottom line: when XL Release tracks the Dev/QA cycle in this way, it furnishes statistics like this, giving how many iterations and their durations:import re
import requests
import sys
HTTP_SUCCESSFUL = 200
def createNewReleaseAndStart():
print "No phase id found in commit message"
print "Invoke new release from template here"
newReleaseUrl = "http://admin:xlradmin@localhost:5516/releases"
payload = '{"title":"Test-Post-Call-6","description":null,"owner":{"username":"admin","fullName":"XL Release Administrator"},"scheduledStartDate":"2015-12-10T14:00:00.000Z","dueDate":"2015-12-10T22:00:00.000Z","plannedDuration":null,"variables":[{"key":"${restartTaskId}","value":"","password":null,"type":"DEFAULT"}, {"key":"${testResult}","value":"","password":null,"type":"DEFAULT"}],"tags":["ACME"],"flag":{"status":"OK"},"abortOnFailure":false,"scriptUsername":"admin","scriptUserPassword":"xlradmin","templateId":"Release2338668"}'
headers = {'Content-Type': 'application/json'}
r1 = requests.post(newReleaseUrl, data=payload, headers=headers)
if r1.status_code != HTTP_SUCCESSFUL:
sys.exit(1)print r1.json()['id']
startReleaseUrl = "http://admin:xlradmin@localhost:5516/releases/%s/start" % r1.json()['id']
r2 = requests.post(startReleaseUrl)
if r2.status_code != HTTP_SUCCESSFUL:
sys.exit(1)def restartReleaseAndResume(m):
taskId = m.string[:m.end()]
phaseId = '-'.join(taskId.split('-')[:-1])
releaseId = '-'.join(phaseId.split('-')[:-1])
print "Restarting XL Release Phase:\nReleaseId is %s\nPhaseId is %s\nTaskId is %s\n" % (releaseId, phaseId, taskId)
restartPhaseUrl = "http://admin:xlradmin@localhost:5516/releases/%s/restartPhases?fromPhaseId=%s&fromTaskId=%s" % (releaseId, phaseId, taskId)
resumeReleaseUrl = "http://admin:xlradmin@localhost:5516/releases/%s/resume" % releaseId
payload='[{"key":"${restartPhaseId}","value":"Test","password":null,"type":"SCRIPT_RESULT"},{"key":"${restartReleaseId}","value":"Test","password":null,"type":"SCRIPT_RESULT"},{"key":"${restartTaskId}","value":"Test","password":null,"type":"SCRIPT_RESULT"}]'
headers = {'Content-Type': 'application/json'}
r3 = requests.post(restartPhaseUrl)
if r3.status_code != HTTP_SUCCESSFUL:
sys.exit(1)r4 = requests.post(resumeReleaseUrl, data=payload, headers=headers)
if r4.status_code != HTTP_SUCCESSFUL:
sys.exit(1)argument = sys.argv[1]
print "Argument is %s\n" % argument
taskMatch = re.compile('^Release[0-9]+-Phase[0-9]+-Task[0-9]+')
m = taskMatch.match(argument)
if m is None:
createNewReleaseAndStart()
else:
restartReleaseAndResume(m)
Have you seen the new XL Release Doc's site? Videos, How to's and much more all in one convenient location. Check it out...