Skip to content

Commit

Permalink
feat: Add possibility to provide onSuccess function (#53)
Browse files Browse the repository at this point in the history
  • Loading branch information
BeyondEvil authored Nov 29, 2020
1 parent be18847 commit 1cb7fab
Show file tree
Hide file tree
Showing 10 changed files with 1,426 additions and 448 deletions.
106 changes: 90 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Add the plugin to your npm-project:
$ npm install semantic-release-slack-bot -D
```

The corresponding slack app has be installed in you slack workspace as well. Follow the instructions under [configuration](#configuration) for more information.
The corresponding slack app has to be installed in your slack workspace as well. Follow the instructions under [configuration](#configuration) for more information.

## Usage

Expand All @@ -45,7 +45,7 @@ The plugin can be configured in the [**semantic-release** configuration file](ht

With this example:

- Slack notifications are skipped on a succesfull release
- Slack notifications are skipped on a successful release
- Slack notifications are sent on a failed release

## Screenshots
Expand All @@ -61,15 +61,15 @@ The plugin uses a slack webhook which you get by adding the slack app to your sl

<a href="https://slack.com/oauth/authorize?client_id=605439709265.611687593109&scope=incoming-webhook"><img alt="Add to Slack" height="40" width="139" src="https://platform.slack-edge.com/img/add_to_slack.png" srcset="https://platform.slack-edge.com/img/add_to_slack.png 1x, https://platform.slack-edge.com/img/[email protected] 2x"></a>

For the security concerned, feel free to [create your own slack app](https://api.slack.com/apps) and create a web-hook or inspect the server code that does this creation for you at [create-webhook.js](lambda/create-webhook.js). The only required permission for the webhook is to publish to a single channel.
For the security concerned, feel free to [create your own slack app](https://api.slack.com/apps) and create a webhook or inspect the server code that does this creation for you at [create-webhook.js](lambda/create-webhook.js). The only required permission for the webhook is to publish to a single channel.

### Slack app authentication

Installing the app will yield you with a webhook that the app uses to publish updates to your selected chanel. The Slack webhook authentication link is **required and needs to be kept a secret**. It should be defined in the [environment variables](#environment-variables).
Installing the app will yield you with a webhook that the app uses to publish updates to your selected channel. The Slack webhook authentication link is **required and needs to be kept a secret**. It should be defined in the [environment variables](#environment-variables).

### Environment variables

The `SLACK_WEBHOOK` variable can be defined in the environment where you will be running semantic release. This can be done by exporting it in bash or in the user interface of your CI provider. Obtain this token by installing the slack app according to [slack app installation](#slack-app-installation).
The `SLACK_WEBHOOK` variable can be defined in the environment where you will run semantic release. This can be done by exporting it in bash or in the user interface of your CI provider. Obtain this token by installing the slack app according to [slack app installation](#slack-app-installation).

Alternatively, you could pass the webhook as a configuration option.

Expand All @@ -80,17 +80,81 @@ Alternatively, you could pass the webhook as a configuration option.

### Options

| Option | Description | Default |
| :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------- |
| `notifyOnSuccess` | Determines if a succesfull release should trigger a slack message to be sent. If `false` this plugin does nothing on success. | false |
| `notifyOnFail` | Determines if a failed release should trigger a slack message to be sent. If `false` this plugin does nothing on fail. | false |
| `onSuccessTemplate` | Provides a template for the slack message object on success when `notifyOnSuccess` is `true`. See [templating](#templating). | undefined |
| `onFailTemplate` | Provides a template for the slack message object on fail when `notifyOnFail` is `true`. See [templating](#templating). | undefined |
| `markdownReleaseNotes` | Pass release notes through markdown to slack formatter before rendering. | false |
| `slackWebhookEnVar` | This decides what the environment variable for exporting the slack webhook is called. | SLACK_WEBHOOK |
| `slackWebhook` | Slack webhook created when adding app to workspace. | value of the environment variable matching `slackWebhookEnVar` |
| `packageName` | Override or add package name instead of npm package name | SEMANTIC_RELEASE_PACKAGE or npm package name |
| `unsafeMaxLength` | Maximum character length for the release notes before truncation. If maxLength is too high, messages can be dropped. [Read here](https://github.com/juliuscc/semantic-release-slack-bot/issues/26#issuecomment-569804359) for more information. Set to '0' to turn off truncation entirely. | 2900 |
| Option | Description | Default |
| :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------- |
| `notifyOnSuccess` | Determines if a successful release should trigger a slack message to be sent. If `false` this plugin does nothing on success. | false |
| `notifyOnFail` | Determines if a failed release should trigger a slack message to be sent. If `false` this plugin does nothing on fail. | false |
| `onSuccessFunction` | Provides a function for the slack message object on success when `notifyOnSuccess` is `true`. See [function](#function). | undefined |
| `onFailFunction` | Provides a function for the slack message object on fail when `notifyOnFail` is `true`. See [function](#function). | undefined |
| `onSuccessTemplate` | Provides a template for the slack message object on success when `notifyOnSuccess` is `true`. See [templating](#templating). | undefined |
| `onFailTemplate` | Provides a template for the slack message object on fail when `notifyOnFail` is `true`. See [templating](#templating). | undefined |
| `markdownReleaseNotes` | Pass release notes through markdown to slack formatter before rendering. | false |
| `slackWebhookEnVar` | This decides what the environment variable for exporting the slack webhook is called. | SLACK_WEBHOOK |
| `slackWebhook` | Slack webhook created when adding app to workspace. | value of the environment variable matching `slackWebhookEnVar` |
| `packageName` | Override or add package name instead of npm package name | SEMANTIC_RELEASE_PACKAGE or npm package name |
| `unsafeMaxLength` | Maximum character length for the release notes before truncation. If unsafeMaxLength is too high, messages can be dropped. [Read here](https://github.com/juliuscc/semantic-release-slack-bot/issues/26#issuecomment-569804359) for more information. Set to '0' to turn off truncation entirely. | 2900 |

### Function

If a function is provided with either the `onSuccessFunction` or `onFailFunction` options, it will be used for the respective slack message. The function should return an object that follows the [Slack API message structure](https://api.slack.com/docs/message-formatting). The function is passed two objects, `pluginConfig` and `context`, the same objects that are passed to [plugins](https://github.com/semantic-release/semantic-release/blob/master/docs/developer-guide/plugin.md#plugin-developer-guide).

**Note**: This only works with a [configuration file](https://semantic-release.gitbook.io/semantic-release/usage/configuration#configuration-file) that exports an object (see below for an example).

<details>

<summary> Example config (some parts omitted) </summary>

```js
const slackifyMarkdown = require('slackify-markdown')
const { chunkifyString } = require('semantic-release-slack-bot/lib/chunkifier')

const onSuccessFunction = (pluginConfig, context) => {
const releaseNotes = slackifyMarkdown(context.nextRelease.notes)
const text = `Updates to ${
pluginConfig.packageName
} has been released to *Stage!*`
const headerBlock = {
type: 'section',
text: {
type: 'mrkdwn',
text
}
}

return {
text,
blocks: [
headerBlock,
...chunkifyString(releaseNotes, 2900).map(chunk => {
return {
type: 'section',
text: {
type: 'mrkdwn',
text: chunk
}
}
})
]
}
}

module.exports = {
branches: ['master'],
preset: 'eslint',
plugins: [
[
'semantic-release-slack-bot',
{
notifyOnSuccess: true,
onSuccessFunction,
packageName: 'Testing Semantic Release'
}
]
]
}
```

</details>

### Templating

Expand All @@ -111,3 +175,13 @@ A sample configuration with template can look like this
"text": "A new version of $package_name with version $npm_package_version has been released at $repo_url!"
}
```

### Helper functions

There are two helper functions exported by the [`chunkifier`](lib/chunkifier.js) module.

`chunkifyArray` takes an array of strings and returns a new array based on `maxLength` and `delimiter`. `delimiter` is optional and defaults to newline.

`chunkifyString` takes a string and returns an array based on `maxLength` and `delimiter`. `delimiter` is optional and defaults to newline.

See respective implementation for more details.
43 changes: 43 additions & 0 deletions lib/chunkifier.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const chunkifyArray = (messageLines, maxLength, delimiter) => {
delimiter = delimiter || '\n'

let message = ''
const resultLines = []
messageLines.forEach(line => {
// the next chunk is made up of the (next) line and the delimiter
let nextChunk = line + delimiter
// if the message plus the next chunk puts us over the maxLength limit...
if ((message + nextChunk).length > maxLength) {
// ...we add the message to the result array...
resultLines.push(message.trimEnd())
// ...and "reset" the message with the next chunk.
message = nextChunk
} else {
// if not, we add the next chunk to the message
message += nextChunk
}
})

// handle the case where we have a trailing message
if (message.length > 0) {
resultLines.push(message.trimEnd())
}

return resultLines
}

const chunkifyString = (messageText, maxLength, delimiter) => {
if (messageText.length <= maxLength) {
return [messageText]
}

delimiter = delimiter || '\n'
const messageLines = messageText.split(delimiter)

return chunkifyArray(messageLines, maxLength, delimiter)
}

module.exports = {
chunkifyArray,
chunkifyString
}
6 changes: 4 additions & 2 deletions lib/fail.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ module.exports = async (pluginConfig, context) => {
: undefined
const repoURL = repoPath && `https://github.com/${repoPath}`

// Override default fail template
if (pluginConfig.onFailTemplate) {
// Override default fail message
if (pluginConfig.onFailFunction) {
slackMessage = pluginConfig.onFailFunction(pluginConfig, context)
} else if (pluginConfig.onFailTemplate) {
slackMessage = template(pluginConfig.onFailTemplate, {
package_name,
repo_path: repoPath,
Expand Down
4 changes: 3 additions & 1 deletion lib/success.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ module.exports = async (pluginConfig, context) => {

let slackMessage = {}
// Override default success message
if (pluginConfig.onSuccessTemplate) {
if (pluginConfig.onSuccessFunction) {
slackMessage = pluginConfig.onSuccessFunction(pluginConfig, context)
} else if (pluginConfig.onSuccessTemplate) {
slackMessage = template(pluginConfig.onSuccessTemplate, {
package_name,
npm_package_version: nextRelease.version,
Expand Down
Loading

0 comments on commit 1cb7fab

Please sign in to comment.