diff --git a/__tests__/wait.test.ts b/__tests__/wait.test.ts deleted file mode 100644 index 1336aaa..0000000 --- a/__tests__/wait.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Unit tests for src/wait.ts - */ - -import { wait } from '../src/wait' -import { expect } from '@jest/globals' - -describe('wait.ts', () => { - it('throws an invalid number', async () => { - const input = parseInt('foo', 10) - expect(isNaN(input)).toBe(true) - - await expect(wait(input)).rejects.toThrow('milliseconds not a number') - }) - - it('waits with a valid number', async () => { - const start = new Date() - await wait(500) - const end = new Date() - - const delta = Math.abs(end.getTime() - start.getTime()) - - expect(delta).toBeGreaterThan(450) - }) -}) diff --git a/action.yml b/action.yml index 101186a..9f13ad0 100644 --- a/action.yml +++ b/action.yml @@ -1,11 +1,7 @@ -name: 'The name of your action here' -description: 'Provide a description here' -author: 'Your name or organization here' - -# Add your action's branding here. This will appear on the GitHub Marketplace. -branding: - icon: 'heart' - color: 'red' +name: 'GitHub notice to Slack' +description: + 'This workflow is triggered by the opening of a pull request and notifies + users assigned as reviewers via mention on Slack.' # Define your inputs here. inputs: diff --git a/src/create-context.ts b/src/create-context.ts new file mode 100644 index 0000000..cb39f21 --- /dev/null +++ b/src/create-context.ts @@ -0,0 +1,47 @@ +import { PullRequestEvent } from '@octokit/webhooks-types' +import { toSlackUser } from './to-slack-user' + +interface ImageElement { + type: 'image' + image_url: string + alt_text: string +} + +interface TextElement { + type: 'mrkdwn' + text: string +} + +export interface Context { + type: 'context' + elements: (ImageElement | TextElement)[] +} + +// createContextはFooterみたいなやつで、PR作成者とそのレポジトリ名が表示されるようになっている +export function createContext(payload: PullRequestEvent): Context { + const { + pull_request: { user }, + repository: { full_name } + } = payload + + const creator = toSlackUser(user.login) + + const contextText = `${creator} - :github: ${full_name}` + + const context: Context = { + type: 'context', + elements: [ + { + type: 'image', + image_url: user.avatar_url, + alt_text: user.login + }, + { + type: 'mrkdwn', + text: contextText + } + ] + } + + return context +} diff --git a/src/create-section.ts b/src/create-section.ts new file mode 100644 index 0000000..84a0e19 --- /dev/null +++ b/src/create-section.ts @@ -0,0 +1,31 @@ +import { PullRequestEvent } from '@octokit/webhooks-types' +import { toSlackUsers } from './to-slack-users' + +export interface Section { + type: 'section' + text: { + type: 'mrkdwn' + text: string + } +} + +// createSectionでメッセージの本文を作成。レビューアーの一覧とPRタイトルのリンクが表示される。 +// toSlackUsersは Record をハードコードで管理していて、Githubのアカウント名からSlackのメンションを作成している +export function createSection(payload: PullRequestEvent): Section { + const { + pull_request: { title, html_url, number, requested_reviewers } + } = payload + + const reviewers = toSlackUsers(requested_reviewers) + + const sectionText = `${reviewers.join(' ')}\n*<${html_url}|#${number} - ${title}>*` + const section: Section = { + type: 'section', + text: { + type: 'mrkdwn', + text: sectionText + } + } + + return section +} diff --git a/src/create-slack-message.ts b/src/create-slack-message.ts new file mode 100644 index 0000000..c7263d2 --- /dev/null +++ b/src/create-slack-message.ts @@ -0,0 +1,26 @@ +import * as core from '@actions/core' +import * as github from '@actions/github' +import { PullRequestEvent } from '@octokit/webhooks-types' +import { createContext } from './create-context' +import { createSection } from './create-section' + +export function createSlackMessage(): void { + const payload = github.context.payload as PullRequestEvent + + if (payload.action !== 'opened') { + throw new Error( + 'This action is supposed to be run on pull_request opened event' + ) + } + + if (!payload.pull_request) { + throw new Error('This action is supposed to be run on pull_request event') + } + + const section = createSection(payload) + const context = createContext(payload) + + const message = JSON.stringify({ blocks: [section, context] }) + + core.setOutput('message', message) +} diff --git a/src/main.ts b/src/main.ts index c804f90..68a0331 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,5 @@ import * as core from '@actions/core' -import { wait } from './wait' +import { createSlackMessage } from './create-slack-message' /** * The main function for the action. @@ -7,18 +7,7 @@ import { wait } from './wait' */ export async function run(): Promise { try { - const ms: string = core.getInput('milliseconds') - - // Debug logs are only output if the `ACTIONS_STEP_DEBUG` secret is true - core.debug(`Waiting ${ms} milliseconds ...`) - - // Log the current timestamp, wait, then log the new timestamp - core.debug(new Date().toTimeString()) - await wait(parseInt(ms, 10)) - core.debug(new Date().toTimeString()) - - // Set outputs for other workflow steps to use - core.setOutput('time', new Date().toTimeString()) + createSlackMessage() } catch (error) { // Fail the workflow run if an error occurs if (error instanceof Error) core.setFailed(error.message) diff --git a/src/to-slack-user.ts b/src/to-slack-user.ts new file mode 100644 index 0000000..8bbb309 --- /dev/null +++ b/src/to-slack-user.ts @@ -0,0 +1,8 @@ +export function toSlackUser(githubUsername: string): string { + const userMappings: Record = { + rysiva: 'Ryohei' + } + + const slackUsername = userMappings[githubUsername] + return slackUsername ? `<@${slackUsername}>` : githubUsername +} diff --git a/src/to-slack-users.ts b/src/to-slack-users.ts new file mode 100644 index 0000000..86d957a --- /dev/null +++ b/src/to-slack-users.ts @@ -0,0 +1,11 @@ +import { User, Team } from '@octokit/webhooks-types' +import { toSlackUser } from './to-slack-user' + +export function toSlackUsers(requestedReviewers: (User | Team)[]): string[] { + return requestedReviewers.map(reviewer => { + if ('login' in reviewer) { + return toSlackUser(reviewer.login) + } + throw new Error('ログインプロパティが見つかりません') + }) +} diff --git a/src/wait.ts b/src/wait.ts deleted file mode 100644 index 0ddf692..0000000 --- a/src/wait.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Wait for a number of milliseconds. - * @param milliseconds The number of milliseconds to wait. - * @returns {Promise} Resolves with 'done!' after the wait is over. - */ -export async function wait(milliseconds: number): Promise { - return new Promise(resolve => { - if (isNaN(milliseconds)) { - throw new Error('milliseconds not a number') - } - - setTimeout(() => resolve('done!'), milliseconds) - }) -}