Come qualsiasi team di sviluppatori che lavora in Scrum, anche il Team OneAPI ha una chiara "Definition of done" (DoD) per ciascuna attività così che qualcun altro possa provare una nuova funzione, rivedere un codice ecc., fino al punto di avere anche una pagina dedicata su Confluence.
Trovare quel "qualcun altro", tuttavia, non è sempre facile. Al contrario, scegliere il "fortunato" può rivelarsi un'impresa ardua: al momento di indicare una persona, tutti sono improvvisamente impegnati o di fretta, o semplicemente fanno orecchie da mercante. Così abbiamo dovuto fare in modo che la scelta diventasse casuale.
PRIMA ITERAZIONE - SCRIPT GROOVY
...abbastanza banale:
def list = Arrays.asList(Member.values())
Collections.shuffle(list)
print list.iterator().next()
public enum Member {
MATS,
DENIS,
PETAR,
MATO
}
...ma è stata completata in pochi secondi. In questa iterazione era necessario randomizzare e assegnare l'attività su JIRA. Essendo però semplice da compromettere, e dato che a nessuno piacciono le attività DoD, abbiamo visto una certa... mancanza di fiducia.
SECONDA ITERAZIONE - BOOKMARKLET
...non male, ma il problema era ancora lì:
javascript:(function () {
function random(){
var devs = ["Matija Bruncic", "Denis Cutic", "Matija Matosevic"];
return devs[Math.floor(Math.random()*devs.length)]
}
var assignee = random();
document.querySelectorAll("[data-field-text='"+assignee+"']")[0].setAttribute("selected", "selected");
document.getElementById("assign-issue-submit").click()
})();
In questo caso era necessario andare sull'attività JIRA nel browser ed eseguire bookmarklet. Anche così, tuttavia, nessuno si fidava, e anche in questo caso era necessario che il codice fosse diverso per ogni membro. Ah, e probabilmente non avrebbe funzionato su IE.
TERZA ITERAZIONE - SLACKBOT
...una possibilità molto semplice, che ha risolto i problemi di fiducia della randomizzazione.
Slack ha Slackbot, un bot di default in grado di fornire una risposta predefinita a una domanda predefinita. Ma quando ti sceglie, se non fosse un bot vorresti strozzarlo!
ITERAZIONE FINALE - HUBOT
...di gran lunga la migliore.
Uno dei motivi per cui abbiamo adottato Slack è la sua integrazione snella con i bot, e il fatto che rendesse possibile scrivere codici utili e interessanti. Abbiamo così deciso di usare Slack-Hubot. All'interno nasconde Hubot, ed è semplice da configurare e programmare. Non scenderò troppo nei dettagli, ma la documentazione è disponibile online.
Oh, ed è necessario dare un nome al bot. Il nostro è un tipo tosto... così lo abbiamo chiamato Rambot. La logica è programmabile in coffeescript, ed è come scrivere un'app nodejs. Ad esempio è possibile elencare le interfacce di rete:
module.exports = (robot) ->
robot.hear /rambot, where are you?/i, (res) ->
os = require('os');
ifaces = os.networkInterfaces();
Object.keys(ifaces).forEach (ifname) ->
ifaces[ifname].forEach (iface) ->
if 'IPv4' != iface.family or iface.internal != false
# skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses
return
# this interface has only one ipv4 adress
res.send 'here somewhere: ' + ifname, iface.address
return
return
Ma torniamo all'argomento principale!
Abbiamo creato uno script campione che riconosce l'utente che invia la richiesta e sceglie randomicamente uno degli altri membri del team:
users = ["denis.cutic", "josip", "mmatosevic", "mbruncic", "pducic"]
module.exports = (robot) ->
robot.hear /dod/i, (response) ->
possible = (user for user in users when user != response.message.user.name)
lucky = possible[Math.floor(Math.random() * possible.length)]
response.send "Hi #{response.message.user.name}! Your dod is assigned to #{lucky}"
Ma non riuscivamo a smettere di programmare, così abbiamo aggiunto una funzione che consente a Rambot di aggiornare gli utenti a cui sono assegnate delle attività tramite l'API JIRA:
jiraData = JSON.stringify({
fields: {
assignee: {
name: luckyJiraUsername
}
}
})
response.http("https://jira.infobip.com/rest/api/2/issue/#{task}")
.auth(jiraUsername, jiraPass)
.header('Content-Type', 'application/json')
.put(jiraData) (err, res, body) ->
response.send(body)
E, ovviamente, una funzione basata sull'API Infobip SMS per informare il malcapitato tramite SMS:
ibData = JSON.stringify({
to:users[lucky].gsm,
text:"You're the lucky one: #{task}"
})
response.http("https://api.infobip.com/sms/1/text/single")
.auth(infobipUsername, infobipPass)
.header('Content-Type', 'application/json')
.post(ibData) (err, res, body) ->
response.send "sent SMS!"
Il codice finale ha un aspetto del genere:
jiraUsername = process.env.HUBOT_JIRA_USERNAME
jiraPass = process.env.HUBOT_JIRA_PASS
infobipUsername = process.env.HUBOT_IB_USERNAME
infobipPass = process.env.HUBOT_IB_PASS
users = {
"denis.cutic": {
jira: "dcutic",
gsm: "3859********"
},
josip: {
jira: "jantolis",
gsm: "3859********"
},
mmatosevic: {
jira: "mmatosevic",
gsm: "3859********"
},
mbruncic: {
jira: "mbruncic",
gsm: "3859********"
},
pducic: {
jira: "pducic",
gsm: "3859********"
},
}
module.exports = (robot) ->
robot.hear /dod (.*)/i, (response) ->
task = response.match[1]
possible = (user for user, value of users when user != response.message.user.name)
lucky = possible[Math.floor(Math.random() * possible.length)]
luckyJiraUsername = users[lucky].jira
if jiraUsername? && jiraPass?
jiraData = JSON.stringify({
fields: {
assignee: {
name: luckyJiraUsername
}
}
})
response.http("https://jira.infobip.com/rest/api/2/issue/#{task}")
.auth(jiraUsername, jiraPass)
.header('Content-Type', 'application/json')
.put(jiraData) (err, res, body) ->
response.send(body)
if infobipUsername? && infobipPass?
ibData = JSON.stringify({
to:users[lucky].gsm,
text:"You're the lucky one: #{task}"
})
response.http("https://api.infobip.com/sms/1/text/single")
.auth(infobipUsername, infobipPass)
.header('Content-Type', 'application/json')
.post(ibData) (err, res, body) ->
response.send "sent SMS!"
response.send "Hi #{response.message.user.name}! Your dod is assigned to #{lucky} "
Dal punto di vista dell'utente, le cose stanno così:
Non può farci nulla! Se hai un'idea per migliorare ancora Rambot, siamo lieti di ascoltare! L'intero codice è disponibile su GitHub.
Buon DoD!
Di Petar Ducic, Software Engineer / Team Leader