diff --git a/.github/workflows/merged-to-main.yaml b/.github/workflows/merged-to-main.yaml index c4afa89..4ebbdae 100644 --- a/.github/workflows/merged-to-main.yaml +++ b/.github/workflows/merged-to-main.yaml @@ -1,7 +1,7 @@ name: Merged to Main on: - # Trigger this workflow when a pull request is closed and merged into the main branch + # Triggered when a pull request is closed or merged into the main branch pull_request: branches: - main @@ -11,12 +11,12 @@ on: workflow_dispatch: inputs: pr_number: - description: "PR Number to use (optional for manual triggers)" + description: "PR Number" default: "200" required: true env: - # Slack webhook secret for sending release notes + # Slack webhook URL from secret CC_RELEASE_SLACK_WH: ${{ secrets.CC_RELEASE_SLACK_WH }} jobs: send_release_notes: @@ -36,7 +36,7 @@ jobs: run: npm i -D @octokit/rest # Step to set PR_NUMBER environment variable from the pull request event - - name: Set PR Number + - name: Set PR Number from PR event if: ${{ github.event_name != 'workflow_dispatch' }} run: echo "PR_NUMBER=${{ github.event.pull_request.number }}" >> $GITHUB_ENV diff --git a/.github/workflows/src/helpers.js b/.github/workflows/src/helpers.js index 1c5e0e6..145d39a 100644 --- a/.github/workflows/src/helpers.js +++ b/.github/workflows/src/helpers.js @@ -2,29 +2,27 @@ const extractPRData = (prBody) => { const lines = prBody.trim().split('\n'); /** - * ^-\s* : Match a dash followed by optional whitespace at the start of the line - * (https:\/\/github\.com\/ : Match the GitHub URL prefix and capture the whole URL - * ([^\/]+)\/ : Capture the repository owner (one or more non-slash characters) - * ([^\/]+)\/ : Capture the repository name (one or more non-slash characters) - * pull\/(\d+)) : Capture the pull request number (one or more digits) - * (?:\s*:\s*(.*))? : Optionally match a colon followed by optional whitespace and capture if any description after the pr + * ^-\s* : Match a dash followed by optional whitespace at the start of the line + * (https:\/\/github\.com\/ : Match the GitHub URL prefix and capture the whole URL + * ([^\/]+)\/ : Capture the repository owner (one or more non-slash characters) + * ([^\/]+)\/ : Capture the repository name (one or more non-slash characters) + * pull\/(\d+)) : Capture the pull request number (one or more digits) + * (?:\s*:\s*(.*))? : Optionally match whitespace and capture if any description after the pr */ - const prLinkMatchExp = - /^-\s*(https:\/\/github\.com\/([^\/]+)\/([^\/]+)\/pull\/(\d+))(?:\s*:\s*(.*))?$/; + const prLinkMatchExp = /^-\s*(https:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+))(?:\s*:\s*(.*))?$/; return lines .filter((line) => prLinkMatchExp.test(line.trim().replace(/(^'|'$)/g, ''))) .map((line) => { - line = line.trim().replace(/(^'|'$)/g, ''); - const [_, prLink, owner, repo, prNumber] = line.match(prLinkMatchExp); + const [, , owner, repo, prNumber] = line.trim().replace(/(^'|'$)/g, '').match(prLinkMatchExp); return { owner, repo, prNumber }; }); }; // Function to fetch PR details using Octokit const fetchPRDetails = async (prData, octokit) => { - const promises = prData.map(({ owner, repo, prNumber }) => - octokit.pulls.get({ owner, repo, pull_number: prNumber }) + const promises = prData.map( + ({ owner, repo, prNumber }) => octokit.pulls.get({ owner, repo, pull_number: prNumber }), ); const result = await Promise.allSettled(promises); @@ -38,7 +36,7 @@ const preparePRTitleMap = async (prBody, octokit) => { const prData = extractPRData(prBody); const prDetails = await fetchPRDetails(prData, octokit); - return prDetails.reduce((pv, { number, title }) => { + return prDetails.reduce((pv, { number, title }) => { pv[number] = title; return pv; }, {}); @@ -47,39 +45,38 @@ const preparePRTitleMap = async (prBody, octokit) => { // Function to parse text and convert it into Slack blocks const parseTextToSlackBlocks = async (inputText, octokit) => { const prNumberTitleMap = await preparePRTitleMap(inputText, octokit); - const prLinkRegex = - /^-\s*(https:\/\/github\.com\/([^\/]+)\/([^\/]+)\/pull\/(\d+))(?:\s*:\s*(.*))?$/; + const prLinkRegex = /^-\s*(https:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+))(?:\s*:\s*(.*))?$/; const mdLinkRegex = /^(.+)\[([\w+]+)\]\((.+)\)/gi; const unorderedListRegex = /^\s*[-+*]\s+(.*$)/gim; const orderedListRegex = /^\s*(\d+)\.\s+(.*$)/gim; - let slackBlocks = []; + const slackBlocks = []; // Process each line of input text inputText .trim() .split('\n') .forEach((line) => { - line = line.trim().replace(/(^'|'$)/g, ''); // Remove leading and trailing quotes + let formattedLine = line.trim().replace(/(^'|'$)/g, ''); // Remove leading and trailing quotes - const prLinkMatch = line.match(prLinkRegex); - const mdLinkMatch = line.match(mdLinkRegex); + const prLinkMatch = formattedLine.match(prLinkRegex); + const mdLinkMatch = formattedLine.match(mdLinkRegex); - /** - * Replace the MD Link format to Slack link format + /** + * Replace the MD Link format to Slack link format * Ex: * MD format: - [Link](https://www.adobe.com/) - * Slack format: - - **/ - if (mdLinkMatch) line = line.replace(mdLinkRegex, '$1 <$3|$2>'); + * Slack format: - + * */ + if (mdLinkMatch) formattedLine = formattedLine.replace(mdLinkRegex, '$1 <$3|$2>'); // Convert unordered lists - line = line.replace(unorderedListRegex, '\t• $1'); + formattedLine = formattedLine.replace(unorderedListRegex, '\t• $1'); // Convert ordered lists - line = line.replace(orderedListRegex, '\t$1. $2'); + formattedLine = formattedLine.replace(orderedListRegex, '\t$1. $2'); // Convert the Bold block - line = line.replace(/\*\*(.*?)\*\*/gim, '*$1*'); + formattedLine = formattedLine.replace(/\*\*(.*?)\*\*/gim, '*$1*'); - if (line === '') { + if (formattedLine === '') { slackBlocks.push({ type: 'section', text: { type: 'mrkdwn', text: '\n' }, @@ -88,21 +85,22 @@ const parseTextToSlackBlocks = async (inputText, octokit) => { } if (prLinkMatch) { - const [_, prLink, owner, repo, prNumber, lineDescription] = prLinkMatch; + // const [_, prLink, owner, repo, prNumber, lineDescription] = prLinkMatch; + const [, prLink, , , prNumber, lineDescription] = prLinkMatch; const prTitle = prNumberTitleMap[prNumber]; slackBlocks.push({ type: 'section', text: { type: 'mrkdwn', text: `\t• :merged: <${prLink}|*${prTitle}* #${prNumber}>${ - lineDescription ? ' - ' + lineDescription : '' + lineDescription ? ` - ${lineDescription}` : '' }`, }, }); return; } - if (line.startsWith('### ')) { + if (formattedLine.startsWith('### ')) { slackBlocks.push({ type: 'header', text: { @@ -111,21 +109,17 @@ const parseTextToSlackBlocks = async (inputText, octokit) => { emoji: true, }, }); - slackBlocks.push({ - type: 'divider', - }); + slackBlocks.push({ type: 'divider' }); return; } slackBlocks.push({ type: 'section', - text: { type: 'mrkdwn', text: line }, + text: { type: 'mrkdwn', text: formattedLine }, }); }); return slackBlocks; }; -module.exports = { - parseTextToSlackBlocks, -}; +module.exports = { parseTextToSlackBlocks }; diff --git a/.github/workflows/src/send-slack.js b/.github/workflows/src/send-slack.js index 7e796a6..e9c66b3 100644 --- a/.github/workflows/src/send-slack.js +++ b/.github/workflows/src/send-slack.js @@ -1,7 +1,7 @@ -const { parseTextToSlackBlocks } = require("./helpers"); +const { parseTextToSlackBlocks } = require('./helpers.js'); // Those env variables are set by an github action automatically -const [owner, repo]=process.env.GITHUB_REPOSITORY?.split('/') || ''; +const [owner, repo] = process.env.GITHUB_REPOSITORY?.split('/') || ''; const auth = process.env.GITHUB_TOKEN; /** @@ -11,57 +11,57 @@ const auth = process.env.GITHUB_TOKEN; * @returns {Promise} - Promise representing the result of the Slack message sending process */ const sendReleaseNotes = async (prNumber, slackWebHookURL) => { + console.log({ owner, repo, prNumber }); - console.log({ owner, repo, pr_number: prNumber, slackWebHookURL}); try { - prNumber = parseInt(prNumber); - const { Octokit } = await import("@octokit/rest"); + const number = parseInt(prNumber, 10); + const { Octokit } = await import('@octokit/rest'); const octokit = new Octokit({ auth }); const { status, data } = await octokit.pulls.get({ - pull_number: prNumber, + pull_number: number, owner, repo, }); - if (status !== 200) throw new Error("Cannot fetch the PR details"); + if (status !== 200) throw new Error('Cannot fetch the PR details'); - const { html_url, title, body } = data; + const { html_url: prLink, title, body } = data; const formattedBodyBlocks = await parseTextToSlackBlocks(body, octokit); const titleBlocks = [ { - type: "header", + type: 'header', text: { - type: "plain_text", - text: ":rocket: Production Release", + type: 'plain_text', + text: ':rocket: Production Release', }, }, { - type: "section", + type: 'section', text: { - type: "mrkdwn", - text: `<${html_url}| *#${prNumber}: ${title}*>`, + type: 'mrkdwn', + text: `<${prLink}| *#${number}: ${title}*>`, }, }, ]; - const slackBodyBlocks = JSON.stringify({ - blocks: [...titleBlocks, ...formattedBodyBlocks], - }); - console.log("Message", slackBodyBlocks); + const slackBodyBlocks = JSON.stringify({ blocks: [...titleBlocks, ...formattedBodyBlocks] }); + + console.log('Message', slackBodyBlocks); // Send message to Slack webhook const result = await fetch(slackWebHookURL, { - method: "POST", + method: 'POST', body: slackBodyBlocks, - headers: { - "Content-type": "application/json", - }, + headers: { 'Content-type': 'application/json' }, }).catch(console.error); - if (result.status === 200) console.log("Slack Message sent"); + if (result.status === 200) console.log('Slack Message sent'); else { - console.log(`Slack Message not sent ${result.status}:${result.statusText}`) + console.log( + `Slack Message not sent ${result.status}:${result.statusText}` + ); + console.log(result); } - console.log(result); + return result; } catch (e) { console.log(e);