Display broken CircleCI builds with a Particle Mesh IoT Network
This project piggybacks on my last article Creating an IoT Scrolling Marquee with Slack and Particle.
Instead of a scrolling marquee displaying unique messages, I wanted to only receive notifications when a continuous integration platform failed a build. Things that would be helpful in this notification would be, what branch failed and who was the user.
Want to see the end product? Skip to the conclusion
CircleCI is a great continuous integration platform that I use a lot for personal projects, however I sure this approach (or similar) will work with most.
The CircleCI config.yml
allows you to call a command on “Build Failed”.
This is great, because we can cURL a POST request to an API passing it our CIRCLE_BRANCH
and CIRCLE_USERNAME
.
Easy!
Once additional spin on my approach with this project is that I wanted to use Particle Mesh Networking. Essentially, a Particle mesh network consists of a Gateway device with network access, and one/may Endpoint devices. A much better explanation can be found here on the Particle blog: How to build a wireless mesh network.
My example is going to use the Argon wifi Gateway device, and 2 x Xenon Endpoint devices.
Gateway and Endpoint devices can communicate with each other by publishing or subscribing to events. Only devices on this network will have access to these events.
Knowing this, we can define a function on our Gateway device (Argon) which when called can then emit a custom event on our network. Our endpoints, which will be subscribing to the event, can then perform a specific action.
My plan is to have 1 Xenon endpoint operate a scrolling marquee, while the other Xenon endpoint displays a tally of the amount of broken builds. What this showcases is that in a workplace environment, we could have multiple devices on our network displaying notifications of broken builds and perhaps more detailed statistics.
All the source code can be found here.
CircleCI
First we need a build that will fail.
Create a circleci repo and define your .circleci/config.yml
file.
Make sure you then select your git repo in the circleci app.
version: 2
jobs:
build:
working_directory: ~/repo
docker:
- image: circleci/node:8
steps:
- checkout
- run: npm install
# npm script 'foo' does not exist in our basic package.json, so should fail
- run: npm foo
- run:
name: Build Success
when: on_success
command: |
if [ "$CIRCLE_NODE_INDEX" == 0 ]; then
echo "Success"
fi
- run:
name: Build Failed
when: on_fail
# update reference to path_to_your_api/display
command: |
if [ "$CIRCLE_NODE_INDEX" == 0 ]; then
curl -d '{ "branch": "'"$CIRCLE_BRANCH"'", "user": "'"$CIRCLE_USERNAME"'"}' -H "Content-Type: application/json" -X POST path_to_your_api/display
fi
Create a very basic package.json
file for your project.
{
"name": "my-failing-circleci-app",
"private": true,
"version": "1.0.0",
"description": "a basic manifest",
"main": "index.js",
"scripts": {},
"devDependencies": {}
}
Pushing this to github with cause circleci to build your project.
First it will install npm dependencies, and then npm foo
which shouldn’t existing in your package.json
file, resulting in a failed build.
API
I have taken the API I created in Creating an IoT Scrolling Marquee with Slack and Particle and just removed unecessary endpoints.
On /api/display
the API will log into my device and call it’s display_broken_build
function, passing it the branch
and user
values.
app.post('/api/display', async (req, res, next) => {
const { branch, user } = req.body
if (!auth) {
await Particle.login({username, password})
.then(data => {
auth = data.body.access_token
})
.catch(({ status, message }) =>
res.status(status).send({ error: message })
)
}
const message = `Broken Build! ${branch} | ${user}`
await Particle.callFunction({
deviceId,
name: 'display_broken_build',
argument: message,
auth
})
.then((data) => res.send(message))
.catch((err) => {
console.error(err)
res.send(`callFunction error: ${JSON.stringify(err)}`)
})
next()
})
All secrets in my .env
have been updated to connect to my Argon Gateway device.
PDEVICE=particle_device_id
PUSER=particle_user
PPASS=particle_password
Once again the API is then hosted on now, and my now.json
manifest exposes my route and secrets to the API.
> now secret add mesh-pub-user foo@bar.com
Once the API has deployed, update the reference to the endpoint in your .circleci/config.yml
file.
Argon Gateway
I’m using the Argon as my gateway device.
Using Particles’ Workbench (or Web IDE), I flashed the argon-publish.ino on my device.
You’ll notice on setup I define the function display_broken_build
that accepts a command
(string), and on the event that function is called via the API, I call a displayMessage
function, which will then emit matrix_display_message
out onto the network.
int displayMessage(String command) {
Mesh.publish("matrix_display_message", command);
digitalWrite(D7, HIGH);
delay(1000);
digitalWrite(D7, LOW);
}
void setup() {
pinMode(D7, OUTPUT);
Particle.function("display_broken_build", displayMessage);
}
void loop() {}
You’ll notice on setup I define the function display_broken_build
that accepts a command
(string), and on the event that function is called via the API, I call a displayMessage
function, which will then emit matrix_display_message
out onto the network.
XENON - Scrolling Marquee
The code for this device is very similar to the original scrolling marquee example.
The difference being that on setup is subscribes to the matrix_display_message
event. Once received, call displayMessage
, resulting in writing to the LCD.
Mesh.subscribe("matrix_display_message", displayMessage);
Source > xenon-subscribe-scrolling-marquee.ino
XENON - Counter
The code for this device is pretty simple.
I initialise a count of 0
and everytime matrix_display_message
is published it calls logToDisplay
, incrementing the count by one and sending it to the display.
#include "adafruit-led-backpack.h"
Adafruit_7segment matrix = Adafruit_7segment();
int count = 0;
void logToDisplay(const char *event, const char *data) {
digitalWrite(D7, HIGH);
delay(1000);
digitalWrite(D7, LOW);
count++;
matrix.print(count);
matrix.writeDisplay();
}
void setup() {
Mesh.subscribe("matrix_display_message", logToDisplay);
matrix.begin(0x70);
matrix.print(count);
matrix.writeDisplay();
}
void loop() {}
Source > xenon-subscribe-counter.ino.
Conclusion
Once the API was deployed, devices flashed, and failing CircleCI repo published, I had my own sweet IoT network of displays notifying me when a build had failed.
Hackster Instructions - Broken CircleCI builds and Particle Mesh
I sourced all hardware and components for this example, from Core Electronics