Skip to content

Commit

Permalink
Export template processes into module template.js (#5)
Browse files Browse the repository at this point in the history
- ref: notify.js has been simplified
- ref: template.js contains all template-text processing
- fix: README.md rollback
  • Loading branch information
rainui28 committed Dec 16, 2022
1 parent 4e19f8f commit e768aae
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 28 deletions.
158 changes: 142 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,159 @@
# :zap: Notify Slack Action: How-to create a template
# :zap: Notify Slack Action

**Last update: 2022-12-02**
_Forked from [abinoda/slack-action](https://github.com/abinoda/slack-action)_

## 1. Fork this repository
:nail_care: Send **fancy** message to a Slack channel, group or DM using [Slack bot tokens](https://api.slack.com/docs/token-types).

After forking this repository, create a new branch from `templates` branch that follows this naming convention `templates/<template-name>`
**Note**: To use this GitHub Action you'll first need to create a Slack App and install it to your Slack workspace.

```shell
git checkout -b templates/my_template templates
**:rocket: Features**:

- Send **simple text message** to a slack channel
- Send **[blocks message](https://api.slack.com/block-kit)** to a slack channel
- Send **message based from templates** available [here](https://github.com/evryfs/notify-slack-action/tree/templates)

## :information_source: Parameters

**Parameters described below are matching the latest version tag.**
You can find available action's tags [here](https://github.com/evryfs/notify-slack-action/tags)

| **Parameter** | **Required** | **Type** | **Description** |
| --------------- | ------------ | -------- | ------------------------------------------------------------------------------------ |
| SLACK_BOT_TOKEN | x | secret | Slack bot token auth |
| channel-id | x | string | Channel where message will be posted |
| value | x | string | Value depending on the mechanism chosen. Details below |
| mechanism | x | string | Mechanism to use when notify a slack channel. Either 'text', 'blocks' or 'templates' |

## :octocat: Usage

```yaml
- name: Notify Slack
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} # required
uses: evryfs/notify-slack-action@<tag>
with:
channel-id: <slack-channel-id> # required
value: <text-to-post> # required
mechanism: <mechanism> # optional. Defaults to 'text'
```
## 2. Create your JSON template file
### Using `text` mechanism

```yaml
- name: Notify Slack using text
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} # required
uses: evryfs/notify-slack-action@latest
with:
channel-id: C04DFAHCTS5
mechanism: text
value: Hello GitHub Action!
```

Slack message would looks like:

<img src="docs/images/bot_mechanism_text.png" width="540">

### Using `templates` mechanism

A template **must be** a JSON file.
Templates mechanism are pre-defined blocks messages that contains (valuable) generics information. They are defined in a **orphan branch** named [`templates`](https://github.com/evryfs/notify-slack-action/tree/templates). Templates can be added by creating a pull request to this branch. More information [here](https://github.com/evryfs/notify-slack-action/blob/templates/README.md).

#### Override template variable

Templates are using environments variables to generate information, that are called **templates variables**.

For instance, [gh_dashboard_simple.json](https://github.com/evryfs/notify-slack-action/blob/templates/templates/gh_dashboard_simple.json) contains template variable `$VERSION_TAG`, `$ACTION_STATUS` or even `$GITHUB_REF_NAME`. Those variables are intended to be override either by the system (ie. github runner envs) or by the developer using `env:` at the workflow/job/step level.

```yaml
- name: Notify Slack using templates
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} # required
VERSION_TAG: ${{ inputs.version-tag }} # NOTE github context can be used to retrieve inputs/outputs/context
ACTION_STATUS: "success :thumbsup:" # NOTE Slack 'mrkdwn' format is supported
TEXT_MESSAGE: "Build of notify-slack-action has been done successfully!" # Override the template variable $VERSION_TAG
uses: evryfs/notify-slack-action@latest
with:
channel-id: C04DFAHCTS5
mechanism: templates
value: gh_dashboard_simple.json # Load template gh_dhasboard_simple.json
```
my_template.json

By loading the [gh_dashboard_simple.json](https://github.com/evryfs/notify-slack-action/blob/templates/templates/gh_dashboard_simple.json) template, Slack message would looks like:

<img src="docs/images/bot_mechanism_templates.png" width="540">

**Note:** templates variables depend of the selected template. A template might not have any template variable. You must take a look at the template's guideline (or source code).

### Using `blocks` mechanism

It is possible to personnalise a message by using Slack Block Kit without submitting a template to `evryfs/notify-slack-action`. You can use [Slack Block Kit Builder](https://api.slack.com/block-kit/building) to easily construct a message layout and generate its JSON code.

**Value must be an JSON array!**

```yaml
- name: Notify Slack using blocks
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} # required
VERSION_TAG: ${{ inputs.version-tag }} # NOTE github context can be used to retrieve inputs/outputs/context
ACTION_STATUS: "success :thumbsup:" # NOTE Slack 'mrkdwn' format is supported
TEXT_MESSAGE: "Build of notify-slack-action has been done successfully!" # Override the template variable $VERSION_TAG
uses: evryfs/notify-slack-action@latest
with:
channel-id: C04DFAHCTS5
mechanism: blocks
value: | # Make sure to use | in order to parse JSON text
[
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Anything humans can do in space, robots can do better.*\n${{ github.run_id }}"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"emoji": true,
"text": "Approve"
},
"style": "primary",
"value": "yes"
},
{
"type": "button",
"text": {
"type": "plain_text",
"emoji": true,
"text": "Deny"
},
"style": "danger",
"value": "no"
}
]
}
]
```

Indeed, the template contains the payload expected by the Slack API when sending a postMessage. In addition, a template must contains a `blocks` payload that complies with [Slack Block Kit](https://api.slack.com/block-kit).
Slack message would looks like:

<img src="docs/images/bot_mechanism_blocks.png" width="540">

You can also take a look at [gh_dashboard_simple.json](./templates/gh_dashboard_simple.json) as a model for your template.
## :book: References

## 3. Document your template in [docs/](./docs/)
- [Templates mechanism guideline](https://github.com/evryfs/notify-slack-action/blob/templates/README.md)
- [Slack's documentation](https://api.slack.com/docs/messages)
- [Slack's API Bot](https://api.slack.com/authentication/basics)
- [Slack chat.postMessage](https://api.slack.com/methods/chat.postMessage)

You can copy/paste [](./GUIDELINE_EXAMPLE.md) into [docs/](./docs/) and rename it with your template filename. Update the information as describe in the guideline example.
**Note**: A "channel ID" can be the ID of a channel, private group, or user you would like to post a message to. Your bot can message any user in your Slack workspace but needs to be invited into channels and private groups before it can post to them.

You can also take a look at [gh_dashboard_simple.md](./docs/gh_dashboard_simple.md) as a model for your guideline.
- **Slack App**: Right click on channel > View channel details > Channel ID (at the bottom)
- **Slack Webapp**: channel IDs at the end of the URL when viewing channels and private groups. Note that this doesn't work for direct messages. `https://myworkspace.slack.com/messages/CHANNEL_ID/`

## 4. Submit your template with a Pull Request
## :memo: License

Create a pull request from your repository to `evryfs/notify-slack-action`. The code owners will take a look at your template and approve it if everything is fine.
The Dockerfile and associated scripts and documentation in this project are released under the [MIT License](LICENSE).
20 changes: 8 additions & 12 deletions src/notify.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
const core = require("@actions/core");
const needle = require("needle");

const getTemplateEnvs = (str) => str.match(/\$\{?([A-Z0-9_]+)\}?/gm);

const trimTemplateEnvs = (envs) => envs.map((env) => env.replace(/\$\{?([A-Z0-9_]+)\}?/, "$1"));
const templateParser = require("./template");

const call_slack_api = (payload) => {
const options = {
Expand Down Expand Up @@ -39,14 +36,13 @@ const processTemplatesMechanism = (channelId, value) => {
core.setFailed(templateMsgStatus);
} else {
core.info(templateMsgStatus);
let template = JSON.parse(response.body);
let blocks = JSON.stringify(template.blocks);
let templateEnvs = getTemplateEnvs(blocks);
let envs = trimTemplateEnvs(templateEnvs);
templateEnvs.forEach((tEnv, i) => {
blocks = blocks.replaceAll(tEnv, process.env[envs[i]]);
});
call_slack_api({ channel: channelId, blocks: blocks });
let slackPayload = { channel: channelId };
let [templateType, templateContent] = Object.entries(JSON.parse(response.body))[0];
if (!templateParser.isValidTemplateTypes(templateType)) {
core.setFailed(`Template of type '${templateType}' is not supported`);
}
slackPayload[templateType] = templateParser.parse(templateContent);
call_slack_api(slackPayload);
}
}
});
Expand Down
53 changes: 53 additions & 0 deletions src/template.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const VALID_TEMPLATE_TYPES = ["blocks", "attachments"];

/**
* Get environments name within the string
*
* @param {String} str
* @returns {Array} Array of environments name within the given string
*/
const getTemplateEnvs = (str) => str.match(/\$\{?([A-Z0-9_]+)\}?/gm);

/**
* Trim the linux environment prefix/suffix ${} from an array of environments
*
* @param {Array} envs
* @returns {Array} Array of environments name without $ or ${} tag
*/
const trimTemplateEnvs = (envs) => envs.map((env) => env.replace(/\$\{?([A-Z0-9_]+)\}?/, "$1"));

/**
* Parse environments variables (envs) within template object
*
* @param {Object} template
*
* @returns {String} parsed template
*/
const parseTemplateEnvs = (template) => {
let stringTemplate = JSON.stringify(template);
let templateEnvs = getTemplateEnvs(stringTemplate);
let envs = trimTemplateEnvs(templateEnvs);
templateEnvs.forEach((tEnv, i) => {
stringTemplate = stringTemplate.replaceAll(tEnv, process.env[envs[i]]);
});
return stringTemplate;
};

/**
* Verify if a template is supported
*
* @param {String} templateType
* @returns {Boolean}
*/
module.exports.isValidTemplateTypes = (templateType) => VALID_TEMPLATE_TYPES.includes(templateType);

/**
* Parse template by applying all the parsing rules defined
*
* @param {Object} template
*
* @returns {String} parsed template
*/
module.exports.parse = (template) => {
return parseTemplateEnvs(template);
};

0 comments on commit e768aae

Please sign in to comment.