diff --git a/.github/scripts/auto-respond-pr.js b/.github/scripts/auto-respond-pr.js
new file mode 100644
index 000000000..8cb989294
--- /dev/null
+++ b/.github/scripts/auto-respond-pr.js
@@ -0,0 +1,40 @@
+const { Octokit } = require('@octokit/rest')
+const fs = require('fs')
+
+const githubToken = process.env.GITHUB_TOKEN
+const repoFullName = process.env.GITHUB_REPOSITORY
+const [owner, repo] = repoFullName.split('/')
+const ev = JSON.parse(
+ fs.readFileSync(process.env.GITHUB_EVENT_PATH, 'utf8')
+)
+const prNumber = ev.pull_request.number
+
+const octokit = new Octokit({
+ auth: githubToken
+})
+
+async function main () {
+ const diffOutput = fs.readFileSync('diff_output.txt', 'utf-8')
+
+ let commentBody = `
+Generated file outputs:
+
+${diffOutput}
+`
+
+ if (commentBody.length > 65536) {
+ commentBody = '❌ Generated diff output is too large to post as a comment, run locally to see the diff and validate'
+ }
+
+ await octokit.rest.issues.createComment({
+ owner,
+ repo,
+ issue_number: prNumber,
+ body: commentBody
+ })
+}
+
+main().catch((err) => {
+ console.error(err)
+ process.exit(1)
+})
diff --git a/.github/scripts/diff-directories.js b/.github/scripts/diff-directories.js
new file mode 100644
index 000000000..15ddefce0
--- /dev/null
+++ b/.github/scripts/diff-directories.js
@@ -0,0 +1,123 @@
+const fs = require('fs')
+const path = require('path')
+const diff = require('diff')
+
+function readFilesRecursively (directory) {
+ const filenames = fs.readdirSync(directory)
+ const files = {}
+
+ filenames.forEach((filename) => {
+ const filePath = path.join(directory, filename)
+ const fileStats = fs.statSync(filePath)
+
+ if (fileStats.isDirectory()) {
+ const nestedFiles = readFilesRecursively(filePath)
+ for (const [nestedFilePath, nestedFileContent] of Object.entries(nestedFiles)) {
+ files[path.join(filename, nestedFilePath)] = nestedFileContent
+ }
+ } else {
+ files[filename] = fs.readFileSync(filePath, 'utf-8')
+ }
+ })
+
+ return files
+}
+
+/**
+ * Removes the version from the file contents to imrpove diff readability
+ * @param {string} fileContent
+ * @param {string} filePath
+ * @returns {string}
+ */
+function mungeFileContents (fileContent, filePath) {
+ if (filePath.endsWith('.json')) {
+ const fileJSON = JSON.parse(fileContent)
+ delete fileJSON.version
+ if ('features' in fileJSON) {
+ for (const key of Object.keys(fileJSON.features)) {
+ if ('hash' in fileJSON.features[key]) {
+ delete fileJSON.features[key].hash
+ }
+ }
+ }
+ return JSON.stringify(fileJSON, null, 4)
+ }
+ return fileContent
+}
+
+function displayDiffs (dir1Files, dir2Files, isOpen) {
+ const out = []
+ for (const [filePath, fileContent] of Object.entries(dir1Files)) {
+ let diffOut = ''
+ if (filePath in dir2Files) {
+ const fileOut = mungeFileContents(fileContent, filePath)
+ const file2Out = mungeFileContents(dir2Files[filePath], filePath)
+ if (fileOut === file2Out) {
+ diffOut = `⚠️ File ${filePath} is identical`
+ } else {
+ // Slice of file header from diff output
+ const fileDiff = diff.createPatch(filePath, fileOut, file2Out).split('\n').slice(2).join('\n')
+ if (fileDiff) {
+ fileDiffOut =
+ diffOut = `
+
+\`\`\`diff\n
+${fileDiff}
+\`\`\`
+
+`
+ }
+ }
+
+ delete dir2Files[filePath]
+ } else {
+ diffOut = `❌ File ${filePath} only exists in old changeset`
+ }
+ out.push(renderDetails(filePath, diffOut, isOpen))
+ }
+
+ for (const filePath of Object.keys(dir2Files)) {
+ out.push(`❌ File ${filePath} only exists in new changeset`)
+ }
+ return out
+}
+
+function renderDetails (section, text, isOpen) {
+ const open = isOpen ? 'open' : ''
+ return `
+${section}
+${text}
+ `
+}
+
+if (process.argv.length !== 4) {
+ console.error('Usage: node diff_directories.js ')
+ process.exit(1)
+}
+
+const dir1 = process.argv[2]
+const dir2 = process.argv[3]
+
+const sections = {
+ legacy: {},
+ latest: {}
+}
+function sortFiles (dirFiles, dirName) {
+ for (const [filePath, fileContent] of Object.entries(dirFiles)) {
+ if (filePath.startsWith('v2')) {
+ sections.latest[dirName] = sections.latest[dirName] || {}
+ sections.latest[dirName][filePath] = fileContent
+ } else {
+ sections.legacy[dirName] = sections.legacy[dirName] || {}
+ sections.legacy[dirName][filePath] = fileContent
+ }
+ }
+}
+sortFiles(readFilesRecursively(dir1), 'dir1')
+sortFiles(readFilesRecursively(dir2), 'dir2')
+
+for (const [section, files] of Object.entries(sections)) {
+ const isOpen = section === 'latest'
+ const fileOut = displayDiffs(files.dir1, files.dir2, isOpen).join('\n')
+ console.log(renderDetails(section, fileOut, isOpen))
+}
diff --git a/.github/workflows/auto-respond-pr.yml b/.github/workflows/auto-respond-pr.yml
new file mode 100644
index 000000000..79c6f3c31
--- /dev/null
+++ b/.github/workflows/auto-respond-pr.yml
@@ -0,0 +1,53 @@
+name: Auto Respond to PR
+
+on:
+ pull_request:
+ types:
+ - opened
+ - synchronize
+
+jobs:
+ auto_respond:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout main branch
+ uses: actions/checkout@v2
+ with:
+ ref: main
+ repository: ${{ github.event.pull_request.head.repo.full_name }}
+ path: main
+
+ - name: Checkout PR branch
+ uses: actions/checkout@v2
+ with:
+ ref: ${{ github.event.pull_request.head.ref }}
+ repository: ${{ github.event.pull_request.head.repo.full_name }}
+ path: pr
+
+ - name: Install dependencies
+ run: |
+ npm install @octokit/rest
+ npm install diff
+
+ - name: Run build script on main branch
+ run: |
+ cd main
+ npm install
+ node index.js
+ cd ..
+
+ - name: Run build script on PR branch
+ run: |
+ cd pr
+ npm install
+ node index.js
+ cd ..
+
+ - name: Create diff of file outputs
+ run: node pr/.github/scripts/diff-directories.js main/generated pr/generated > diff_output.txt
+
+ - name: Run auto response script
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: node pr/.github/scripts/auto-respond-pr.js
diff --git a/features/runtime-checks.json b/features/runtime-checks.json
index 099d17597..c2a8e6a13 100644
--- a/features/runtime-checks.json
+++ b/features/runtime-checks.json
@@ -64,7 +64,7 @@
"domain": "facebook.com"
},
{
- "domain": "youtube.com"
+ "disabled_domain": "youtube.com"
},
{
"disabled_domain": "stackoverflow.com"
diff --git a/package-lock.json b/package-lock.json
index 5847fa34d..7ad6cdf8a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,6 +15,7 @@
"devDependencies": {
"ajv": "^8.6.3",
"chai": "^4.3.4",
+ "diff": "^5.1.0",
"eslint": "^7.32.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.24.2",
@@ -609,9 +610,9 @@
}
},
"node_modules/diff": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
- "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz",
+ "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==",
"dev": true,
"engines": {
"node": ">=0.3.1"
@@ -2026,6 +2027,15 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
+ "node_modules/mocha/node_modules/diff": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
+ "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
"node_modules/mocha/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -3742,9 +3752,9 @@
}
},
"diff": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
- "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz",
+ "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==",
"dev": true
},
"doctrine": {
@@ -4781,6 +4791,12 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
+ "diff": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
+ "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
+ "dev": true
+ },
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
diff --git a/package.json b/package.json
index 4b311e070..14c281dc9 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"devDependencies": {
"ajv": "^8.6.3",
"chai": "^4.3.4",
+ "diff": "^5.1.0",
"eslint": "^7.32.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.24.2",