From a93e4e178110ac2f71bf85d0c22b4b1086cdcceb Mon Sep 17 00:00:00 2001 From: bgorsline Date: Thu, 19 Dec 2024 17:49:08 -0500 Subject: [PATCH 1/5] codepath alerts --- .github/workflows/code-path-changes.yml | 34 +++++ .../workflows/scripts/codepath-notification | 1 + .../scripts/send-notification-on-change.js | 138 ++++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 .github/workflows/code-path-changes.yml create mode 100644 .github/workflows/scripts/codepath-notification create mode 100644 .github/workflows/scripts/send-notification-on-change.js diff --git a/.github/workflows/code-path-changes.yml b/.github/workflows/code-path-changes.yml new file mode 100644 index 00000000000..8d180480538 --- /dev/null +++ b/.github/workflows/code-path-changes.yml @@ -0,0 +1,34 @@ +name: Notify Code Path Changes + +on: + pull_request: + types: [opened, synchronize] + paths: + - '**' + +env: + OAUTH2_CLIENT_ID: ${{ secrets.OAUTH2_CLIENT_ID }} + OAUTH2_CLIENT_SECRET: ${{ secrets.OAUTH2_CLIENT_SECRET }} + OAUTH2_REFRESH_TOKEN: ${{ secrets.OAUTH2_REFRESH_TOKEN }} + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: '18' + + - name: Install dependencies + run: npm install axios nodemailer + + - name: Run Notification Script + run: | + node .github/workflows/scripts/send-notification-on-change.js diff --git a/.github/workflows/scripts/codepath-notification b/.github/workflows/scripts/codepath-notification new file mode 100644 index 00000000000..4b3b6e8ee0d --- /dev/null +++ b/.github/workflows/scripts/codepath-notification @@ -0,0 +1 @@ +/ix|Ix: pdu-supply-prebid@indexexchange.com diff --git a/.github/workflows/scripts/send-notification-on-change.js b/.github/workflows/scripts/send-notification-on-change.js new file mode 100644 index 00000000000..718aa6cabe9 --- /dev/null +++ b/.github/workflows/scripts/send-notification-on-change.js @@ -0,0 +1,138 @@ +// send-notification-on-change.js +// +// called by the code-path-changes.yml workflow, this script queries github for +// the changes in the current PR, checkes the config file for whether any of those +// file paths are set to alert an email address, and sends email to multiple +// parties if needed + +const fs = require('fs'); +const path = require('path'); +const axios = require('axios'); +const nodemailer = require('nodemailer'); + +async function getAccessToken(clientId, clientSecret, refreshToken) { + try { + const response = await axios.post('https://oauth2.googleapis.com/token', { + client_id: clientId, + client_secret: clientSecret, + refresh_token: refreshToken, + grant_type: 'refresh_token', + }); + return response.data.access_token; + } catch (error) { + console.error('Failed to fetch access token:', error.response?.data || error.message); + process.exit(1); + } +} + +(async () => { + const configFilePath = path.join(__dirname, 'codepath-notification'); + const repo = process.env.GITHUB_REPOSITORY; + const prNumber = process.env.GITHUB_PR_NUMBER; + const token = process.env.GITHUB_TOKEN; + + // Generate OAuth2 access token + const CLIENT_ID = process.env.OAUTH2_CLIENT_ID; + const CLIENT_SECRET = process.env.OAUTH2_CLIENT_SECRET; + const REFRESH_TOKEN = process.env.OAUTH2_REFRESH_TOKEN; + + if (!repo || !prNumber || !token || !CLIENT_ID || !CLIENT_SECRET | !REFRESH_TOKEN) { + console.error('Missing required environment variables.'); + process.exit(1); + } + + try { + // Read and process the configuration file + const configFileContent = fs.readFileSync(configFilePath, 'utf-8'); + const configRules = configFileContent + .split('\n') + .filter(line => line.trim() !== '') // Filter out empty lines + .map(line => { + const [regex, email] = line.split(':').map(part => part.trim()); + return { regex: new RegExp(regex), email }; + }); + + // Fetch changed files + const [owner, repoName] = repo.split('/'); + const apiUrl = `https://api.github.com/repos/${owner}/${repoName}/pulls/${prNumber}/files`; + + const response = await axios.get(apiUrl, { + headers: { + Authorization: `Bearer ${token}`, + Accept: 'application/vnd.github.v3+json', + }, + }); + + const changedFiles = response.data.map(file => file.filename); + console.log('Changed files:', changedFiles); + + // Group matched files by email address + const matchesByEmail = {}; + changedFiles.forEach(file => { + configRules.forEach(rule => { + if (rule.regex.test(file)) { + if (!matchesByEmail[rule.email]) { + matchesByEmail[rule.email] = []; + } + matchesByEmail[rule.email].push(file); + } + }); + }); + + // Exit successfully if no matches are found + if (Object.keys(matchesByEmail).length === 0) { + console.log('No matches found. Exiting successfully.'); + process.exit(0); + } + + console.log('Grouped matches by email:', matchesByEmail); + + const accessToken = await getAccessToken(CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN); + + // Configure Nodemailer with OAuth2 + // service: 'Gmail', + const transporter = nodemailer.createTransport({ + host: "smtp.gmail.com", + port: 465, + secure: true, + auth: { + type: 'OAuth2', + user: 'info@prebid.org', + clientId: CLIENT_ID, + clientSecret: CLIENT_SECRET, + refreshToken: REFRESH_TOKEN, + accessToken: accessToken + }, + }); + + // Send one email per recipient + for (const [email, files] of Object.entries(matchesByEmail)) { + const emailBody = ` + ${email}, +

+ Files owned by you have been changed in open source ${repo}. The pull request is #${prNumber}. These are the files you own that have been modified: +

+ `; + + try { + await transporter.sendMail({ + from: `"Prebid Info" `, + to: email, + subject: `Files have been changed in open source ${repo}`, + html: emailBody, + }); + + console.log(`Email sent successfully to ${email}`); + console.log(`${emailBody}`); + } catch (error) { + console.error(`Failed to send email to ${email}:`, error.message); + } + } + } catch (error) { + console.error('Error:', error.message); + process.exit(1); + } +})(); + From 54632d1dc2f57abd171a1ac38f59bbac16e6a36c Mon Sep 17 00:00:00 2001 From: bretg Date: Fri, 20 Dec 2024 13:37:46 -0500 Subject: [PATCH 2/5] comments, consistency --- .../scripts/send-notification-on-change.js | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/.github/workflows/scripts/send-notification-on-change.js b/.github/workflows/scripts/send-notification-on-change.js index 718aa6cabe9..d421d68bd6a 100644 --- a/.github/workflows/scripts/send-notification-on-change.js +++ b/.github/workflows/scripts/send-notification-on-change.js @@ -32,15 +32,17 @@ async function getAccessToken(clientId, clientSecret, refreshToken) { const token = process.env.GITHUB_TOKEN; // Generate OAuth2 access token - const CLIENT_ID = process.env.OAUTH2_CLIENT_ID; - const CLIENT_SECRET = process.env.OAUTH2_CLIENT_SECRET; - const REFRESH_TOKEN = process.env.OAUTH2_REFRESH_TOKEN; + const clientId = process.env.OAUTH2_CLIENT_ID; + const clientSecret = process.env.OAUTH2_CLIENT_SECRET; + const refreshToken = process.env.OAUTH2_REFRESH_TOKEN; - if (!repo || !prNumber || !token || !CLIENT_ID || !CLIENT_SECRET | !REFRESH_TOKEN) { + // validate params + if (!repo || !prNumber || !token || !clientId || !clientSecret | !refreshToken) { console.error('Missing required environment variables.'); process.exit(1); } + // the whole process is in a big try/catch. e.g. if the config file doesn't exist, github is down, etc. try { // Read and process the configuration file const configFileContent = fs.readFileSync(configFilePath, 'utf-8'); @@ -52,10 +54,9 @@ async function getAccessToken(clientId, clientSecret, refreshToken) { return { regex: new RegExp(regex), email }; }); - // Fetch changed files + // Fetch changed files from github const [owner, repoName] = repo.split('/'); const apiUrl = `https://api.github.com/repos/${owner}/${repoName}/pulls/${prNumber}/files`; - const response = await axios.get(apiUrl, { headers: { Authorization: `Bearer ${token}`, @@ -66,7 +67,7 @@ async function getAccessToken(clientId, clientSecret, refreshToken) { const changedFiles = response.data.map(file => file.filename); console.log('Changed files:', changedFiles); - // Group matched files by email address + // match file pathnames that are in the config and group them by email address const matchesByEmail = {}; changedFiles.forEach(file => { configRules.forEach(rule => { @@ -79,7 +80,7 @@ async function getAccessToken(clientId, clientSecret, refreshToken) { }); }); - // Exit successfully if no matches are found + // Exit successfully if no matches were found if (Object.keys(matchesByEmail).length === 0) { console.log('No matches found. Exiting successfully.'); process.exit(0); @@ -87,7 +88,8 @@ async function getAccessToken(clientId, clientSecret, refreshToken) { console.log('Grouped matches by email:', matchesByEmail); - const accessToken = await getAccessToken(CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN); + // get ready to email the changes + const accessToken = await getAccessToken(clientId, clientSecret, refreshToken); // Configure Nodemailer with OAuth2 // service: 'Gmail', @@ -98,9 +100,9 @@ async function getAccessToken(clientId, clientSecret, refreshToken) { auth: { type: 'OAuth2', user: 'info@prebid.org', - clientId: CLIENT_ID, - clientSecret: CLIENT_SECRET, - refreshToken: REFRESH_TOKEN, + clientId: clientId, + clientSecret: clientSecret, + refreshToken: refreshToken, accessToken: accessToken }, }); @@ -135,4 +137,3 @@ async function getAccessToken(clientId, clientSecret, refreshToken) { process.exit(1); } })(); - From b72b0c7f0fa5f0c3267d31c2354c1ed78a2da52e Mon Sep 17 00:00:00 2001 From: bretg Date: Fri, 20 Dec 2024 13:42:12 -0500 Subject: [PATCH 3/5] allowing comments in config file --- .github/workflows/scripts/send-notification-on-change.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scripts/send-notification-on-change.js b/.github/workflows/scripts/send-notification-on-change.js index d421d68bd6a..d77989d565b 100644 --- a/.github/workflows/scripts/send-notification-on-change.js +++ b/.github/workflows/scripts/send-notification-on-change.js @@ -48,7 +48,7 @@ async function getAccessToken(clientId, clientSecret, refreshToken) { const configFileContent = fs.readFileSync(configFilePath, 'utf-8'); const configRules = configFileContent .split('\n') - .filter(line => line.trim() !== '') // Filter out empty lines + .filter(line => line.trim() !== '' && !line.trim().startsWith('#')) // Ignore empty lines and comments .map(line => { const [regex, email] = line.split(':').map(part => part.trim()); return { regex: new RegExp(regex), email }; From bf79ce62f32abaa0000da25b543e716a3d649540 Mon Sep 17 00:00:00 2001 From: bretg Date: Fri, 20 Dec 2024 13:48:35 -0500 Subject: [PATCH 4/5] added comments --- .../workflows/scripts/codepath-notification | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/workflows/scripts/codepath-notification b/.github/workflows/scripts/codepath-notification index 4b3b6e8ee0d..7665b800901 100644 --- a/.github/workflows/scripts/codepath-notification +++ b/.github/workflows/scripts/codepath-notification @@ -1 +1,19 @@ -/ix|Ix: pdu-supply-prebid@indexexchange.com +# when a changed file paths matches the regex, send an alert email +# structure of the file is: +# +# javascriptRegex : email address +# +# For example, in PBS Java, there are many paths that can belong to bid adapter: +# +# /src/main/java/org/prebid/server/bidder/BIDDER +# /src/main/resources/static/bidder-params/BIDDER.json +# /src/main/resources/bidder-config/BIDDER.yaml +# /src//main/java/org/prebid/server/proto/openrtb/ext/request/BIDDER +# /src/test/resources/org/prebid/server/it/openrtb2/BIDDER +# /src/test/java/org/prebid/server/it/BIDDERTest.java +# /src/test/java/org/prebid/server/bidder/BIDDER +# /src/main/java/org/prebid/server/spring/config/bidder/BIDDERConfiguration.java +# +# The aim is to find a minimal set of regex patterns that matches any file in these paths + +/ix|Ix|ix.json|ix.yaml: pdu-supply-prebid@indexexchange.com From 5d7e5ea2d6c84a9f48958a82e049b803dc0d91f1 Mon Sep 17 00:00:00 2001 From: bretg Date: Thu, 2 Jan 2025 09:47:58 -0500 Subject: [PATCH 5/5] fixed typo --- .github/workflows/scripts/send-notification-on-change.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scripts/send-notification-on-change.js b/.github/workflows/scripts/send-notification-on-change.js index d77989d565b..f4e4fdcd3ca 100644 --- a/.github/workflows/scripts/send-notification-on-change.js +++ b/.github/workflows/scripts/send-notification-on-change.js @@ -37,7 +37,7 @@ async function getAccessToken(clientId, clientSecret, refreshToken) { const refreshToken = process.env.OAUTH2_REFRESH_TOKEN; // validate params - if (!repo || !prNumber || !token || !clientId || !clientSecret | !refreshToken) { + if (!repo || !prNumber || !token || !clientId || !clientSecret || !refreshToken) { console.error('Missing required environment variables.'); process.exit(1); }