In the previous two articles in this series, we created our Lambda function and the Alexa Skill. In this tutorial, we’ll learn how to connect Amazon Alexa Skill to the AWS Lambda function to trigger Bitrise builds via a voice user interface.
After we discussed how to create the AWS Lambda function and Amazon Alexa Skill, it’s time now to put them all together and use Alexa to trigger a Bitrise Build using the Lambda function.
Sounds good? Let’s get started!
The solution
As we know AWS Lambda function needs to be triggered by any supported triggers. Because of this, we will use Alexa and Alexa Skill Kit (ASK) to trigger the function. Inside the function, we will add our logic to receive the voice command from Alexa and pass it to Bitrise API using the POST endpoint to trigger a new build in our Bitrise app. Then, we will get the response from Lambda to the Alexa as a JSON output.
Previously our Lambda function was using the following code to trigger the build via the POST endpoint:
var request = require('request');
let app_slug = process.env.APP_SLUG
let api_key = process.env.API_KEY
var options = {
'method': 'POST',
'url': 'https://api.bitrise.io/v0.1/apps/'+app_slug+'/builds',
'headers': {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': api_key
},
body: JSON.stringify({
'build_params': {
'branch': 'chapter-3',
'workflow_id': 'primary'
},
'hook_info': {
'type': 'bitrise'
}
})
};
exports.handler = async function(event, response) {
console.log(response.body);
return "done..."
}
request(options, function (error,response) {
if (error) throw new Error(error);
console.log(response.body);
});
But with the Alexa Skill Kit, we need to change this handler and the code logic with something to work with the voice command and the Skill Kit by the following code:
var request = require('request');
exports.handler = (event, context, callback) => {
try {
if (event.request.type === 'LaunchRequest') {
callback(null, buildResponse('Hello from Bitrise'));
} else if (event.request.type === 'IntentRequest') {
const intentName = event.request.intent.name;
if (intentName === 'triggerBuild') {
buildBitriseApp(function (err, result) {
if(!err) callback(null, buildResponse('The build was launched! make sure you take a look.'));
else callback(null, buildResponse("Please check your build logs. Something went wrong."));
});
} else {
callback(null, buildResponse("Sorry, i don't understand"));
}
} else if (event.request.type === 'SessionEndedRequest') {
callback(null, buildResponse('Session Ended'));
}
} catch (e) {
context.fail(`Exception: ${e}`);
}
};
function buildResponse(response) {
return {
version: '1.0',
response: {
outputSpeech: {
type: 'PlainText',
text: response,
},
shouldEndSession: true,
},
sessionAttributes: {},
};
}
let api_key = process.env.API_PERSONAL_KEY;
let app_slug = process.env.APP_SLUG;
function buildBitriseApp(callback) {
var options = {
'method': 'POST',
'url': 'https://api.bitrise.io/v0.1/apps/'+app_slug+'/builds',
'headers': {
'Authorization': api_key
},
body: JSON.stringify({
"User-Agent": "alexa-skill",
"hook_info": {
"type": "bitrise"
},
"build_params": {
"branch": 'main',
"workflow_id": 'primary'
}
})
};
request.post(options, function(error, response, body){
if(error){
callback("ERROR");
} else {
callback(null,"SUCCESS");
}
});
}
In the above code, we are specifying that when we launch the skill we’ll receive a message “Hello from Bitrise” and check if the intentName is “triggerBuild”.
We can call the function “buildResponse”, which calls the POST endpoint from Bitrise API with the payload that we added, specifying the following parameters:
- API Key
- App Slug
- Branch Name
- Workflow ID
And we already added the API Key and the App Slug as Lambda Environment Variables.
Click the Deploy button to save the changes.
Now it’s time to add the Alexa Skill Kit as a trigger for our Lambda function.
Click on the + Add trigger button in the Function Overview and the trigger configuration and select the Alexa Skills kit.
Then you can add your Skill ID and click the add button. You can get the Skill ID from the Endpoint menu in the Skill Kit build section.
And now the trigger should be displayed like this:
On the other side, you need to add the Lambda ARN from the Lambda function page.
And go to the Alexa Skill kit by opening the Endpoint option in the build section and pasting the ARN there, then clicking Save Endpoint and choosing yes.
After that, click on the Code button and you will see a notification that the default endpoint has changed and is no longer hosted by Alexa because it’s now on AWS and Lambda.
Test the integration between Lambda and Alexa
Now click on the Test button to go to the Alexa Simulator page. And in the development write the following phrase:
“Alexa, open bitrise ci” and click the Enter button
To trigger a new build you can write: “Alexa, ask bitrise ci trigger a new bitrise build” and click the Enter button
Open your Bitrise Dashboard and, if the command ran successfully, you will notice that the build was triggered successfully and you can abort it after that to save your credits.
We did it! Congratulations you have successfully triggered your first build using Bitrise API, Lambda via Alexa Skill kit.
Amazing!
Specifying the git branch name and the workflow name using Slots
You may notice that in our Lambda function we are using hardcoded values like the branch name and the workflow name as build parameters.
"build_params": {
"branch": 'main',
"workflow_id": 'primary'
}
But what if I need to use different values... Can I do that?
Of course, you can!
In the first part, we mentioned that Alexa Skill has a key feature which is Slots and you can use them as variables that you provide with a name and a slot type for each slot in your interaction model.
Let’s do it then!
- In your Alexa Skills Console, click on Build then click on Slot Types, and click Add Slot Type.
- Add BranchName and click the Next button
- Enter default values such as main and master
- Add another Slot WorkflowName click the Next button
- Enter default values such as deploy and primary
- Click on Intents then triggerBuild intent
- Change the Sample Utterance to be like the following
The Slots will be added under Intent Slots and make sure that the Slot Type is what we created previously.
- Save and Build the Model
Now we need to do the same change in our Lambda function by passing the branch and workflow name as parameters to be able to use different values like the following code:
var request = require('request');
var workflowSlot;
var branchSlot;
exports.handler = (event, context, callback) => {
try {
if (event.request.type === 'LaunchRequest') {
callback(null, buildResponse('Hello from Bitrise'));
} else if (event.request.type === 'IntentRequest') {
const intentName = event.request.intent.name;
workflowSlot = event.request.intent.slots.WorkflowName.value;
branchSlot = event.request.intent.slots.BranchName.value;
if (intentName === 'triggerBuild') {
buildBitriseApp(function (err, result) {
if(!err) callback(null, buildResponse('The build was launched with '+ workflowSlot +' workflow for '+ branchSlot + ' branch. make sure you take a look.'));
else callback(null, buildResponse("Please check your build logs. Something went wrong."));
});
} else {
callback(null, buildResponse("Sorry, i don't understand"));
}
} else if (event.request.type === 'SessionEndedRequest') {
callback(null, buildResponse('Session Ended'));
}
} catch (e) {
context.fail(`Exception: ${e}`);
}
};
function buildResponse(response) {
return {
version: '1.0',
response: {
outputSpeech: {
type: 'PlainText',
text: response,
},
shouldEndSession: true,
},
sessionAttributes: {},
};
}
let api_key = process.env.API_PERSONAL_KEY;
let app_slug = process.env.APP_SLUG;
function buildBitriseApp(callback) {
var options = {
'method': 'POST',
'url': 'https://api.bitrise.io/v0.1/apps/'+app_slug+'/builds',
'headers': {
'Authorization': api_key
},
body: JSON.stringify({
"User-Agent": "alexa-skill",
"hook_info": {
"type": "bitrise"
},
"build_params": {
"branch": branchSlot,
"workflow_id": workflowSlot
}
})
};
request.post(options, function(error, response, body){
if(error){
callback("ERROR");
} else {
callback(null,"SUCCESS");
}
});
}
In the above code we added these values:
var workflowSlot;
var branchSlot;
workflowSlot = event.request.intent.slots.WorkflowName.value;
branchSlot = event.request.intent.slots.BranchName.value;
And in the build parameters we replace the hardcoded values:
"build_params": {
"branch": branchSlot,
"workflow_id": workflowSlot
}
Click Deploy to save the changes and now you can test your Alexa Skills Kit.
In the Alexa Simulator write the following phrase:
“Alexa, ask bitrise ci trigger build for primary workflow for main branch”
Note: You should have a workflow and branch name with the same values otherwise the build will not be triggered
I will try to use different apps and specify the workflow name and the branch name as well.
Monitor AWS Lambda with CloudWatch
You can also check the runtime metrics for your functions on Amazon CloudWatch. The metrics shown are an aggregate view of all function runtime activity
What’s next?
In the next article, we will distribute our Alexa Skills Kit to be able to use in our AWS organization.
Thank you for reading these articles! If you have any questions or feedback, please reach out to us on Twitter, email, or join the Bitrise Slack to chat with us directly.