SLACK ➡ PYTHON ➡JENKINS

Sabbir Ul Alam
7 min readMar 22, 2021

❗❗ Spoiler alert. This gonna be a very long post. But this gonna be fun.❗❗

One needs either motivations or necessities do to things. I’ve been following RIOT games since I was an undergraduate student. So, a few days earlier, I saw a tech blog on their website about how they use slack apps to manage their deployment. It was kinda fun and I thought why not try that in my workplace. So there, got the motivation, now time to do it.

In my last post, I talked about Jenkins pipeline and how to deploy it in widlfly container. I’ll use the same pipeline job for this example as well. This post will be divided into three parts a) Slack App b) Python-backend for the Slack app c) Jenkins pipeline

SLACK APP

First thing first, let’s go to https://api.slack.com/ and create a custom app. Give it a cool name and select your workspace. I’ll be naming it JARVIS (just a rather very intelligent system ). Then let’s go to the feature and functionality section.

App Features

We’ll be using all the features listed here for our app. Let’s start with-

Slash Commands: We’ll use a slash command called /jarvis to start the app from our workspace channels. To do that, select the slash commands option and create a new command.

Notice that there is a field named Request URL . When we’ll write/jarvis in our slack channel, SLACK will send some info/event to this URL. As I am building this locally on my machine and SLACK doesn't know my local address, so to let SLACK know about my local address, I am gonna use a third-party app ngrok to convert our local address to a public address.

Let’s download ngrok, and run it. We should see a console app.

Now again lets go to ngrok site and log in. From there let’s copy our auth token. Open ngrok console and type ngrok authtoken <your_token> .

Our python slack app will use 3000 port. So in ngrok console type ngrok http 3000 to generate a public address. Keep in mind, we need to keep ngrok running in the background for our app to use the public address.

Let’s copy the forwarding address, append/slack/events and paste it in our slash command’s request url and save it.

Incoming Webhooks: We’ll be needing webhooks for our app as we want to send data from Jenkins to our slack channels. So to do that, switch on the incoming webhook and click on Add new webhook to Workplace .

It will generate a webhook URL and an integration will be added to our channel. All sort of message we’ll post from Jenkins, we go through this webhook.

Event Subscriptions: To subscribe to events, enable evet subscription. It will need a request URL for our events. We can use the same forwarding URL we used for our slash command feature. But make sure you append /slack/events at the end of it.

When we do any sort of activities with our app, slack we send a request for that event to this request URL.

Interactivity: If we wanna use any sort of button or shortcut, we need to enable interactivity.

Here we can use the same request URL mentioned above for even subscriptions and slash command.

Permissions: Last but not least, Permissions. We need to add different scopes for our app to access different things. Such as we need to add Chat:wrtie a scope for our app to send messages to our workspace. There are lots of scopes out there, we’ll be using only these for now.
Finally, save it and install it in your workspace.

There you go. We are done with our slack app. Now let’s work on the next part- our python backend.

PYTHON BACKEND

Before we start writing our code. We need to add two environment variables.

  • SLACK_BOT_TOKEN
  • SLACK_SIGNING_SECRET

We can get these values from https://api.slack.com SLACK_SIGNING_SECRET can be found under basic information section and SLACK_BOT_TOKEN can be found under OAuth & Permissions.We also need to install theslack_bolt package.

So our code will look like this. We are using 3000 port here same as ngrok.

import os
# Use the package we installed
from slack_bolt import App
# Initializes your app with your bot token and signing secret
app = App(
token=os.environ.get("SLACK_BOT_TOKEN"), signing_secret=os.environ.get("SLACK_SIGNING_SECRET"))

# Start your app
if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000)))

If we run this code, nothing noticeable will happen. So let's make our app fun. Remember the slash command we created /jarvis . If we type this in our app channel, slack will send us an event. And we can do whatever we wanna do with it.

But here I’ll be showing this message in response to the /jarvis command. If we press the Build & Deploy button, it will send a trigger to build Jenkins jobs. you can see the whole python code HERE on git.

JENKINS

So far we’ve built our slack app, wrote the python backend. Now let's finish up with our Jenkins pipeline. To create a Jenkins pipeline from scratch you can see my previous story. I will modify the same pipeline. But before that, we need to configure Jenkins. To do that-

Go to manage jenkins > manage plugins , search for Slack Notification plugin and install it.

Then go to manage jenkins > configure system , scroll down to the slack section. Here add your workspace name, channel id, and credentials.

Credential kind has to be secrect text . To get your secret key, go to https://api.slack.com > select your app, find OAuth & Permission section. From there find the Bot User OAuth Token . Copy and add this to your Jenkins credentials.

To confirm everything is working properly, you can test your connections.

Now, let's modify our pipeline job. I’ll be using this pipeline job created previously.

To configure our pipeline, select This project is paremeterized .

We will use three-parameter. The parameter name must be unique and we will use them in our script. Our previous script was -

pipeline {
agent any
stages {
stage('Pull') {
steps {
git credentialsId: 'bitbucket', url: 'https://name@bitbucket.org/project'
}
}
stage('Build') {
steps {
bat 'mvn clean package'
}
}
stage('Deploy') {
steps {
build 'deploy'
}
}
}
}

As we can see, in our previous script it will always pull and build one project. But now we want to select the project name from slack and if the deployment fails we would like Jenkins to send us the log as well. So to do that we need to update our code accordingly. Here’s our new pipeline script-

import groovy.transform.Field@Field 
def res = "some value"
pipeline {
agent any
stages {
stage('Pull') {
steps {
script {
if (project == 'cms') {
git branch: branch, credentialsId: 'bitbucket', url: 'https://name@bitbucket.org/project2'
}
}
}
}
stage('Build') {
steps {
script{
if (project=='cms'){
bat 'gradle clean -Pprofile=%profile% build -x test'
}
}
}
post {
success {
script{
slackSend color: "good", message: "$project build complete. Now deploying"
}
}
failure {
slackSend color: "danger", message: "${project} build failed"
}
}
}
stage('Deploy') {
steps {
script{
if (project=='cms'){
res = build ('deploy')
}
}
}
post {
success {
slackSend color: "good", message: "$project demployment complete. <@$user_name>"
}
failure {
script{
echo "build number ${res.getNumber()} and ${res.getProjectName()}"
var= readFile "${JENKINS_HOME}/jobs/${res.getProjectName()}/builds/${res.getNumber()}/log"

attachments= [
[
"blocks": [
[
"type": "section",
"text": [
"type": "mrkdwn",
"text": "Deployment failed. See the attached logs:console:```${var}```"
]
]
]
]
]

slackSend(color: "danger", channel: "#sabbir-jenkins-channel", attachments: attachments)
}
}
}
}
}
}

There are some things to talk about that are new.

a) We are using post after steps. So when all the steps are done, post block will execute

b) We are using slackSend method, this method comes from previous installed Slack Notification plugin.

c) And last of all we are using slack block kit to send attachments. We can design our block with slack’s block kit builder .

All done, now if we use /jarvis command in our workspace, we shall see a prompt to select our project. And if we build our project from there, it should trigger our Jenkins pipeline. And if build and deployment were successful we shall this message in our workspace.

P.S. I am still learning and very new to all these. All the code mentioned above may not be the best optimized one, but hey it works.

--

--