diff --git a/.github/slack.yml b/.github/slack.yml
index 0b4b8db3..703af7e1 100644
--- a/.github/slack.yml
+++ b/.github/slack.yml
@@ -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
diff --git a/README.md b/README.md
index 2b480dee..5bcc7c55 100644
--- a/README.md
+++ b/README.md
@@ -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) }}
+
+
+
#### `channel` (optional)
To override the channel or to send the Slack message to an individual
@@ -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**
@@ -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
diff --git a/__tests__/blocks.test.ts b/__tests__/blocks.test.ts
index ffb4c236..7953995c 100644
--- a/__tests__/blocks.test.ts
+++ b/__tests__/blocks.test.ts
@@ -40,6 +40,7 @@ const jobSteps = {
conclusion: 'failure'
}
}
+const jobMatrix = {}
const channel = '#github-ci'
// mock github context
@@ -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({
diff --git a/__tests__/config.test.ts b/__tests__/config.test.ts
index 78bb45f7..9d517d62 100644
--- a/__tests__/config.test.ts
+++ b/__tests__/config.test.ts
@@ -40,6 +40,7 @@ const jobSteps = {
conclusion: 'failure'
}
}
+const jobMatrix = {}
const channel = '#github-ci'
// mock github context
@@ -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({
diff --git a/__tests__/inputs.test.ts b/__tests__/inputs.test.ts
index 4cc518cb..fe9a9b52 100644
--- a/__tests__/inputs.test.ts
+++ b/__tests__/inputs.test.ts
@@ -40,6 +40,7 @@ const jobSteps = {
conclusion: 'cancelled'
}
}
+const jobMatrix = {}
const channel = '#deploy'
let message = 'Successfully deployed to {{ env.ENVIRONMENT }}!'
@@ -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({
diff --git a/__tests__/job_matrix.test.ts b/__tests__/job_matrix.test.ts
new file mode 100644
index 00000000..8a4e1b35
--- /dev/null
+++ b/__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: '** for \n - 4 commits',
+ title: '',
+ fields: [
+ {
+ title: 'Job Matrix',
+ value: 'name1: value1\nname2: value2\n',
+ short: false
+ }
+ ],
+ footer: ' #8',
+ footer_icon: 'https://github.githubassets.com/favicon.ico',
+ ts: expect.stringMatching(/[0-9]+/)
+ }
+ ]
+ })
+
+ mockAxios.resetHistory()
+ mockAxios.reset()
+})
diff --git a/__tests__/job_status.test.ts b/__tests__/job_status.test.ts
index 0712b123..abf12a5e 100644
--- a/__tests__/job_status.test.ts
+++ b/__tests__/job_status.test.ts
@@ -39,6 +39,7 @@ const jobSteps = {
conclusion: 'skipped'
}
}
+const jobMatrix = {}
const channel = '#github-ci'
const message = undefined
@@ -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({
diff --git a/__tests__/pull_request.test.ts b/__tests__/pull_request.test.ts
index 7b6e24a1..27c1d5c9 100644
--- a/__tests__/pull_request.test.ts
+++ b/__tests__/pull_request.test.ts
@@ -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
@@ -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({
diff --git a/__tests__/push.test.ts b/__tests__/push.test.ts
index 00c19bbb..2cf1d846 100644
--- a/__tests__/push.test.ts
+++ b/__tests__/push.test.ts
@@ -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
@@ -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({
diff --git a/__tests__/release.test.ts b/__tests__/release.test.ts
index 8bec9f4c..6e416548 100644
--- a/__tests__/release.test.ts
+++ b/__tests__/release.test.ts
@@ -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
@@ -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({
diff --git a/__tests__/schedule.test.ts b/__tests__/schedule.test.ts
index 2871af84..a317966e 100644
--- a/__tests__/schedule.test.ts
+++ b/__tests__/schedule.test.ts
@@ -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
@@ -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({
diff --git a/__tests__/workflow_dispatch.test.ts b/__tests__/workflow_dispatch.test.ts
index 1991620a..9ce0a908 100644
--- a/__tests__/workflow_dispatch.test.ts
+++ b/__tests__/workflow_dispatch.test.ts
@@ -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
@@ -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({
diff --git a/__tests__/workflow_run.test.ts b/__tests__/workflow_run.test.ts
index f533a589..c2164153 100644
--- a/__tests__/workflow_run.test.ts
+++ b/__tests__/workflow_run.test.ts
@@ -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
@@ -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({
diff --git a/action.yml b/action.yml
index 96558e72..7601f82b 100644
--- a/action.yml
+++ b/action.yml
@@ -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
diff --git a/docs/images/example4.png b/docs/images/example4.png
new file mode 100644
index 00000000..9d463ebc
Binary files /dev/null and b/docs/images/example4.png differ
diff --git a/src/main.ts b/src/main.ts
index 58b475b0..27d1a689 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -29,13 +29,15 @@ async function run(): Promise {
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.')
diff --git a/src/slack.ts b/src/slack.ts
index d2d19f71..a7f268d0 100644
--- a/src/slack.ts
+++ b/src/slack.ts
@@ -132,6 +132,7 @@ export async function send(
jobName: string,
jobStatus: string,
jobSteps: object,
+ jobMatrix: object,
channel?: string,
message?: string,
opts?: ConfigOptions
@@ -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) {
@@ -274,6 +282,7 @@ export async function send(
jobName,
jobStatus,
jobSteps,
+ jobMatrix,
eventName,
workflow,
workflowUrl,