DEV Community

Adrien Chauve
Adrien Chauve

Posted on • Originally published at tech-blog.serenytics.com on

5

Use a Slack bot to deploy your app

Deploying your web application is certainly not the most interesting activity of your day. But what if it could be simple, reliable and fun?

Using a Slack bot to deploy your app is actually fun and it brings a lot of perks to the table.

Why?

Aside from the fun to talk to your bot through your preferred chat app, the main benefit is that there is finally a single source of trust for your deployments. If you’re using software engineering good practices, your code should be versioned, your database schema should be versioned, your infrastructure should be versioned… so you know exactly what code is deployed on what infrastructure. But what about the code that deploys the rest of the code (whatever it is: shell script, fabric, ansible, salt, puppet, chef, anything…)? Sure it is also versioned. But this code is not deployed on your servers, it’s executed on a developer|ops|devops machine. Has the code been modified? Is the working directory dirty? Is it the latest available version? You never know. Unless all the deployments are executed from a single machine that cannot be accessed through SSH (only deployed to) and that everybody uses. Yes, I’m talking about your Slack bot.

Besides knowing exactly how your deployments are made, another essential point is communication. Everybody on the team needs to know when a deployment starts, when it ends and what has been deployed. Everybody also needs to know the deployment history. What is currently deployed? Did someone perform a rollback? Just use a Slack bot, and you get all that for free.

There are other smaller perks too. You don’t need your computer with you for emergency support. You can restart a Docker container or perform a rollback right from your phone. You just need the mobile Slack app! It’s also really easy to handle deployment permissions. Just invite the right people to the authorized channel!

Did I mention it’s a lot of fun to write a Slack bot?

Why a bot, why not just a slash command?

Slack custom slash commands are a great way to trigger an event through a web hook and print the results in Slack. For instance I added to our Serenytics team a command /srn-active-users that returns the last active users on our web app. We could use this to deploy our stack. But a bot is actually a better solution, for two main reasons.

You can have a conversation with a bot. You can’t have a conversation with a slash command. And I think this is a killer feature to perform deployments. Why? Because you first ask your bot to deploy your app. It answers with the list of new commits available since the last deployment, and asks you if you want to proceed. Then you can validate the changes before going on. And it’s just great.

Technically, another benefit from using a bot is that you don’t need to deploy a new server somewhere with a domain, an API, an SSL certificate, authentication… a simple daemon script is enough.

What deploying actually looks like in Slack

Here is the Slack transcript of one of our own actual deployment. Our bot is called Nestor and I’m deploying our frontend application on our AWS servers.

Example bot code

Here is the skeleton of the Slack bot we use at Serenytics. It’s a complete running script where you just have to fill in the blanks: your slack token and the actual deployment code. I use shell commands and Docker Cloud python client in our case.

If you want to know more about Slack bots, go there.

import logging
import os
import time
import traceback
# pip install slackclient
from slackclient import SlackClient
SLACK_BOT_USER = 'YOUR_SLACK_BOT_USER_ID'
SLACK_BOT_MENTION = '<@%s>' % SLACK_BOT_USER
SLACK_BOT_NAME = 'nestor'
SLACK_CHANNEL = 'ask-nestor'
SLACK_TOKEN = os.environ.get('SLACK_TOKEN')
slack_client = SlackClient(SLACK_TOKEN)
class HelpException(Exception):
pass
def send_message(text):
slack_client.rtm_send_message(channel=SLACK_CHANNEL, message=text)
def has_conversation_started_with(user):
pass
def process_conversation(cmd, event):
pass
def process_help(*args):
pass
def process_deploy(cmd, event):
pass
def process_rollback(cmd, event):
pass
def process_restart(cmd, event):
pass
def process_event(event):
# filter out slack events that are not for us
text = event.get('text')
if text is None or not text.startswith((SLACK_BOT_NAME, SLACK_BOT_MENTION)):
return
# make sure our bot is only called for a specified channel
channel = event.get('channel')
if channel is None:
return
if channel != sc.server.channels.find(SLACK_CHANNEL).id:
send_message('<@{user}> I only run tasks asked from `{channel}` channel'.format(user=event['user'],
channel=SLACK_CHANNEL))
return
# remove bot name and extract command
if text.startswith(SLACK_BOT_MENTION):
cmd = text.split('%s' % SLACK_BOT_MENTION)[1]
if cmd.startswith(':'):
cmd = cmd[2:]
cmd = cmd.strip()
else:
cmd = text.split('%s ' % SLACK_BOT_NAME)[1]
# process command
try:
if has_conversation_started_with(event['user']):
process_conversation(cmd, event)
elif cmd.startswith('help'):
process_help(cmd, event)
elif cmd.startswith('deploy '):
process_deploy(cmd, event)
elif cmd.startswith('rollback '):
process_rollback(cmd, event)
elif cmd.startswith('restart '):
process_restart(cmd, event)
else:
send_message("*I don't know how to do that*: `%s`" % cmd)
process_help()
except HelpException:
return process_help()
def process_events(events):
for event in events:
try:
process_event(event)
except Exception as e:
logging.exception(e)
msg = '%s: %s\n%s' % (e.__class__.__name__, e, traceback.format_exc())
send_message(msg)
def main():
if slack_client.rtm_connect():
send_message('_starting..._')
# --
# Here is a good place to init git repositories if needed, in order to provide git-based features:
# - list of commits to deploy
# - history of deployments
# - status of deployed services vs what's available in git
send_message("*All right, I'm ready, ask me anything!*")
while True:
events = slack_client.rtm_read()
if events:
logging.info(events)
process_events(events)
time.sleep(0.1)
else:
logging.error('Connection Failed, invalid token?')
if __name__ == '__main__':
main()

Give us a shout if you want more details about integrating git history or deploying on Docker Cloud.


AWS GenAI LIVE image

How is generative AI increasing efficiency?

Join AWS GenAI LIVE! to find out how gen AI is reshaping productivity, streamlining processes, and driving innovation.

Learn more

Top comments (0)

AWS Q Developer image

Your AI Code Assistant

Generate and update README files, create data-flow diagrams, and keep your project fully documented. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay