Skip to content

Commit

Permalink
Merge pull request #268 from sue445/add_matrix_input
Browse files Browse the repository at this point in the history
feat: Add matrix input
  • Loading branch information
satterly committed Sep 20, 2023
2 parents d2cd23b + ddddfa0 commit 160cf66
Show file tree
Hide file tree
Showing 17 changed files with 155 additions and 22 deletions.
3 changes: 3 additions & 0 deletions .github/slack.yml
Expand Up @@ -22,6 +22,9 @@ fields:
- title: Job Steps
value: "{{#each jobSteps}}{{icon this.outcome}} {{@key}}\n{{/each}}"
short: false
- title: Job Matrix
value: "{{#each jobMatrix}}{{@key}}: {{this}}\n{{/each}}"
short: false
- title: Workflow
value: "<{{{workflowUrl}}}|{{workflow}}>"
short: true
Expand Down
14 changes: 13 additions & 1 deletion README.md
Expand Up @@ -60,6 +60,15 @@ message using:

**Note: Only steps that have a "step id" will be reported on. See example below.**

#### `matrix` (optional)
Parameters for [matrix jobs](https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs) can be included in Slack messages:

with:
status: ${{ job.status }}
matrix: ${{ toJson(matrix) }}

<img src="./docs/images/example4.png" width="505" title="Slack Example #4">

#### `channel` (optional)

To override the channel or to send the Slack message to an individual
Expand Down Expand Up @@ -116,7 +125,7 @@ The following Slack [message fields](https://api.slack.com/reference/messaging/a

**Supported Template variables**

`env.*`, `payload.*`, `jobName`, `jobStatus`, `jobSteps`,
`env.*`, `payload.*`, `jobName`, `jobStatus`, `jobSteps`, `jobMatrix`,
`eventName`, `workflow`, `workflowUrl`, `workflowRunUrl`, `repositoryName`, `repositoryUrl`, `runId`, `runNumber`, `sha`, `shortSha`, `branch`, `actor`, `action`, `ref`, `refType`, `refUrl`, `diffRef`, `diffUrl`, `description`, `sender`

**Helper Functions**
Expand Down Expand Up @@ -166,6 +175,9 @@ fields:
- title: Job Steps
value: "{{#each jobSteps}}{{icon this.outcome}} {{@key}}\n{{/each}}"
short: false
- title: Job Matrix
value: "{{#each jobMatrix}}{{@key}}: {{this}}\n{{/each}}"
short: false
- title: Workflow
value: "<{{workflowUrl}}|{{workflow}}>"
short: true
Expand Down
3 changes: 2 additions & 1 deletion __tests__/blocks.test.ts
Expand Up @@ -40,6 +40,7 @@ const jobSteps = {
conclusion: 'failure'
}
}
const jobMatrix = {}
const channel = '#github-ci'

// mock github context
Expand Down Expand Up @@ -92,7 +93,7 @@ test('custom config of slack action using legacy and blocks', async () => {
schema: yaml.FAILSAFE_SCHEMA
}) as ConfigOptions

let res = await send(url, jobName, jobStatus, jobSteps, channel, message, config)
let res = await send(url, jobName, jobStatus, jobSteps, jobMatrix, channel, message, config)
await expect(res).toStrictEqual({text: {status: 'ok'}})

expect(JSON.parse(mockAxios.history.post[0].data)).toStrictEqual({
Expand Down
3 changes: 2 additions & 1 deletion __tests__/config.test.ts
Expand Up @@ -40,6 +40,7 @@ const jobSteps = {
conclusion: 'failure'
}
}
const jobMatrix = {}
const channel = '#github-ci'

// mock github context
Expand Down Expand Up @@ -92,7 +93,7 @@ test('custom config of slack action using legacy attachments', async () => {
schema: yaml.FAILSAFE_SCHEMA
}) as ConfigOptions

let res = await send(url, jobName, jobStatus, jobSteps, channel, message, config)
let res = await send(url, jobName, jobStatus, jobSteps, jobMatrix, channel, message, config)
await expect(res).toStrictEqual({text: {status: 'ok'}})

expect(JSON.parse(mockAxios.history.post[0].data)).toStrictEqual({
Expand Down
3 changes: 2 additions & 1 deletion __tests__/inputs.test.ts
Expand Up @@ -40,6 +40,7 @@ const jobSteps = {
conclusion: 'cancelled'
}
}
const jobMatrix = {}
const channel = '#deploy'
let message = 'Successfully deployed to {{ env.ENVIRONMENT }}!'

Expand Down Expand Up @@ -92,7 +93,7 @@ test('custom config of slack action using inputs for channel and message', async
schema: yaml.FAILSAFE_SCHEMA
}) as ConfigOptions

let res = await send(url, jobName, jobStatus, jobSteps, channel, message, config)
let res = await send(url, jobName, jobStatus, jobSteps, jobMatrix, channel, message, config)
await expect(res).toStrictEqual({text: {status: 'ok'}})

expect(JSON.parse(mockAxios.history.post[0].data)).toStrictEqual({
Expand Down
94 changes: 94 additions & 0 deletions __tests__/job_matrix.test.ts
@@ -0,0 +1,94 @@
import * as github from '@actions/github'
import axios from 'axios'
import MockAdapter from 'axios-mock-adapter'
import {send} from '../src/slack'
import {readFileSync} from 'fs'

const url = 'https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX'
const jobName = 'Build and Test'
const jobStatus = 'Success'
const jobSteps = {}
const jobMatrix = {
name1: 'value1',
name2: 'value2'
}
const channel = '@override'
const message = undefined

// mock github context
const dump = JSON.parse(readFileSync('./__tests__/fixtures/push.json', 'utf-8'))

github.context.payload = dump.event
github.context.eventName = dump.event_name
github.context.sha = dump.sha
github.context.ref = dump.ref
github.context.workflow = dump.workflow
github.context.action = dump.action
github.context.actor = dump.actor

process.env.CI = 'true'
process.env.GITHUB_WORKFLOW = 'build-test'
process.env.GITHUB_RUN_ID = '100143423'
process.env.GITHUB_RUN_NUMBER = '8'
process.env.GITHUB_ACTION = 'self2'
process.env.GITHUB_ACTIONS = 'true'
process.env.GITHUB_ACTOR = 'satterly'
process.env.GITHUB_REPOSITORY = 'act10ns/slack'
process.env.GITHUB_EVENT_NAME = 'push'
process.env.GITHUB_EVENT_PATH = '/home/runner/work/_temp/_github_workflow/event.json'
process.env.GITHUB_WORKSPACE = '/home/runner/work/slack/slack'
process.env.GITHUB_SHA = '68d48876e0794fba714cb331a1624af6b20942d8'
process.env.GITHUB_REF = 'refs/heads/master'
process.env.GITHUB_HEAD_REF = ''
process.env.GITHUB_BASE_REF = ''
process.env.GITHUB_SERVER_URL = 'https://github.com'
process.env.GITHUB_API_URL = 'https://github.com'
process.env.GITHUB_GRAPHQL_URL = 'https://api.github.com/graphql'

test('push event to slack', async () => {
const mockAxios = new MockAdapter(axios, {delayResponse: 200})

mockAxios
.onPost()
.reply(config => {
console.log(config.data)
return [200, {status: 'ok'}]
})
.onAny()
.reply(500)

const res = await send(url, jobName, jobStatus, jobSteps, jobMatrix, channel, message)
await expect(res).toStrictEqual({text: {status: 'ok'}})

expect(JSON.parse(mockAxios.history.post[0].data)).toStrictEqual({
username: 'GitHub Actions',
icon_url: 'https://octodex.github.com/images/original.png',
channel: '@override',
attachments: [
{
fallback: '[GitHub]: [act10ns/slack] build-test push Success',
color: 'good',
author_name: 'satterly',
author_link: 'https://github.com/satterly',
author_icon: 'https://avatars0.githubusercontent.com/u/615057?v=4',
mrkdwn_in: ['pretext', 'text', 'fields'],
pretext: '',
text: '*<https://github.com/act10ns/slack/actions?query=workflow:build-test|Workflow _build-test_ job _Build and Test_ triggered by _push_ is _Success_>* for <https://github.com/act10ns/slack/commits/master|`master`>\n<https://github.com/act10ns/slack/compare/db9fe60430a6...68d48876e079|`68d48876`> - 4 commits',
title: '',
fields: [
{
title: 'Job Matrix',
value: 'name1: value1\nname2: value2\n',
short: false
}
],
footer: '<https://github.com/act10ns/slack|act10ns/slack> #8',
footer_icon: 'https://github.githubassets.com/favicon.ico',
ts: expect.stringMatching(/[0-9]+/)
}
]
})

mockAxios.resetHistory()
mockAxios.reset()
})
3 changes: 2 additions & 1 deletion __tests__/job_status.test.ts
Expand Up @@ -39,6 +39,7 @@ const jobSteps = {
conclusion: 'skipped'
}
}
const jobMatrix = {}
const channel = '#github-ci'
const message = undefined

Expand Down Expand Up @@ -86,7 +87,7 @@ test('push event to slack', async () => {

const config: ConfigOptions = {}

const res = await send(url, jobName, jobStatus, jobSteps, channel, message, config)
const res = await send(url, jobName, jobStatus, jobSteps, jobMatrix, channel, message, config)
await expect(res).toStrictEqual({text: {status: 'ok'}})

expect(JSON.parse(mockAxios.history.post[0].data)).toStrictEqual({
Expand Down
3 changes: 2 additions & 1 deletion __tests__/pull_request.test.ts
Expand Up @@ -8,6 +8,7 @@ const url = 'https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXX
const jobName = 'Build and Test'
const jobStatus = 'Success'
const jobSteps = {}
const jobMatrix = {}
const channel = '@override'
const message = undefined

Expand Down Expand Up @@ -53,7 +54,7 @@ test('pull request event to slack', async () => {
.onAny()
.reply(500)

const res = await send(url, jobName, jobStatus, jobSteps, channel, message)
const res = await send(url, jobName, jobStatus, jobSteps, jobMatrix, channel, message)
await expect(res).toStrictEqual({text: {status: 'ok'}})

expect(JSON.parse(mockAxios.history.post[0].data)).toStrictEqual({
Expand Down
3 changes: 2 additions & 1 deletion __tests__/push.test.ts
Expand Up @@ -8,6 +8,7 @@ const url = 'https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXX
const jobName = 'Build and Test'
const jobStatus = 'Success'
const jobSteps = {}
const jobMatrix = {}
const channel = '@override'
const message = undefined

Expand Down Expand Up @@ -53,7 +54,7 @@ test('push event to slack', async () => {
.onAny()
.reply(500)

const res = await send(url, jobName, jobStatus, jobSteps, channel, message)
const res = await send(url, jobName, jobStatus, jobSteps, jobMatrix, channel, message)
await expect(res).toStrictEqual({text: {status: 'ok'}})

expect(JSON.parse(mockAxios.history.post[0].data)).toStrictEqual({
Expand Down
3 changes: 2 additions & 1 deletion __tests__/release.test.ts
Expand Up @@ -8,6 +8,7 @@ const url = 'https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXX
const jobName = 'Build and Test'
const jobStatus = 'Success'
const jobSteps = {}
const jobMatrix = {}
const channel = '@override'
const message = undefined

Expand Down Expand Up @@ -56,7 +57,7 @@ test('release event to slack', async () => {
.onAny()
.reply(500)

const res = await send(url, jobName, jobStatus, jobSteps, channel, message)
const res = await send(url, jobName, jobStatus, jobSteps, jobMatrix, channel, message)
await expect(res).toStrictEqual({text: {status: 'ok'}})

expect(JSON.parse(mockAxios.history.post[0].data)).toStrictEqual({
Expand Down
3 changes: 2 additions & 1 deletion __tests__/schedule.test.ts
Expand Up @@ -8,6 +8,7 @@ const url = 'https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXX
const jobName = 'Build and Test'
const jobStatus = 'Success'
const jobSteps = {}
const jobMatrix = {}
const channel = '@override'
const message = undefined

Expand Down Expand Up @@ -49,7 +50,7 @@ test('schedule event to slack', async () => {
.onAny()
.reply(500)

const res = await send(url, jobName, jobStatus, jobSteps, channel, message)
const res = await send(url, jobName, jobStatus, jobSteps, jobMatrix, channel, message)
await expect(res).toStrictEqual({text: {status: 'ok'}})

expect(JSON.parse(mockAxios.history.post[0].data)).toStrictEqual({
Expand Down
3 changes: 2 additions & 1 deletion __tests__/workflow_dispatch.test.ts
Expand Up @@ -8,6 +8,7 @@ const url = 'https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXX
const jobName = 'Build and Test'
const jobStatus = 'Success'
const jobSteps = {}
const jobMatrix = {}
const channel = '@override'
const message = undefined

Expand Down Expand Up @@ -54,7 +55,7 @@ test('workflow_dispatch event to slack', async () => {
.onAny()
.reply(500)

const res = await send(url, jobName, jobStatus, jobSteps, channel, message)
const res = await send(url, jobName, jobStatus, jobSteps, jobMatrix, channel, message)
await expect(res).toStrictEqual({text: {status: 'ok'}})

expect(JSON.parse(mockAxios.history.post[0].data)).toStrictEqual({
Expand Down
3 changes: 2 additions & 1 deletion __tests__/workflow_run.test.ts
Expand Up @@ -47,6 +47,7 @@ const url = process.env.SLACK_WEBHOOK_URL as string
const jobName = process.env.GITHUB_JOB as string
const jobStatus = (process.env.INPUT_STATUS as string).toUpperCase()
const jobSteps = process.env.INPUT_STEPS || {}
const jobMatrix = {}
const channel = process.env.INPUT_CHANNEL as string
const message = process.env.INPUT_MESSAGE as string

Expand All @@ -66,7 +67,7 @@ test('workflow_run event to slack', async () => {
schema: yaml.FAILSAFE_SCHEMA
}) as ConfigOptions

const res = await send(url, jobName, jobStatus, jobSteps, channel, message, config)
const res = await send(url, jobName, jobStatus, jobSteps, jobMatrix, channel, message, config)
await expect(res).toStrictEqual({text: {status: 'ok'}})

expect(JSON.parse(mockAxios.history.post[0].data)).toStrictEqual({
Expand Down
3 changes: 3 additions & 0 deletions action.yml
Expand Up @@ -15,6 +15,9 @@ inputs:
steps:
description: Report on the status of individual steps
required: false
matrix:
description: matrix properties
required: false
channel:
description: Override default channel with different channel or username
required: false
Expand Down
Binary file added docs/images/example4.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion src/main.ts
Expand Up @@ -29,13 +29,15 @@ async function run(): Promise<void> {
const jobName = process.env.GITHUB_JOB as string
const jobStatus = core.getInput('status', {required: true}).toUpperCase()
const jobSteps = JSON.parse(core.getInput('steps', {required: false}) || '{}')
const jobMatrix = JSON.parse(core.getInput('matrix', {required: false}) || '{}')
const channel = core.getInput('channel', {required: false})
const message = core.getInput('message', {required: false})
core.debug(`jobName: ${jobName}, jobStatus: ${jobStatus}`)
core.debug(`channel: ${channel}, message: ${message}`)
core.debug(`jobMatrix: ${JSON.stringify(jobMatrix)}`)

if (url) {
await send(url, jobName, jobStatus, jobSteps, channel, message, config)
await send(url, jobName, jobStatus, jobSteps, jobMatrix, channel, message, config)
core.info(`Sent ${jobName} status of ${jobStatus} to Slack!`)
} else {
core.warning('No "SLACK_WEBHOOK_URL"s env or "webhook-url" input configured. Skip.')
Expand Down
29 changes: 19 additions & 10 deletions src/slack.ts
Expand Up @@ -132,6 +132,7 @@ export async function send(
jobName: string,
jobStatus: string,
jobSteps: object,
jobMatrix: object,
channel?: string,
message?: string,
opts?: ConfigOptions
Expand Down Expand Up @@ -241,16 +242,23 @@ export async function send(
}{{jobStatus}}`
const fallbackTemplate = Handlebars.compile(opts?.fallback || defaultFallback)

const defaultFields = Object.entries(jobSteps).length
? [
{
title: 'Job Steps',
value: '{{#each jobSteps}}{{icon this.outcome}} {{@key}}\n{{~/each}}',
short: false,
if: 'always()'
}
]
: []
const defaultFields = []
if (Object.entries(jobSteps).length) {
defaultFields.push({
title: 'Job Steps',
value: '{{#each jobSteps}}{{icon this.outcome}} {{@key}}\n{{~/each}}',
short: false,
if: 'always()'
})
}
if (Object.entries(jobMatrix).length) {
defaultFields.push({
title: 'Job Matrix',
value: '{{#each jobMatrix}}{{@key}}: {{this}}\n{{~/each}}',
short: false,
if: 'always()'
})
}

const filteredFields: object[] = []
for (const field of opts?.fields || defaultFields) {
Expand All @@ -274,6 +282,7 @@ export async function send(
jobName,
jobStatus,
jobSteps,
jobMatrix,
eventName,
workflow,
workflowUrl,
Expand Down

0 comments on commit 160cf66

Please sign in to comment.