Skip to content

Commit

Permalink
Merge pull request #1 from Gamesight/staging
Browse files Browse the repository at this point in the history
Cleanup and Documentation
  • Loading branch information
AnthonyKinson authored May 20, 2020
2 parents 52c6c95 + 253ba40 commit 1b2a091
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 55 deletions.
88 changes: 86 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,86 @@
# slack-workflow-status
Github Action for sending Workflow run results to Slack
# Slack Workflow Status
This action will post workflow status notifications into your Slack channel. The notification includes the name of the Actor, Event, Branch, Workflow Name, Status and Run Durations. This action can optionally include the status and duration of individual jobs in a workflow to quickly help you identify where failures and slowdowns occur.

<img src="./docs/images/example.png" title="Slack Example">

## Action Inputs

### slack_webhook_url
_`required`_

Create a Slack Webhook URL using the [Incoming Webhooks App](https://slack.com/apps/A0F7XDUAZ-incoming-webhooks?next_id=0)
It is recommended that youu create a new secret on your repo `SLACK_WEBHOOK_URL` for holding this value, and passing it to the action with `${{secrets.SLACK_WEBHOOK_URL}}`.

### repo_token
_`required`_

A token is automatically available in your workflow secrets var. `${{secrets.GITHUB_TOKEN}}`. You can optionaly send an alternative self-generated token.

### channel
_`optional`_

Accepts a Slack channel name where you would like the notifications to appear. Overrides the default channel created with your webhook.

### name
_`optional`_

Allows you to provide a name for the slack bot user posting the notifications. Overrides the default name created with your webhook.

### icon_emoji
_`optional`_

Allows you to provide an emoji as the slack bot user image when posting notifications. Overrides the default image created with your webhook. _[Emoji Code Cheat Sheet](https://www.webfx.com/tools/emoji-cheat-sheet/)_

### icon_url
_`optional`_

Allows you to provide a url for an image to use as the slack bot user image when posting notifications. Overrides the default image created with your webhook.

### include_jobs
_`optional`_ _`default: true`_

When set to `true`, individual job status and durations in the slack notification. When `false` only the event status and workflow status lines are included.

## Usage
To use this action properly, you should create a new `job` at the end of your workflow that `needs` all other jobs in the workflow. This ensures that this action is only run once all jobs in your workflow are complete.

```yaml
name: World Greeter
on:
push:
branches: [ master, staging ]
jobs:
job-1:
runs-on: ubuntu-latest
steps:
- name: Say Hello
run: echo "Hello"
job-2:
runs-on: ubuntu-latest
steps:
- name: Say World
run: echo "World"
slack-workflow-status:
if: always()
name: Post Workflow Status To Slack
needs:
- job-1
- job-2
runs-on: ubuntu-latest
steps:
- name: Slack Workflow Notification
uses: Gamesight/slack-workflow-status@master
with:
# Required Input
repo_token: ${{secrets.GITHUB_TOKEN}}
slack_webhook_url: ${{secrets.SLACK_WEBHOOK_URL}}
# Optional Input
channel: '#anthony-test-channel'
name: 'Anthony Workflow Bot'
icon_emoji: ':poop:'
icon_url: 'https://avatars0.githubusercontent.com/u/1701160?s=96&v=4'
```
This action can also be used for Pull Request workflows and will include pull request information in the notification.
<img src="./docs/images/example-pr.png" title="Slack Pull Request Example">
6 changes: 6 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ inputs:
name:
description: 'Override the default name configured by the Slack SLACK_WEBHOOK_URL'
required: false
icon_emoji:
description: 'Override the default webhook icon with an emoji. Uses emoji code: https://www.webfx.com/tools/emoji-cheat-sheet/'
required: false
icon_url:
description: 'Override the default webhook icon with an image via url.'
required: false
include_jobs:
descrtiption: 'Should the slack notification include individual job status and run times'
required: true
Expand Down
88 changes: 62 additions & 26 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3296,26 +3296,44 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _actions_github__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_actions_github__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var request_promise_native__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(117);
/* harmony import */ var request_promise_native__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(request_promise_native__WEBPACK_IMPORTED_MODULE_2__);
/******************************************************************************\
* Main entrypoint for GitHib Action. Fetches information regarding the *
* currently running Workflow and it's Jobs. Sends individual job status and *
* workflow status as a formatted notification to the Slack Webhhok URL set *
* in the environment variables. *
* *
* Org: Gamesight <https://gamesight.io> *
* Author: Anthony Kinson <[email protected]> *
* Repository: https://github.com/Gamesight/slack-workflow-status *
* License: MIT *
* Copyright (c) 2020 Gamesight, Inc *
\******************************************************************************/



process.on('unhandledRejection', handleError);
main().catch(handleError);
// Action entrypoint
async function main() {
// Collect action inputs
// Collect Action Inputs
const webhook_url = _actions_core__WEBPACK_IMPORTED_MODULE_0__.getInput('slack_webhook_url', { required: true });
const github_token = _actions_core__WEBPACK_IMPORTED_MODULE_0__.getInput('repo_token', { required: true });
const include_jobs = _actions_core__WEBPACK_IMPORTED_MODULE_0__.getInput('include_jobs', { required: true });
const slack_channel = _actions_core__WEBPACK_IMPORTED_MODULE_0__.getInput('channel');
const slack_name = _actions_core__WEBPACK_IMPORTED_MODULE_0__.getInput('name');
const slack_icon = _actions_core__WEBPACK_IMPORTED_MODULE_0__.getInput('icon_url');
const slack_emoji = _actions_core__WEBPACK_IMPORTED_MODULE_0__.getInput('icon_emoji'); // https://www.webfx.com/tools/emoji-cheat-sheet/
// Force as secret, forces *** when trying to print or log values
_actions_core__WEBPACK_IMPORTED_MODULE_0__.setSecret(github_token);
_actions_core__WEBPACK_IMPORTED_MODULE_0__.setSecret(webhook_url);
const run_id = Number(process.env.GITHUB_RUN_ID);
// Collect Environment Variables
const workflow_name = process.env.GITHUB_WORKFLOW;
const include_jobs = _actions_core__WEBPACK_IMPORTED_MODULE_0__.getInput('include_jobs', { required: true });
const run_id = Number(process.env.GITHUB_RUN_ID);
const actor = process.env.GITHUB_ACTOR;
const event = process.env.GITHUB_EVENT_NAME;
const ref = process.env.GITHUB_REF;
const branch = ref.substr(ref.lastIndexOf('/') + 1);
const slack_channel = _actions_core__WEBPACK_IMPORTED_MODULE_0__.getInput('channel');
const slack_name = _actions_core__WEBPACK_IMPORTED_MODULE_0__.getInput('name');
// Auth github with octokit module
const options = {};
const github = new _actions_github__WEBPACK_IMPORTED_MODULE_1__.GitHub(github_token, options);
// Fetch workflow run data
Expand All @@ -3324,16 +3342,9 @@ async function main() {
repo: _actions_github__WEBPACK_IMPORTED_MODULE_1__.context.repo.repo,
run_id: run_id
});
// Fetch workflow job information
const jobs_response = await github.request(workflow_run.data.jobs_url);
// Setup PR String
let pull_requests = "";
for (let pull_request of workflow_run.data.pull_requests) {
pull_requests += ",<" + pull_request.url + "|#" + pull_request.number + ">";
}
if (pull_requests != "") {
pull_requests = "for " + pull_requests.substr(1) + " ";
}
// Build Slack Payload
// Build Job Data Fields and Workflow Status
let job_fields = [];
let workflow_success = true;
let workflow_failure = false;
Expand Down Expand Up @@ -3365,10 +3376,10 @@ async function main() {
});
}
// Configure slack attachment styling
let workflow_color = "";
let workflow_color = ""; // can be good, danger, warning or a HEX colour (#00FF00)
let workflow_msg = "";
if (workflow_success) {
workflow_color = "good"; // can be replaced with HEX values
workflow_color = "good";
workflow_msg = "Success:";
}
else if (workflow_failure) {
Expand All @@ -3377,36 +3388,61 @@ async function main() {
}
else {
workflow_color = "warning";
workflow_msg = "Cancelled: ";
workflow_msg = "Cancelled:";
}
// Payload Formatting Shortcuts
const workflow_duration = job_duration(new Date(workflow_run.data.created_at), new Date(workflow_run.data.updated_at));
const repo_url = "<https://github.com/" + workflow_run.data.repository.full_name + "|*" + workflow_run.data.repository.full_name + "*>";
const branch_url = "<https://github.com/" + workflow_run.data.repository.full_name + "/tree/" + branch + "|*" + branch + "*>";
const workflow_run_url = "<" + workflow_run.data.html_url + "|#" + workflow_run.data.run_number + ">";
// Example: Success: AnthonyKinson's `push` on `master` for pull_request
let status_string = workflow_msg + " " + actor + "'s `" + event + "` on `" + branch_url + "`\n";
// Example: Workflow: My Workflow #14 completed in `1m 30s`
const details_string = "Workflow: " + workflow_name + " " + workflow_run_url + " completed in `" + workflow_duration + "`";
// Build Pull Request string if required
let pull_requests = "";
for (let pull_request of workflow_run.data.pull_requests) {
pull_requests += ", <" + pull_request.url + "|#" + pull_request.number + "> from `" + pull_request.head.ref + "` to `" + pull_request.base.ref + "`";
}
// create our slack payload
// We're using old style attachments rather than the new blocks because we don't
// get the notification color highlighting with blocks.
if (pull_requests != "") {
pull_requests = pull_requests.substr(1);
status_string = workflow_msg + " " + actor + "'s `pull_request`" + pull_requests + "\n";
}
// We're using old style attachments rather than the new blocks because:
// - Blocks don't allow colour indicators on messages
// - Block are limited to 10 fields. >10 jobs in a workflow results in payload failure
// Build our notification attachment
const slack_attachment = {
mrkdwn_in: ["text"],
color: workflow_color,
text: workflow_msg + " " + actor + "'s " + event + " on " + branch + " " + pull_requests + "\nWorkflow: " + workflow_name + " <" + workflow_run.data.html_url + "|#" + workflow_run.data.run_number + "> completed in " + job_duration(new Date(workflow_run.data.created_at), new Date(workflow_run.data.updated_at)),
footer: "<https://github.com/" + workflow_run.data.repository.full_name + "|*" + workflow_run.data.repository.full_name + "*>",
text: status_string + details_string,
footer: repo_url,
footer_icon: "https://github.githubassets.com/favicon.ico",
fields: (include_jobs == 'true') ? job_fields : []
};
// Build our notification payload
const slack_payload_body = {
attachments: [slack_attachment]
};
// Add some overrides
// Do we have any overrides?
if (slack_name != "") {
slack_payload_body.username = slack_name;
}
if (slack_channel != "") {
slack_payload_body.channel = slack_channel;
}
if (slack_emoji != "") {
slack_payload_body.icon_emoji = slack_emoji;
}
if (slack_icon != "") {
slack_payload_body.icon_url = slack_icon;
}
const request_options = {
uri: webhook_url,
method: 'POST',
body: slack_payload_body,
json: true
};
// await request(request_options)
request_promise_native__WEBPACK_IMPORTED_MODULE_2__(request_options).catch(err => {
_actions_core__WEBPACK_IMPORTED_MODULE_0__.setFailed(err);
});
Expand All @@ -3424,9 +3460,9 @@ const job_duration = function (start, end) {
let seconds = Math.floor(delta % 60);
// Format duration sections
const format_duration = function (value, text, hide_on_zero) {
return (value <= 0 && hide_on_zero) ? "" : value + text;
return (value <= 0 && hide_on_zero) ? "" : value + text + " ";
};
return format_duration(days, "d", true) + format_duration(hours, "h", true) + format_duration(minutes, "m", true) + format_duration(seconds, "s", false);
return format_duration(days, "d", true) + format_duration(hours, "h", true) + format_duration(minutes, "m", true) + format_duration(seconds, "s", false).trim();
};
function handleError(err) {
console.error(err);
Expand Down
Binary file added docs/images/example-pr.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 1b2a091

Please sign in to comment.