Set up organization-wide GitHub release notifications to Slack
Learn how to create a GitHub tag and release on every production update, then send a notification to Slack with the release notes. All with reusable GitHub Actions workflows defined in a central repo.
If you are using GitHub and GitHub Actions, have multiple code repositories composing your product, and want an optimal way of tracking releases across your organization, read on, the setup is rather simple and scalable.
Outcome
Let’s quickly go over the outcome of this exercise to get you excited and align expectations.
Releases
Each time the production code is shipped, a new GitHub Release is created pointing to the appropriate version tag. The version can either be auto-incremented, or specified. This means you can use the approach whether you have already been doing versioned releases. If you were not, you would get versioned releases as a bonus.
Notifications
Right after the GitHub release is created, a message is sent to Github specifying the change author, the name of the subsystem, the version number, and the changelog specified in the PR description. You will be able to customize most of it to your liking.
☝️ If this is what you want, read on.
Setup
Here’s a short list of what is needed to make the above possible:
A GitHub repository for centralizing the release workflows.
A Slack app with Incoming Webhooks integration enabled.
A GitHub organization secret with the Slack webhook URL.
A release job reference in your existing GitHub Actions production workflows.
Centralized workflow repository
Go to your GitHub organization and create a new repository where we will store the reusable workflows. I prefer calling it “internal-workflows”, but feel free to rename it to anything you like, just make sure to replace all references in case you do rename it.
Go to the settings page of your new repository, navigate to “Actions” → “General” and enable other repositories in your organization to access it.
The next step is to decide whether to use versioned, unversioned releases or a mix of both. Versioned means you already follow versioning in your repositories and want to keep doing so, and hence you can pass the shared release workflow a version number and it will use that exact version. Unversioned means that the shared workflow will autoincrement semantic versions starting from v0.0.1
and updating the patch versions, meaning subsequent releases will be tagged as v0.0.2
, v0.0.3
, etc. You can change that in the workflow file to autoincrement any other version part.
The workflows are not that small so I’ve prepared the following gists for you to copy-paste into the .github/workflows folder in your new shared workflow repository:
You don’t need to do anything else here, the workflows will work for you out of the box if you follow the steps correctly. Think of these merely as reusable functions with inputs that execute a workflow instead of some other code.
Slack channel app with Incoming webhooks
Create a Slack app that will receive notifications from GitHub from Slack’s admin panel at https://slack.com/admin. Call the app anything you want, I prefer just “Release”, and make sure you pick the right workspace.
Then, go into the app → “Add features and functionality” → “Incoming webhooks” → “Add a new webhook to workspace”, specify the channel you want to use (create one first if it doesn’t exist), take a note of the resulting Webhook URL, we’ll need it in the next step.
The default icon looks rather uninteresting, so please prepare a nice icon you want to use with these notifications, then head to https://api.slack.com/apps, select your newly created app, scroll down to the bottom, and update it to look beautiful. They will ask for an icon of at least 512x512.
Github organization secret
Head to the homepage of your GitHub organization and navigate to settings. Make sure you are on the organization-wide settings page, not the settings page of any individual repository. Select “Secrets and variables” → “Actions” → “New organization secret”.
Call the new secret “SLACK_WEBHOOK_URL_PRODUCT_RELEASES” since it is referenced in the gists provided earlier, but if you wish to change the name, make sure you do it in the shared workflow code too. Use the Webhook URL from the previous step as the value, double check to ensure you are not copying any spaces or empty lines.
Release job reference
You’re all set to trigger the workflows and you can now reference the new shared workflows in your production workflows. The examples assume your production branch is called “main” and any merge to that branch triggers a production deployment, but you can adjust any of this to your particular situation.
Keep in mind that this needs to be added to every repository for which you want to track releases in this way.
Unversioned
Assuming you already have a step called something like “deploy”, you need to add a “post_deploy” step, point to the shared unversioned release workflow, and specify a few things.
First, make sure you set the “uses” property to point to the unversioned workflow within your organization. If you just use the gists as they were, you only need to change the prefix which is your GitHub organization ID.
Then, to make sure the release workflow runs after the actual deployment and only in case it was successful, specify the “needs” keyword and point to the deployment job.
Lastly, keep the “secrets” and “concurrency” properties in there and adjust the “with.name” property according to your liking - it will be displayed in the Slack message.
name: Production Deployment - Unversioned
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
concurrency: main
steps:
- name: Mock deploy to prod
run: echo "Deploying to production..."
post_deploy:
uses: {your-github-organization-id}/internal-workflows/.github/workflows/release.yml@main
needs: deploy
secrets: inherit
concurrency: main
with:
name: Frontend
Versioned
A versioned workflow in itself is simpler because there is no need to discover and bump the version, but it requires an extra step in the release workflow calling it to figure out the version. In the example provided, the version is extracted from a branch name when following the “release/x.x.x” branch naming convention, but you can adjust it to retrieve the version in any other possible way.
The setup is similar to the unversioned one but requires referencing the “release-versioned.yml” shared workflow and passing a “version” argument to it.
name: Production Deployment Versioned
on:
pull_request:
branches:
- main
types: [closed]
jobs:
deploy:
runs-on: ubuntu-latest
if: github.event.pull_request.merged == true
outputs:
tag: ${{ steps.version.outputs.tag }}
steps:
- name: Mock deploy to prod
run: echo "Deploying to production..."
- id: version
env:
BRANCH_NAME: ${{ github.head_ref }}
run: echo "tag=${BRANCH_NAME##*/}" >> "$GITHUB_OUTPUT"
post_deploy:
uses: {your-github-organization-id}/internal-workflows/.github/workflows/release-versioned.yml@main
needs: deploy
secrets: inherit
concurrency: main
with:
name: Frontend
version: ${{ needs.deploy.outputs.tag }}
Test the setup
To test the versioned setup, create a PR from a “release/x.x.x” branch into “main”, and specify your changelog in the PR description. I prefer to use a bulleted list which then looks nice in Slack.
To test the unversioned setup, you can either create a PR into “main”, or even push a commit directly. If you create a PR, then that PR description will be used as a changelog. If you push directly, then the commit message will be used as a changelog.
Enjoy!