diff --git a/.github/workflows/initiative-auto-pr.yml b/.github/workflows/initiative-auto-pr.yml index 5f9bb790..22bca608 100644 --- a/.github/workflows/initiative-auto-pr.yml +++ b/.github/workflows/initiative-auto-pr.yml @@ -31,6 +31,7 @@ jobs: uses: nick-fields/retry@v2 env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + ISSUE_TITLE: ${{ github.event.issue.title }} ISSUE_BODY: ${{ github.event.issue.body }} with: timeout_minutes: 5 @@ -39,6 +40,9 @@ jobs: - name: Generate PR uses: actions/github-script@v6 + env: + ISSUE_AUTHOR: ${{ github.event.issue.user.login }} + ISSUE_TITLE: ${{ github.event.issue.title }} with: script: | const script = require(process.env.GITHUB_WORKSPACE + '/scripts/generatePullRequestForNewInitiative.js') diff --git a/package-lock.json b/package-lock.json index 4e4a02c5..4e79fdfc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "remark-html": "^16.0.1" }, "devDependencies": { + "@octokit/rest": "^20.0.2", "@trivago/prettier-plugin-sort-imports": "^4.2.1", "@types/lunr": "^2.3.6", "@types/mixpanel-browser": "^2.47.4", @@ -1364,6 +1365,26 @@ "glob": "7.1.7" } }, + "node_modules/@next/eslint-plugin-next/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@next/mdx": { "version": "13.5.6", "resolved": "https://registry.npmjs.org/@next/mdx/-/mdx-13.5.6.tgz", @@ -1562,6 +1583,163 @@ "node": ">= 8" } }, + "node_modules/@octokit/auth-token": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", + "dev": true, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.0.1.tgz", + "integrity": "sha512-lyeeeZyESFo+ffI801SaBKmCfsvarO+dgV8/0gD8u1d87clbEdWsP5yC+dSj3zLhb2eIf5SJrn6vDz9AheETHw==", + "dev": true, + "dependencies": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.0.0", + "@octokit/request": "^8.0.2", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.2.tgz", + "integrity": "sha512-qhKW8YLIi+Kmc92FQUFGr++DYtkx/1fBv+Thua6baqnjnOsgBYJDCvWZR1YcINuHGOEQt416WOfE+A/oG60NBQ==", + "dev": true, + "dependencies": { + "@octokit/types": "^12.0.0", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz", + "integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==", + "dev": true, + "dependencies": { + "@octokit/request": "^8.0.1", + "@octokit/types": "^12.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.0.2.tgz", + "integrity": "sha512-8li32fUDUeml/ACRp/njCWTsk5t17cfTM1jp9n08pBrqs5cDFJubtjsSnuz56r5Tad6jdEPJld7LxNp9dNcyjQ==", + "dev": true + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.1.2.tgz", + "integrity": "sha512-euDbNV6fxX6btsCDnZoZM4vw3zO1nj1Z7TskHAulO6mZ9lHoFTpwll6farf+wh31mlBabgU81bBYdflp0GLVAQ==", + "dev": true, + "dependencies": { + "@octokit/types": "^12.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=5" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.0.tgz", + "integrity": "sha512-2uJI1COtYCq8Z4yNSnM231TgH50bRkheQ9+aH8TnZanB6QilOnx8RMD2qsnamSOXtDj0ilxvevf5fGsBhBBzKA==", + "dev": true, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=5" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.1.2.tgz", + "integrity": "sha512-JztgZ82CY4JNlPTuF0jh4iWuuGpEi5czFCoXyAbMg4F2XyFBbG5DWAKfa3odRvdZww6Df1tQgBKnqpd9X0WF9g==", + "dev": true, + "dependencies": { + "@octokit/types": "^12.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=5" + } + }, + "node_modules/@octokit/request": { + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.4.tgz", + "integrity": "sha512-M0aaFfpGPEKrg7XoA/gwgRvc9MSXHRO2Ioki1qrPDbl1e9YhjIwVoHE7HIKmv/m3idzldj//xBujcFNqGX6ENA==", + "dev": true, + "dependencies": { + "@octokit/endpoint": "^9.0.0", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz", + "integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==", + "dev": true, + "dependencies": { + "@octokit/types": "^12.0.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest": { + "version": "20.0.2", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.0.2.tgz", + "integrity": "sha512-Ux8NDgEraQ/DMAU1PlAohyfBBXDwhnX2j33Z1nJNziqAfHi70PuxkFYIcIt8aIAxtRE7KVuKp8lSR8pA0J5iOQ==", + "dev": true, + "dependencies": { + "@octokit/core": "^5.0.0", + "@octokit/plugin-paginate-rest": "^9.0.0", + "@octokit/plugin-request-log": "^4.0.0", + "@octokit/plugin-rest-endpoint-methods": "^10.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.1.1.tgz", + "integrity": "sha512-qnJTldJ1NyGT5MTsCg/Zi+y2IFHZ1Jo5+njNCjJ9FcainV7LjuHgmB697kA0g4MjZeDAJsM3B45iqCVsCLVFZg==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^19.0.2" + } + }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", @@ -2464,6 +2642,12 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", + "dev": true + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -2889,6 +3073,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -3947,26 +4137,6 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -4789,6 +4959,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-reference": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", @@ -10410,6 +10589,26 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -11516,6 +11715,12 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/universal-user-agent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", + "dev": true + }, "node_modules/update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", diff --git a/package.json b/package.json index 14b808d4..a9ad9369 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "remark-html": "^16.0.1" }, "devDependencies": { + "@octokit/rest": "^20.0.2", "@trivago/prettier-plugin-sort-imports": "^4.2.1", "@types/lunr": "^2.3.6", "@types/mixpanel-browser": "^2.47.4", diff --git a/scripts/generatePullRequestForNewInitiative.js b/scripts/generatePullRequestForNewInitiative.js index 5604e8af..c4b8dd3f 100644 --- a/scripts/generatePullRequestForNewInitiative.js +++ b/scripts/generatePullRequestForNewInitiative.js @@ -1,7 +1,8 @@ module.exports = async ({github, context}) => { - const fs = require('fs'); - const cp = require('child_process'); + const fs = require("fs").promises; + const path = require("path"); + const cp = require("child_process"); function tryExtractJson(text, jsonStartMarker, jsonEndMarker) { console.log(`Attempting to extract JSON with start marker "${jsonStartMarker}" and end marker "${jsonEndMarker}"` ); @@ -21,7 +22,7 @@ module.exports = async ({github, context}) => { return text.substring(indexOfJsonStart + jsonStartMarker.length, indexOfJsonEnd); } - async function createOrUpdatePullRequest(branch, name) { + async function createOrUpdatePullRequestAsync(branch, name) { const existingResponse = await github.rest.pulls.list({ owner: context.repo.owner, @@ -39,7 +40,7 @@ module.exports = async ({github, context}) => { } const newResponse = await github.rest.pulls.create({ - title: 'New Initiative: ' + name, + title: `New Initiative: ${name} [Suggested by ${process.env.ISSUE_AUTHOR}]`, owner: context.repo.owner, repo: context.repo.repo, head: branch, @@ -51,7 +52,7 @@ module.exports = async ({github, context}) => { return newResponse.data } - async function createComment(body) { + async function createCommentAsync(body) { await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, @@ -60,18 +61,122 @@ module.exports = async ({github, context}) => { }); } - async function warnAndComment(warning, exception, json) { - console.warn(`${warning}: ${exception}`); - await createComment(`WARNING: ${warning} (see GitHub Action logs for more details)\n` + "Automatic PR will NOT be generated:\n" + json); + async function warnAndCommentAsync(warning, exception, json) { + console.warn(`${warning} [${exception}]`); + await createCommentAsync(`**WARNING**: ${warning} + +Automatic PR will NOT be generated +${json} +See GitHub Action logs for more details: ${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`); + } + + function executeShellCommand(cmd, args) { + console.log(`Executing: ${cmd} ${args.join(" ")}`); + + let stdout; + try { + stdout = cp.execFileSync(cmd, args); + } + catch (e) { + console.error(`Command executed with non-zero exit code ${e.status}: ${e.message}`); + console.error(`stdout: ${e.stdout}`); + console.error(`stderr: ${e.stderr}`); + throw e; + } + + console.log(`Command executed successfully with stdout: ${stdout}`); } function executeGitCommand(args) { - console.log(`Executing: git ${args}`); - cp.execFileSync("git", args); + executeShellCommand("git", args); + } + + function pushPrBranch(branch, categoryLinksJsonFile, initiativeName) { + executeGitCommand(["config", "user.name", "github-actions"]); + executeGitCommand(["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"]); //https://github.com/orgs/community/discussions/26560 + executeGitCommand(["checkout", "-b", branch]); + executeGitCommand(["add", categoryLinksJsonFile]); + executeGitCommand(["commit", "-m", initiativeName || "new initiative"]); + executeGitCommand(["push", "origin", branch, "--force"]); + } + + function removeRedundantInitiativeJsonProperties(json) { + console.log("Removing redundant initiative JSON properties") + + //not in our links.json schema, and worse - will interfere with existing initiative detection + delete json.category; + + var redundantValues = new Set([undefined, "", "N/A", "NOTAPPLICABLE", "NOTAVAILABLE", "UNAVAILABLE"]) + for (const prop in json) { + const value = json[prop]; + if (typeof value !== "string") { + continue; + } + + // remove whitespace and capitalize + const PropValueNormalized = value?.replace(/\s/g, "").toLocaleUpperCase("en-us"); + + if (redundantValues.has(PropValueNormalized)) { + console.log(`Removing redundant property '${prop}' with value '${value}'`); + delete json[prop]; + } + } + } + + async function* readdirRecursiveAsync(dir, ext) { + const upperExtension = ext.toLocaleUpperCase("en-us"); + + const dirents = await fs.readdir(dir, { withFileTypes: true }); + for (const dirent of dirents) { + const absolutePath = path.resolve(dir, dirent.name); + + if (dirent.isFile && (path.extname(absolutePath).toLocaleUpperCase("en-us") === upperExtension)) { + yield absolutePath; + } + else if (dirent.isDirectory()) { + yield* await readdirRecursiveAsync(absolutePath, ext) + } + } + } + + async function detectExistingInitiativeAsync(newInitiativeJson) { + console.log("Attempting to detect already-existing initiative"); + + const linksFolder = `${process.env.GITHUB_WORKSPACE}/_data/links`; + for await (const linkJsonFileName of readdirRecursiveAsync(linksFolder, ".json")) { + + console.log(`processing links file: ${linkJsonFileName}`); + const linksJsonString = await fs.readFile(linkJsonFileName, "utf8"); + const upperlinksJsonString = linksJsonString.toLocaleUpperCase("en-us"); + + for (const prop in newInitiativeJson) { + + const value = newInitiativeJson[prop]; + if (typeof value !== "string") { + continue; + } + + const PropValueUpper = value.toLocaleUpperCase("en-us"); + if (upperlinksJsonString.indexOf(PropValueUpper) !== -1) { + await warnAndCommentAsync( +`Initiative might already exist! +The value of property \`${prop}\` (\`${value}\`) is already present in \`${linkJsonFileName}\`: +\`\`\`json +${linksJsonString} +\`\`\` +**If this is a mistake and it doesn't already exist:** edit the issue's title so that it starts with **\`[NEW-INITIATIVE-FORCE-PR]:\`**`, + "Suspected existing initiative", + markdownNewInitiativeJson) + return true; + } + } + } + + return false; } const tempFolder = process.env.TEMP || "/tmp"; - const gptResponse = fs.readFileSync(tempFolder + "/gpt-auto-comment.output", "utf8"); + const gptResponse = await fs.readFile(tempFolder + "/gpt-auto-comment.output", "utf8"); // https://stackoverflow.com/a/51602415/67824 const sanitizedGptResponse = gptResponse.replace(/[\u0000-\u001F\u007F-\u009F]/g, ""); @@ -86,68 +191,56 @@ module.exports = async ({github, context}) => { } console.log("Extracted JSON: " + jsonString); + let newInitiativeJson; try { - json = JSON.parse(jsonString); + newInitiativeJson = JSON.parse(jsonString); } catch (e) { - return await warnAndComment("Could not process GPT response as JSON", e, jsonString); + return await warnAndCommentAsync("Could not process GPT response as JSON", e, jsonString); } - humanReadableJson = "```json\n" + JSON.stringify(json, null, 2) + "\n```"; + const markdownNewInitiativeJson = "```json\n" + JSON.stringify(newInitiativeJson, null, 2) + "\n```"; - let categoryLinksJsonFile, categoryJsonString; + let categoryLinksJsonFile; try { - categoryLinksJsonFile = `${process.env.GITHUB_WORKSPACE}/_data/links/${json.category}/links.json`; + categoryLinksJsonFile = `${process.env.GITHUB_WORKSPACE}/_data/links/${newInitiativeJson.category}/links.json`; console.log("resolved category links file: " + categoryLinksJsonFile); - categoryJsonString = fs.readFileSync(categoryLinksJsonFile, "utf8"); + const categoryJsonString = await fs.readFile(categoryLinksJsonFile, "utf8"); categoryJson = JSON.parse(categoryJsonString); } catch (e) { - return await warnAndComment("Could not process category links JSON", e, humanReadableJson); + return await warnAndCommentAsync("Could not process category links JSON", e, markdownNewInitiativeJson); } - delete json.category //not in our schema, and worse - will interfere with the existing initiative detection below - console.log("Attempting to detect already existing initiative under this category"); + removeRedundantInitiativeJsonProperties(newInitiativeJson); - const upperCategoryJsonString = categoryJsonString.toLocaleUpperCase("en-us"); - for (const prop in json) { - - const value = json[prop]; - if (typeof value !== "string" || value == "") { - continue; - } - - const PropValueUpper = value.toLocaleUpperCase("en-us"); - if (upperCategoryJsonString.indexOf(PropValueUpper) !== -1) { - return await warnAndComment(`Initiative might already exist under this category, the value of property ${prop} is already present in the JSON: ${value}`, "suspected existing initiative", humanReadableJson); - } + if (process.env.ISSUE_TITLE.toLocaleUpperCase("en-us").startsWith("[NEW-INITIATIVE-FORCE-PR]:")) { + console.warn("FORCE-PR requested: skipping existing initiative validation"); } - - categoryJson.links.push(json); - fs.writeFileSync(categoryLinksJsonFile, JSON.stringify(categoryJson, null, 2), "utf8"); + else if (await detectExistingInitiativeAsync(newInitiativeJson)) { + return; + } + + categoryJson.links.push(newInitiativeJson); + await fs.writeFile(categoryLinksJsonFile, JSON.stringify(categoryJson, null, 2), "utf8"); const branch = `auto-pr-${context.issue.number}`; try { - executeGitCommand(["config", "user.name", "github-actions"]) - executeGitCommand(["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"]) //https://github.com/orgs/community/discussions/26560 - executeGitCommand(["checkout", "-b", branch]) - executeGitCommand(["add", categoryLinksJsonFile]) - executeGitCommand(["commit", "-m", json.name || "new initiative"]) - executeGitCommand(["push", "origin", branch, "--force"]) + pushPrBranch(branch, categoryLinksJsonFile, newInitiativeJson.name); } catch (e) { - return await warnAndComment("encountered error during git execution", e, humanReadableJson); + return await warnAndCommentAsync("encountered error during git execution", e, markdownNewInitiativeJson); } let pr; try { - pr = await createOrUpdatePullRequest(branch, json.name || "???"); + pr = await createOrUpdatePullRequestAsync(branch, newInitiativeJson.name || "???"); } catch (e) { - return await warnAndComment("Could not create pull request", e, humanReadableJson); + return await warnAndCommentAsync("Could not create pull request", e, markdownNewInitiativeJson); } console.log("resolved PR: " + JSON.stringify(pr)) - createComment((pr.existing ? "Updated" : "Created") + " PR: " + pr.html_url); + await createCommentAsync((pr.existing ? "Updated" : "Created") + " PR: " + pr.html_url); } diff --git a/scripts/gptGenerateJsonFromIssue.py b/scripts/gptGenerateJsonFromIssue.py index 743bb3af..d99d8d0b 100644 --- a/scripts/gptGenerateJsonFromIssue.py +++ b/scripts/gptGenerateJsonFromIssue.py @@ -107,8 +107,19 @@ def ask_gpt_for_initiative_json(self, issue_body: str) -> str: logging.warn("GPT did not infer function calling, best-effort answer parsing will be performed") return response_message["content"] +def get_new_initiative_details() -> str: + initiative_details = f""" + ### Initiative Display Name + {os.environ["ISSUE_TITLE"].replace("[NEW-INITIATIVE]:", "").replace("[NEW-INITIATIVE-FORCE-PR]:", "")} + + {os.environ["ISSUE_BODY"]} + """ + + # replace double-quotes with single quotes to minimize chance of invalid JSON returned by GPT + return initiative_details.replace('"', "'") + if __name__ == "__main__": - gpt_response = GPTAssistant().ask_gpt_for_initiative_json(os.environ["ISSUE_BODY"]) + gpt_response = GPTAssistant().ask_gpt_for_initiative_json(get_new_initiative_details()) respose_output_file = os.getenv("TEMP", "/tmp") + "/gpt-auto-comment.output" logging.info(f"Writing GPT Response to {respose_output_file}:\n{gpt_response}") diff --git a/scripts/test_generatePullRequestForNewInitiative.js b/scripts/test_generatePullRequestForNewInitiative.js index ac7cfd39..e41f8f97 100644 --- a/scripts/test_generatePullRequestForNewInitiative.js +++ b/scripts/test_generatePullRequestForNewInitiative.js @@ -1,10 +1,12 @@ const context = { + serverUrl: "https://github.com", + runId: 6762019754, repo: { owner: "ohadschn", repo: "ConnectPortal" }, issue: { - number: 13 + number: 10 } }