UNPKG

buddy-bot

Version:

Automated & optimized dependency updates for JavaScript & TypeScript projects. Like Renovate & Dependabot.

86 lines (58 loc) 46.8 kB
// @bun import{f as d,g as r}from"./chunk-50s6gscf.js";import{h as l,i as n}from"./chunk-pms917dv.js";import{j as m}from"./chunk-byfbb805.js";import{l as s}from"./chunk-wmz42t5c.js";import{m as o,q as i}from"./chunk-4331gqf2.js";import{x as P,y as p}from"./chunk-rjfbmp1q.js";import{A as g,B as c}from"./chunk-q3tv2y4q.js";import"./chunk-zmck1ahy.js";import{D as G,H as E}from"./chunk-jcxe2rnh.js";import KK from"fs";import w from"process";class b{generateDashboard(Q,K={}){let{showOpenPRs:X=!0,showDetectedDependencies:$=!0,showDeprecatedDependencies:z=!0,bodyTemplate:Z}=K,O="Dependency Dashboard";if(Z)return{title:"Dependency Dashboard",body:this.applyTemplate(Z,Q)};let W=this.generateDefaultHeader(Q);if(z&&Q.deprecatedDependencies&&Q.deprecatedDependencies.length>0)W+=this.generateDeprecatedDependenciesSection(Q.deprecatedDependencies);if(X&&Q.openPRs.length>0)W+=this.generateOpenPRsSection(Q.openPRs);if($)W+=this.generateDetectedDependenciesSection(Q.detectedDependencies);return W+=this.generateFooter(),{title:"Dependency Dashboard",body:W}}generateDefaultHeader(Q){return`This issue lists Buddy Bot updates and detected dependencies. Read the [Dependency Dashboard](https://buddy-bot.sh/features/dependency-dashboard) docs to learn more. `}generateOpenPRsSection(Q){let K=`## Open The following updates have all been created. To force a retry/rebase of any, click on a checkbox below. `;for(let X of Q){let $=this.extractPackageInfo(X),z=X.head,Z=X.url.includes("/pull/")&&X.url.includes("github.com")?`../pull/${X.number}`:X.url;if(K+=` - [ ] <!-- rebase-branch=${z} -->[${X.title}](${Z})`,$.length>0)K+=` (\`${$.join("`, `")}\`)`;K+=` `}return K+=` - [ ] <!-- rebase-all-open-prs -->**Click on this checkbox to rebase all open PRs at once** `,K+=` `,K}generateDetectedDependenciesSection(Q){let K=`## Detected dependencies `;if(Q.packageJson.length>0)K+=this.generatePackageJsonSection(Q.packageJson);let X=Q.dependencyFiles.filter((O)=>O.path==="composer.json"||O.path.endsWith("/composer.json")),$=X.filter((O)=>O.path==="composer.json"),z=X.filter((O)=>O.path!=="composer.json");if($.length>0)K+=this.generateComposerSection($);if(Q.githubActions.length>0)K+=this.generateGitHubActionsSection(Q.githubActions);let Z=Q.dependencyFiles.filter((O)=>!O.path.endsWith("/composer.json")&&O.path!=="composer.json");if(Z.length>0||z.length>0)K+=this.generateDependencyFilesSection([...Z,...z]);return K}generatePackageJsonSection(Q){let K=`<details><summary>npm</summary> <blockquote> `;for(let X of Q){let $=X.path.split("/").pop()||X.path;K+=`<details><summary>${$}</summary> `;let z={dependencies:X.dependencies.filter((Z)=>Z.type==="dependencies"),devDependencies:X.dependencies.filter((Z)=>Z.type==="devDependencies"),peerDependencies:X.dependencies.filter((Z)=>Z.type==="peerDependencies"),optionalDependencies:X.dependencies.filter((Z)=>Z.type==="optionalDependencies")};for(let[Z,O]of Object.entries(z))if(O.length>0)for(let W of O)K+=` - \`${W.name} ${W.currentVersion}\` `;K+=` </details> `}return K+=`</blockquote> </details> `,K}generateGitHubActionsSection(Q){let K=`<details><summary>github-actions</summary> <blockquote> `;for(let X of Q){K+=`<details><summary>${X.path}</summary> `;let $=new Map;for(let z of X.dependencies){let Z=`${z.name}@${z.currentVersion}`;if(!$.has(Z))$.set(Z,{name:z.name,currentVersion:z.currentVersion})}for(let z of $.values())K+=` - \`${z.name} ${z.currentVersion}\` `;K+=` </details> `}return K+=`</blockquote> </details> `,K}generateDependencyFilesSection(Q){let K=`<details><summary>dependency-files</summary> <blockquote> `;for(let X of Q){K+=`<details><summary>${X.path}</summary> `;for(let $ of X.dependencies)K+=` - \`${$.name} ${$.currentVersion}\` `;K+=` </details> `}return K+=`</blockquote> </details> `,K}generateComposerSection(Q){let K=`<details><summary>composer</summary> <blockquote> `;for(let X of Q){let $=X.path.split("/").pop()||X.path;K+=`<details><summary>${$}</summary> `;let z={require:X.dependencies.filter((Z)=>Z.type==="require"),"require-dev":X.dependencies.filter((Z)=>Z.type==="require-dev")};for(let[Z,O]of Object.entries(z))if(O.length>0)for(let W of O)K+=` - \`${W.name} ${W.currentVersion}\` `;K+=` </details> `}return K+=`</blockquote> </details> `,K}generateDeprecatedDependenciesSection(Q){let K=`> [!WARNING] > These dependencies are deprecated and should be updated to avoid potential security risks and compatibility issues. | Datasource | Name | Replacement PR? | |------------|------|-----------------| `;for(let X of Q){let $=`\`${X.name}\``,Z=(X.replacementAvailable?"available":"unavailable")==="available"?"![available](https://img.shields.io/badge/available-green)":"![unavailable](https://img.shields.io/badge/unavailable-orange)";K+=`| ${X.datasource} | ${$} | ${Z} | `}return K+=` `,K}generateFooter(){return`--- - [ ] <!-- manual job -->Check this box to trigger a request for Buddy Bot to run again on this repository `}extractPackageInfo(Q){let K=[],X=[/update.*?dependency\s+(\S+)/i,/update\s+require(?:-dev)?\s+(\S+)\s+to\s+v?\d+/i,/update\s+(\S+)\s+to\s+v?\d+/i,/bump\s+(\S+)\s+from/i,/chore\(deps\):\s*update\s+dependency\s+(\S+)/i];for(let z of X){let Z=Q.title.match(z);if(Z&&Z[1]&&!K.includes(Z[1]))K.push(Z[1])}let $=[{name:"npm",pattern:/### npm Dependencies[\s\S]*?(?=###|\n\n---|z)/i},{name:"pkgx",pattern:/### Launchpad\/pkgx Dependencies[\s\S]*?(?=###|\n\n---|z)/i},{name:"actions",pattern:/### GitHub Actions[\s\S]*?(?=###|\n\n---|z)/i}];for(let z of $){let Z=Q.body.match(z.pattern);if(Z){let W=Z[0].match(/\|\s*\[([^\]]+)\]\([^)]+\)\s*\|/g);if(W)for(let S of W){let Y=S.match(/\|\s*\[([^\]]+)\]/);if(Y&&Y[1]){let q=Y[1].trim();if(q.includes("`")&&q.includes("->")){let j=S.match(/\]\(([^)]+)\)/);if(j&&j[1]){let I=j[1].match(/\/diffs\/npm\/([^/]+)\//);if(I&&I[1]){let H=decodeURIComponent(I[1]);if(H&&H.length>1&&!K.includes(H))K.push(H)}}continue}if(!q.includes("://")&&!q.includes("Compare Source")&&!q.includes("badge")&&!q.includes("!")&&!q.startsWith("[![")&&!q.includes("`")&&!q.includes("->")&&!q.includes(" -> ")&&!q.match(/^\d+\.\d+/)&&!q.includes(" ")&&q.length>0&&!K.includes(q))K.push(q)}}}}if(K.length<3){let z=Q.body.match(/`([^`]+)`/g);if(z)for(let Z of z){let O=Z.replace(/`/g,"").trim();if(O.includes("->")||O.includes(" -> ")||O.includes("` -> `")||O.match(/^\d+\.\d+/)||O.match(/^v\d+/)||O.match(/[\d.]+\s*->\s*[\d.]+/)||O.match(/^[\d.]+$/)||O.match(/^\d+\.\d+\.\d+/)||O.match(/^\d+\.\d+\.\d+\./)||O.match(/^\^?\d+\.\d+/)||O.match(/^~\d+\.\d+/)||O.includes("://")||O.includes("Compare Source")||O.includes("badge")||O.includes(" "))continue;if(O=O.split(",")[0].trim(),O&&O.length>1&&!K.includes(O)&&(O.startsWith("@")||O.includes("/")||O.match(/^[a-z][a-z0-9.-]*$/i)))K.push(O)}}return K}applyTemplate(Q,K){return Q.replace(/\{\{repository\.owner\}\}/g,K.repository.owner).replace(/\{\{repository\.name\}\}/g,K.repository.name).replace(/\{\{openPRs\.count\}\}/g,K.openPRs.length.toString()).replace(/\{\{lastUpdated\}\}/g,K.lastUpdated.toISOString()).replace(/\{\{detectedDependencies\.packageJson\.count\}\}/g,K.detectedDependencies.packageJson.length.toString()).replace(/\{\{detectedDependencies\.githubActions\.count\}\}/g,K.detectedDependencies.githubActions.length.toString()).replace(/\{\{detectedDependencies\.dependencyFiles\.count\}\}/g,K.detectedDependencies.dependencyFiles.length.toString())}}var{Glob:e}=globalThis.Bun;import{readdir as F,readFile as T,stat as x}from"fs/promises";import{join as M}from"path";class y{projectPath;logger;ignoreGlobs=[];constructor(Q,K,X){this.projectPath=Q;this.logger=K;if(X&&X.length>0)this.ignoreGlobs=X.map(($)=>new e($)),this.logger.info(`Initialized ${this.ignoreGlobs.length} ignore patterns: ${X.join(", ")}`)}async scanProject(){this.logger.info("Scanning project for package files...");let Q=Date.now(),K=[];try{let X=await this.findFiles("package.json");for(let Y of X){if(this.shouldIgnorePath(Y))continue;let q=await this.parsePackageJsonFile(Y);if(q)K.push(q)}let $=await this.findDependencyFiles();for(let Y of $){if(this.shouldIgnorePath(Y))continue;let q=await this.parseDependencyFile(Y);if(q)K.push(q)}let z=await this.findLockFiles();for(let Y of z){if(this.shouldIgnorePath(Y))continue;let q=await this.parseLockFile(Y);if(q)K.push(q)}let Z=await this.findComposerFiles();for(let Y of Z){if(this.shouldIgnorePath(Y))continue;let q=await this.parseComposerFile(Y);if(q)K.push(q)}let O=await this.findGitHubActionsFiles();for(let Y of O){if(this.shouldIgnorePath(Y))continue;let q=await this.parseGitHubActionsFile(Y);if(q)K.push(q)}let W=await this.findDockerfiles();this.logger.info(`\uD83D\uDD0D Found ${W.length} Dockerfile(s): ${W.join(", ")}`);for(let Y of W){if(this.shouldIgnorePath(Y))continue;let q=await this.parseDockerfile(Y);if(q)K.push(q),this.logger.info(`\uD83D\uDCE6 Parsed Dockerfile: ${Y} with ${q.dependencies.length} dependencies`)}let S=Date.now()-Q;return this.logger.success(`Found ${K.length} package files in ${S}ms`),K}catch(X){throw this.logger.error("Failed to scan project:",X),new o(`Failed to scan project: ${X instanceof Error?X.message:"Unknown error"}`)}}async parsePackageJsonFile(Q){try{let K=M(this.projectPath,Q),X=await T(K,"utf-8"),$=JSON.parse(X),z=[];return this.extractDependencies($.dependencies,"dependencies",Q,z),this.extractDependencies($.devDependencies,"devDependencies",Q,z),this.extractDependencies($.peerDependencies,"peerDependencies",Q,z),this.extractDependencies($.optionalDependencies,"optionalDependencies",Q,z),{path:Q,type:"package.json",content:X,dependencies:z}}catch(K){return this.logger.warn(`Failed to parse package.json file ${Q}:`,K),null}}async parseLockFile(Q){try{let K=M(this.projectPath,Q),X=await T(K,"utf-8"),$=Q.split("/").pop()||"",z;if($==="bun.lockb")z="bun.lockb";else if($==="package-lock.json")z="package-lock.json";else if($==="yarn.lock")z="yarn.lock";else if($==="pnpm-lock.yaml")z="pnpm-lock.yaml";else return null;let Z=await this.extractLockFileDependencies(X,z,Q);return{path:Q,type:z,content:X,dependencies:Z}}catch(K){return this.logger.warn(`Failed to parse lock file ${Q}:`,K),null}}async parseDependencyFile(Q){try{let K=M(this.projectPath,Q),X=await T(K,"utf-8");return await c(Q,X)}catch(K){return this.logger.warn(`Failed to parse dependency file ${Q}:`,K),null}}async findDependencyFiles(){let Q=[],K=["deps.yaml","deps.yml","dependencies.yaml","dependencies.yml","pkgx.yaml","pkgx.yml",".deps.yaml",".deps.yml"];for(let z of K){let Z=await this.findFiles(z);Q.push(...Z)}let X=await this.findFilesByPattern("*.yaml"),$=await this.findFilesByPattern("*.yml");for(let z of[...X,...$])if(g(z)&&!Q.includes(z))Q.push(z);return Q}async findGitHubActionsFiles(){let Q=[];try{let X=M(this.projectPath,".github","workflows");if((await x(X).catch(()=>null))?.isDirectory()){let z=await this.findFilesByPatternInDir("*.yaml",X),Z=await this.findFilesByPatternInDir("*.yml",X);for(let O of[...z,...Z])if(d(O))Q.push(O)}}catch{}return Q}async findDockerfiles(){let Q=[];try{let K=["Dockerfile","dockerfile","Dockerfile.dev","Dockerfile.prod","Dockerfile.production","Dockerfile.development","Dockerfile.test","Dockerfile.staging"];for(let $ of K){let z=await this.findFiles($);Q.push(...z)}let X=await this.findFilesByPattern("Dockerfile*");for(let $ of X)if(!Q.includes($)&&l($))Q.push($)}catch{}return Q}async parseGitHubActionsFile(Q){try{let K=M(this.projectPath,Q),X=await T(K,"utf-8");return await r(Q,X)}catch(K){return this.logger.warn(`Failed to parse GitHub Actions file ${Q}:`,K),null}}async parseDockerfile(Q){try{let K=M(this.projectPath,Q),X=await T(K,"utf-8");return await n(Q,X)}catch(K){return this.logger.warn(`Failed to parse Dockerfile ${Q}:`,K),null}}async findComposerFiles(){let Q=[];try{let K=await this.findFiles("composer.json");Q.push(...K);let X=await this.findFiles("composer.lock");Q.push(...X)}catch{}return Q}async parseComposerFile(Q){try{let K=M(this.projectPath,Q),X=await T(K,"utf-8"),{parseComposerFile:$}=await import("./chunk-a37jncfn.js");return await $(Q,X)}catch(K){return this.logger.warn(`Failed to parse Composer file ${Q}:`,K),null}}extractDependencies(Q,K,X,$){if(!Q)return;for(let[z,Z]of Object.entries(Q))$.push({name:z,currentVersion:Z,type:K,file:this.getRelativePath(X)})}async extractLockFileDependencies(Q,K,X){let $=[];if(K==="package-lock.json")try{let z=JSON.parse(Q);if(z.packages){for(let[Z,O]of Object.entries(z.packages))if(Z&&Z!==""&&O&&typeof O==="object"){let W=O;if(W.version){let S=Z.startsWith("node_modules/")?Z.replace("node_modules/",""):Z;$.push({name:S,currentVersion:W.version,type:W.dev?"devDependencies":"dependencies",file:this.getRelativePath(X)})}}}}catch(z){this.logger.warn("Failed to parse package-lock.json:",z)}return $}async findFiles(Q,K=this.projectPath){let X=[];try{let $=await F(K);for(let z of $){let Z=M(K,z);try{let O=await x(Z);if(O.isDirectory()){if(!this.shouldSkipDirectory(z)){let W=await this.findFiles(Q,Z);X.push(...W)}}else if(O.isFile()&&z===Q){let S=E("path").relative(this.projectPath,Z);X.push(S)}}catch{continue}}}catch{}return X}async findLockFiles(){let Q=["bun.lockb","package-lock.json","yarn.lock","pnpm-lock.yaml"],K=[];for(let X of Q){let $=await this.findFiles(X);K.push(...$)}return K}async findFilesByPattern(Q){let K=[],X=Q.replace("*.","");try{let $=await F(this.projectPath);for(let z of $){let Z=M(this.projectPath,z),O=await x(Z);if(O.isFile()&&z.endsWith(`.${X}`)){let S=E("path").relative(this.projectPath,Z);K.push(S)}else if(O.isDirectory()&&!this.shouldSkipDirectory(z)){let W=await this.findFilesByPatternInDir(Q,Z);K.push(...W)}}}catch{}return K}async findFilesByPatternInDir(Q,K){let X=[],$=Q.replace("*.","");try{let z=await F(K);for(let Z of z){let O=M(K,Z),W=await x(O);if(W.isFile()&&Z.endsWith(`.${$}`)){let Y=E("path").relative(this.projectPath,O);X.push(Y)}else if(W.isDirectory()&&!this.shouldSkipDirectory(Z)){let S=await this.findFilesByPatternInDir(Q,O);X.push(...S)}}}catch{}return X}shouldIgnorePath(Q){if(this.ignoreGlobs.length===0)return!1;let K=Q;if(Q.startsWith("/")||Q.includes(":"))K=this.getRelativePath(Q);for(let X of this.ignoreGlobs)if(X.match(K))return this.logger.debug(`Ignoring path: ${K} (matched pattern)`),!0;return!1}shouldSkipDirectory(Q){return["node_modules",".git",".next",".nuxt","dist","build","coverage",".nyc_output","tmp","temp",".cache",".vscode",".idea"].includes(Q)||Q.startsWith(".")}getRelativePath(Q){return E("path").relative(this.projectPath,Q)}async getDependencyCount(){return(await this.scanProject()).reduce((K,X)=>K+X.dependencies.length,0)}async getUniqueDependencies(){return(await this.scanProject()).flatMap(($)=>$.dependencies).filter(($,z,Z)=>z===Z.findIndex((O)=>O.name===$.name))}}class k{async checkDeprecatedDependencies(Q){let K=[];for(let X of Q){let $=await this.checkFileForDeprecatedDependencies(X);K.push(...$)}return K}async checkFileForDeprecatedDependencies(Q){let K=[];for(let X of Q.dependencies){let $=await this.checkDependencyDeprecation(X,Q.type);if($.deprecated)K.push({name:X.name,currentVersion:X.currentVersion,datasource:this.getDatasourceFromFileType(Q.type),file:Q.path,type:X.type,replacementAvailable:!1,deprecationMessage:$.message,suggestedReplacement:$.suggestedReplacement})}return K}async checkDependencyDeprecation(Q,K){try{if(K==="package.json"||K.includes("lock"))return await this.checkNpmDeprecation(Q);else if(K==="composer.json"||K==="composer.lock")return await this.checkComposerDeprecation(Q);else if(K==="deps.yaml"||K==="deps.yml"||K.includes("deps"))return await this.checkBunDeprecation(Q);return{deprecated:!1}}catch(X){return console.warn(`Failed to check deprecation for ${Q.name}:`,X),{deprecated:!1}}}async checkNpmDeprecation(Q){try{let K=await fetch(`https://registry.npmjs.org/${Q.name}`);if(!K.ok)return{deprecated:!1};let X=await K.json();if(X.deprecated)return{deprecated:!0,message:X.deprecated,suggestedReplacement:this.extractSuggestedReplacement(X.deprecated)};let $=X.versions?.[Q.currentVersion];if($?.deprecated)return{deprecated:!0,message:$.deprecated,suggestedReplacement:this.extractSuggestedReplacement($.deprecated)};return{deprecated:!1}}catch(K){return console.warn(`Failed to check npm deprecation for ${Q.name}:`,K),{deprecated:!1}}}async checkComposerDeprecation(Q){try{let K=await fetch(`https://packagist.org/packages/${Q.name}.json`);if(!K.ok)return{deprecated:!1};let X=await K.json();if(X.package?.abandoned){let $=typeof X.package.abandoned==="string"?X.package.abandoned:null;return{deprecated:!0,message:`Package is abandoned${$?`, use ${$} instead`:""}`,suggestedReplacement:$||void 0}}return{deprecated:!1}}catch(K){return console.warn(`Failed to check Composer deprecation for ${Q.name}:`,K),{deprecated:!1}}}async checkBunDeprecation(Q){return await this.checkNpmDeprecation(Q)}extractSuggestedReplacement(Q){let K=[/use\s+([a-z0-9@/-]+)\s+instead/i,/replaced\s+by\s+([a-z0-9@/-]+)/i,/migrate\s+to\s+([a-z0-9@/-]+)/i,/switch\s+to\s+([a-z0-9@/-]+)/i];for(let X of K){let $=Q.match(X);if($)return $[1]}return}getDatasourceFromFileType(Q){if(Q==="package.json"||Q.includes("lock"))return"npm";else if(Q==="composer.json"||Q==="composer.lock")return"composer";else if(Q==="deps.yaml"||Q==="deps.yml"||Q.includes("deps"))return"bun";else if(Q==="github-actions")return"github-actions";return"unknown"}}class R{verbose;constructor(Q=!1){this.verbose=Q}info(Q,...K){console.log(Q,...K)}warn(Q,...K){console.warn(Q,...K)}error(Q,...K){console.error(Q,...K)}debug(Q,...K){if(this.verbose)console.log(`\x1B[90m\uD83D\uDC1B\x1B[0m ${Q}`,...K)}success(Q,...K){console.log(`\x1B[32m\u2713\x1B[0m ${Q}`,...K)}static verbose(){return new R(!0)}static quiet(){return new R(!1)}}class QK{config;projectPath;logger;scanner;registryClient;dashboardGenerator;constructor(Q,K=w.cwd()){this.config=Q;this.projectPath=K;this.logger=new R(Q.verbose??!1),this.scanner=new y(this.projectPath,this.logger,this.config.packages?.ignorePaths),this.registryClient=new i(this.projectPath,this.logger,this.config),this.dashboardGenerator=new b}async scanForUpdates(){let Q=Date.now();this.logger.info("Starting dependency update scan...");try{let K=await this.scanner.scanProject(),X=K.reduce((j,A)=>j+A.dependencies.length,0),$=[];if(this.config.packages?.ignore&&this.config.packages.ignore.length>0)$=(await this.registryClient.getOutdatedPackages()).filter((A)=>!this.config.packages.ignore.includes(A.name));else $=await this.registryClient.getOutdatedPackages();let z=await this.checkDependencyFilesForUpdates(K),Z=await this.checkGitHubActionsForUpdates(K),O=await this.checkDockerfilesForUpdates(K),W=[...$,...z,...Z,...O];if(this.config.packages?.ignore&&this.config.packages.ignore.length>0)W=W.filter((j)=>!this.config.packages.ignore.includes(j.name));if(this.config.packages?.strategy)W=this.filterUpdatesByStrategy(W,this.config.packages.strategy);W=p(W);let S=this.config.packages?.groups?this.groupUpdatesByConfig(W):P(W),Y=Date.now()-Q,q={totalPackages:X,updates:W,groups:S,scannedAt:new Date,duration:Y};return this.logger.success(`Scan completed in ${Y}ms. Found ${W.length} updates.`),q}catch(K){throw this.logger.error("Failed to scan for updates:",K),K}}async createPullRequests(Q){this.logger.info("Creating pull requests for updates...");try{if(!this.config.repository){this.logger.error("\u274C Repository configuration required for PR creation"),this.logger.info("Configure repository.provider, repository.owner, repository.name in buddy-bot.config.ts");return}let K=w.env.BUDDY_BOT_TOKEN||w.env.GITHUB_TOKEN;if(!K){this.logger.error("\u274C GITHUB_TOKEN or BUDDY_BOT_TOKEN environment variable required for PR creation");return}let X=!!w.env.BUDDY_BOT_TOKEN;if(w.env.BUDDY_BOT_TOKEN)console.log("\u2705 BUDDY_BOT_TOKEN detected - workflow permissions enabled"),console.log(`\uD83D\uDD11 Token length: ${w.env.BUDDY_BOT_TOKEN.length} characters`);else console.log("\u26A0\uFE0F BUDDY_BOT_TOKEN not found - workflow permissions disabled"),console.log("\uD83D\uDCA1 Ensure BUDDY_BOT_TOKEN is properly configured in GitHub secrets"),console.log("\uD83D\uDCA1 The workflow should set: env: BUDDY_BOT_TOKEN: ${{ secrets.BUDDY_BOT_TOKEN }}");let $=new m(K,this.config.repository.owner,this.config.repository.name,X),z=new s(this.config);for(let Z of Q.groups)try{this.logger.info(`Creating PR for group: ${Z.name} (${Z.updates.length} updates)`);let O=Z.title,W=await z.generateBody(Z),S=await $.getPullRequests("open"),Y=`buddy-bot/update-${Z.name.toLowerCase().replace(/\s+/g,"-")}-`,q=S.find((_)=>(_.title===O||_.head.startsWith(Y)||this.isSimilarPRTitle(_.title,O))&&(_.author==="github-actions[bot]"||_.author.includes("buddy")||_.head.startsWith("buddy-bot/"))&&!_.head.includes("renovate/")&&!_.head.includes("dependabot/")&&!_.author.toLowerCase().includes("renovate")&&!_.author.toLowerCase().includes("dependabot"));if(q){if(this.logger.info(`\uD83D\uDD04 Found existing PR #${q.number}: ${q.title}`),this.shouldAutoClosePR(q,Z.updates)){this.logger.info(`\uD83D\uDD12 Auto-closing PR #${q.number} due to respectLatest config change`);try{await $.closePullRequest(q.number),await $.deleteBranch(q.head),this.logger.success(`\u2705 Auto-closed PR #${q.number} and deleted branch ${q.head}`);continue}catch(L){this.logger.error(`\u274C Failed to auto-close PR #${q.number}:`,L)}}if(this.checkIfUpdatesMatch(q.body,Z.updates)){this.logger.info("\u2705 Existing PR has the same updates, skipping creation");continue}else{this.logger.info("\uD83D\uDD04 Updates differ, will update existing PR with new content");let L=q.head;try{let{spawn:J}=await import("child_process"),D=(N,t)=>{return new Promise((a,h)=>{let u=J(N,t,{stdio:"pipe"});u.on("close",(f)=>{if(f===0)a();else h(new Error(`Git command failed with code ${f}`))}),u.on("error",h)})};await D("git",["checkout","main"]),await D("git",["reset","--hard","HEAD"]),await D("git",["clean","-fd"]),console.log(`\uD83E\uDDF9 Reset to clean main state before updating existing PR ${q.number}`)}catch(J){console.warn("\u26A0\uFE0F Failed to reset to clean state, continuing anyway:",J)}let B=await this.generateAllFileUpdates(Z.updates);if(B.length===0)this.logger.warn(`\u2139\uFE0F No file changes generated for existing PR ${q.number}, updating metadata only`);else{this.logger.info(`\uD83D\uDCDD Regenerated ${B.length} file changes for existing PR ${q.number}`);try{let{hasBranchDifferences:J}=await import("./chunk-j03xcvv3.js");if(!await J(B,L))this.logger.info(`\u2139\uFE0F No content differences for ${L}; skipping commit`);else await $.commitChanges(L,`${Z.title} (updated)`,B),this.logger.success(`\u2705 Updated files in branch ${L} with latest dependency versions`)}catch(J){this.logger.warn("\u26A0\uFE0F Failed to compare branch content, proceeding with commit:",J),await $.commitChanges(L,`${Z.title} (updated)`,B),this.logger.success(`\u2705 Updated files in branch ${L} with latest dependency versions`)}}let U=z.generateLabels(Z);await $.updatePullRequest(q.number,{title:O,body:W,labels:U,reviewers:this.config.pullRequest?.reviewers,assignees:this.config.pullRequest?.assignees}),this.logger.success(`\u2705 Updated existing PR #${q.number}: ${O}`),this.logger.info(`\uD83D\uDD17 ${q.url}`);continue}}let j=Date.now(),A=`buddy-bot/update-${Z.name.toLowerCase().replace(/\s+/g,"-")}-${j}`;await $.createBranch(A,this.config.repository.baseBranch||"main");try{let{spawn:_}=await import("child_process"),V=(L,B)=>{return new Promise((U,J)=>{let D=_(L,B,{stdio:"pipe"});D.on("close",(N)=>{if(N===0)U();else J(new Error(`Git command failed with code ${N}`))}),D.on("error",J)})};await V("git",["checkout","main"]),await V("git",["reset","--hard","HEAD"]),await V("git",["clean","-fd"]),console.log(`\uD83E\uDDF9 Reset to clean main state before generating updates for ${Z.name}`)}catch(_){console.warn("\u26A0\uFE0F Failed to reset to clean state, continuing anyway:",_)}let I=await this.generateAllFileUpdates(Z.updates);if(I.length===0){this.logger.warn(`\u2139\uFE0F No file changes generated for group ${Z.name}, skipping PR creation`);continue}let H=!1;for(let _ of I)try{let V=await import("fs");if(V.existsSync(_.path)){if(V.readFileSync(_.path,"utf-8")!==_.content){H=!0;break}}else{H=!0;break}}catch{H=!0;break}if(!H){this.logger.warn(`\u2139\uFE0F No actual content changes for group ${Z.name}, skipping PR creation`);continue}this.logger.info(`\uD83D\uDCDD Generated ${I.length} file changes for ${Z.name}`),await $.commitChanges(A,Z.title,I);let v=z.generateLabels(Z),C=await $.createPullRequest({title:O,body:W,head:A,base:this.config.repository.baseBranch||"main",draft:!1,reviewers:this.config.pullRequest?.reviewers,assignees:this.config.pullRequest?.assignees,labels:v});this.logger.success(`\u2705 Created PR #${C.number}: ${C.title}`),this.logger.info(`\uD83D\uDD17 ${C.url}`)}catch(O){this.logger.error(`\u274C Failed to create PR for group ${Z.name}:`,O)}this.logger.success(`\u2705 Completed PR creation for ${Q.groups.length} group(s)`)}catch(K){throw this.logger.error("Failed to create pull requests:",K),K}}shouldRespectVersion(Q){if(!(this.config.packages?.respectLatest??!0))return!1;let X=["latest","*","main","master","develop","dev"],$=Q.toLowerCase().trim();return X.includes($)}async checkDependencyFilesForUpdates(Q){let{isDependencyFile:K}=await import("./chunk-q3tv2y4q.js"),{resolveDependencyFile:X}=await import("./chunk-zmck1ahy.js"),$=[],z=Q.filter((Z)=>K(Z.path));for(let Z of z)try{this.logger.info(`Checking dependency file: ${Z.path}`);let O=await X(Z.path);for(let W of O.allDependencies||[]){if(this.shouldRespectVersion(W.constraint)){this.logger.debug(`Skipping ${W.name} - version "${W.constraint}" should be respected`);continue}if(W.constraint!==W.version&&W.version){let S=W.constraint.replace(/^[\^~>=<]+/,"");if(!this.isNewerVersion(S,W.version)){this.logger.debug(`Skipping ${W.name} - latest (${W.version}) is not newer than constraint (${S})`);continue}let Y=this.getUpdateType(S,W.version),q=W.version;$.push({name:W.name,currentVersion:W.constraint,newVersion:q,updateType:Y,dependencyType:"dependencies",file:Z.path,metadata:void 0,releaseNotesUrl:void 0,changelogUrl:void 0,homepage:void 0})}}}catch(O){this.logger.error(`Failed to check dependency file ${Z.path}:`,O)}return $}async checkGitHubActionsForUpdates(Q){let{isGitHubActionsFile:K}=await import("./chunk-50s6gscf.js"),{fetchLatestActionVersion:X}=await import("./chunk-50s6gscf.js"),$=[],z=Q.filter((O)=>K(O.path));this.logger.info(`\uD83D\uDD0D Found ${z.length} GitHub Actions workflow files`);for(let O of z)try{this.logger.info(`Checking GitHub Actions file: ${O.path}`);let W=O.dependencies.filter((S)=>S.type==="github-actions");this.logger.info(`Found ${W.length} GitHub Actions in ${O.path}`);for(let S of W)try{this.logger.info(`Checking action: ${S.name}@${S.currentVersion}`);let Y=await X(S.name);if(Y)if(this.logger.info(`Latest version for ${S.name}: ${Y}`),Y!==S.currentVersion){let q=this.getUpdateType(S.currentVersion,Y);this.logger.info(`Update available: ${S.name} ${S.currentVersion} \u2192 ${Y} (${q})`),$.push({name:S.name,currentVersion:S.currentVersion,newVersion:Y,updateType:q,dependencyType:"github-actions",file:O.path,metadata:void 0,releaseNotesUrl:`https://github.com/${S.name}/releases`,changelogUrl:void 0,homepage:`https://github.com/${S.name}`})}else this.logger.info(`No update needed for ${S.name}: already at ${Y}`);else this.logger.warn(`Could not fetch latest version for ${S.name}`)}catch(Y){this.logger.warn(`Failed to check version for action ${S.name}:`,Y)}}catch(W){this.logger.error(`Failed to check GitHub Actions file ${O.path}:`,W)}this.logger.info(`Generated ${$.length} GitHub Actions updates`);let Z=$.reduce((O,W)=>{if(!O.find((Y)=>Y.name===W.name&&Y.currentVersion===W.currentVersion&&Y.newVersion===W.newVersion&&Y.file===W.file))O.push(W);return O},[]);return this.logger.info(`After deduplication: ${Z.length} unique GitHub Actions updates`),Z}async checkDockerfilesForUpdates(Q){let{isDockerfile:K}=await import("./chunk-pms917dv.js"),{fetchLatestDockerImageVersion:X}=await import("./chunk-pms917dv.js"),$=[],z=Q.filter((O)=>K(O.path));this.logger.info(`\uD83D\uDD0D Found ${z.length} Dockerfile(s)`);for(let O of z)try{this.logger.info(`Checking Dockerfile: ${O.path}`);let W=O.dependencies.filter((S)=>S.type==="docker-image");this.logger.info(`Found ${W.length} Docker images in ${O.path}`);for(let S of W)try{if(this.logger.info(`Checking Docker image: ${S.name}:${S.currentVersion}`),this.shouldRespectVersion(S.currentVersion)){this.logger.debug(`Skipping ${S.name} - version "${S.currentVersion}" should be respected`);continue}let Y=await X(S.name);if(Y)if(this.logger.info(`Latest version for ${S.name}: ${Y}`),Y!==S.currentVersion){let q=this.getUpdateType(S.currentVersion,Y);this.logger.info(`Update available: ${S.name} ${S.currentVersion} \u2192 ${Y} (${q})`),$.push({name:S.name,currentVersion:S.currentVersion,newVersion:Y,updateType:q,dependencyType:"docker-image",file:O.path,metadata:void 0,releaseNotesUrl:`https://hub.docker.com/r/${S.name}/tags`,changelogUrl:void 0,homepage:`https://hub.docker.com/r/${S.name}`})}else this.logger.info(`No update needed for ${S.name}: already at ${Y}`);else this.logger.warn(`Could not fetch latest version for Docker image ${S.name}`)}catch(Y){this.logger.warn(`Failed to check version for Docker image ${S.name}:`,Y)}}catch(W){this.logger.error(`Failed to check Dockerfile ${O.path}:`,W)}this.logger.info(`Generated ${$.length} Docker image updates`);let Z=$.reduce((O,W)=>{if(!O.find((Y)=>Y.name===W.name&&Y.currentVersion===W.currentVersion&&Y.newVersion===W.newVersion&&Y.file===W.file))O.push(W);return O},[]);return this.logger.info(`After deduplication: ${Z.length} unique Docker image updates`),Z}getUpdateType(Q,K){try{let X=Q.replace(/^[v^~>=<@]+/,""),$=K.replace(/^[v^~>=<@]+/,""),z=(W)=>{let S=W.split(".");while(S.length<3)S.push("0");return S.join(".")};if(X=z(X),$=z($),Bun.semver.order($,X)<=0)return"patch";let Z=X.split(".").map(Number),O=$.split(".").map(Number);if(O[0]>Z[0])return"major";if(O[0]===Z[0]&&O[1]>Z[1])return"minor";return"patch"}catch{return"patch"}}isNewerVersion(Q,K){try{return Bun.semver.order(K.replace(/^[v^~>=<@]+/,""),Q.replace(/^[v^~>=<@]+/,""))>0}catch{return!1}}async generateAllFileUpdates(Q){let K=[],X=Q.filter((S)=>S.file.endsWith("package.json")&&!S.file.includes(".yaml")&&!S.file.includes(".yml")&&!S.file.includes(".github/workflows/")),$=new Map;for(let S of X){if(!$.has(S.file))$.set(S.file,[]);$.get(S.file).push(S)}for(let[S,Y]of $)try{let q=KK.readFileSync(S,"utf-8"),j=JSON.parse(q);for(let A of Y){let I=!1,H=A.name.replace(/\s*\(dev\)$/,"").replace(/\s*\(peer\)$/,"").replace(/\s*\(optional\)$/,""),v=["dependencies","devDependencies","peerDependencies","optionalDependencies"];for(let C of v)if(j[C]&&j[C][H]){let V=j[C][H].match(/^(\D*)/),L=V?V[1]:"",B=H.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),U=C.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),J=new RegExp(`("${U}"\\s*:\\s*\\{[^}]*?)("${B}"\\s*:\\s*")([^"]+)(")([^}]*?\\})`,"gs"),D=`${L}${A.newVersion}`;q=q.replace(J,`$1$2${D}$4$5`),I=!0;break}if(!I)console.warn(`Package ${H} not found in ${S}`)}K.push({path:S,content:q,type:"update"})}catch(q){console.warn(`Failed to update ${S}:`,q)}let z=Q.filter((S)=>{let Y=S.file.toLowerCase();return(Y.endsWith("deps.yaml")||Y.endsWith("deps.yml")||Y.endsWith("dependencies.yaml")||Y.endsWith("dependencies.yml"))&&!S.file.includes(".github/workflows/")});if(z.length>0)try{let{generateDependencyFileUpdates:S}=await import("./chunk-q3tv2y4q.js"),Y=await S(z);K.push(...Y)}catch(S){this.logger.error("Failed to generate dependency file updates:",S)}let Z=Q.filter((S)=>S.file.endsWith("composer.json")||S.file.endsWith("composer.lock"));if(Z.length>0)try{let{generateComposerUpdates:S}=await import("./chunk-a37jncfn.js"),Y=await S(Z);K.push(...Y)}catch(S){this.logger.error("Failed to generate Composer updates:",S)}let O=Q.filter((S)=>S.file.includes(".github/workflows/"));if(O.length>0)try{let{generateGitHubActionsUpdates:S}=await import("./chunk-50s6gscf.js"),Y=await S(O);K.push(...Y)}catch(S){this.logger.error("Failed to generate GitHub Actions updates:",S)}let W=Q.filter((S)=>S.dependencyType==="docker-image");if(W.length>0)try{let{generateDockerfileUpdates:S}=await import("./chunk-pms917dv.js"),Y=await S(W);K.push(...Y)}catch(S){this.logger.error("Failed to generate Dockerfile updates:",S)}return K}async run(){let Q=await this.scanForUpdates();if(Q.updates.length===0)return this.logger.info("No updates available!"),Q;if(this.config.pullRequest)await this.createPullRequests(Q);return Q}async checkPackages(Q){return this.logger.info(`Checking specific packages: ${Q.join(", ")}`),this.registryClient.getUpdatesForPackages(Q)}async checkPackagesWithPattern(Q){return this.logger.info(`Checking packages with pattern: ${Q}`),this.registryClient.getUpdatesWithPattern(Q)}filterUpdatesByStrategy(Q,K){if(K==="all")return Q;return Q.filter((X)=>{switch(K){case"major":return X.updateType==="major";case"minor":return X.updateType==="major"||X.updateType==="minor";case"patch":return!0;default:return!0}})}groupUpdatesByConfig(Q){let K=[],X=[...Q];if(this.config.packages?.groups)for(let $ of this.config.packages.groups){let z=[];for(let Z of $.patterns){let O=new RegExp(Z.replace("*",".*")),W=X.filter((S)=>O.test(S.name));z.push(...W),W.forEach((S)=>{let Y=X.indexOf(S);if(Y>-1)X.splice(Y,1)})}if(z.length>0){let Z=z;if($.strategy)Z=this.filterUpdatesByStrategy(z,$.strategy);K.push({name:$.name,updates:Z,updateType:this.getHighestUpdateType(Z),title:`chore(deps): update ${$.name}`,body:`Update ${Z.length} packages in ${$.name} group`})}}if(X.length>0){let $=P(X);K.push(...$)}return K}getHighestUpdateType(Q){if(Q.some((K)=>K.updateType==="major"))return"major";if(Q.some((K)=>K.updateType==="minor"))return"minor";return"patch"}isSimilarPRTitle(Q,K){if(Q.toLowerCase()===K.toLowerCase())return!0;if(K.toLowerCase().includes("update dependency ")){let z=K.match(/update dependency (\S+)/i),Z=Q.match(/update dependency (\S+)/i);if(z&&Z)return z[1]===Z[1]}let X=Q.toLowerCase(),$=K.toLowerCase();if(X.includes("all non-major")&&$.includes("dependency ")||$.includes("all non-major")&&X.includes("dependency "))return!1;if(X.includes("dependency ")&&$.includes("dependency "))return!1;return!1}checkIfUpdatesMatch(Q,K){if(!Q)return!1;let X=/([\w@\-./]+):\s*(\d+\.\d+\.\d\S*)\s*\u2192\s*(\d+\.\d+\.\d\S*)/g,$=new Map,z;while((z=X.exec(Q))!==null){let[,Z,O,W]=z;$.set(Z,{from:O,to:W})}for(let Z of K){let O=$.get(Z.name);if(!O||O.to!==Z.newVersion)return!1}return $.size===K.length}generatePRLabels(Q){let K=new Set;K.add("dependencies");let X=Q.updates.map((O)=>O.updateType),$={major:X.includes("major"),minor:X.includes("minor"),patch:X.includes("patch")};if($.major)K.add("major");if($.minor)K.add("minor");if($.patch)K.add("patch");if(Q.updates.length>5)K.add("dependencies");let z=["helmet","express-rate-limit","cors","bcrypt","jsonwebtoken"];if(Q.updates.some((O)=>z.some((W)=>O.name.includes(W))))K.add("security");if(this.config.pullRequest?.labels)this.config.pullRequest.labels.forEach((O)=>{if(O!=="dependencies")K.add(O)});return Array.from(K)}shouldAutoClosePR(Q,K){if(this.shouldAutoCloseForRespectLatest(Q))return!0;if(this.shouldAutoCloseForIgnorePaths(Q))return!0;if(this.shouldAutoCloseForRemovedFiles(Q))return!0;return!1}shouldAutoCloseForRespectLatest(Q){if(!(this.config.packages?.respectLatest??!0))return!1;let X=["latest","*","main","master","develop","dev"],$=Q.body.toLowerCase();if(!X.some((W)=>$.includes(W.toLowerCase())))return!1;return this.extractPackagesFromPRBody(Q.body).filter((W)=>{let S=new RegExp(`\\|\\s*\\[${W.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}\\]\\([^)]+\\)\\s*\\|\\s*([^|]+)\\s*\\|`,"i"),Y=Q.body.match(S);if(!Y){let I=new RegExp(`${W.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}[^\\w]*[:=]\\s*["']?([^"' ]+)["']?`,"i"),H=Q.body.match(I);if(!H)return!1;let v=H[1].toLowerCase().trim();return X.includes(v)}let j=Y[1].trim().match(/^([^\u2192]+)\u2192/);if(!j)return!1;let A=j[1].trim().toLowerCase();return X.includes(A)}).length>0}shouldAutoCloseForIgnorePaths(Q){let K=this.config.packages?.ignorePaths;if(!K||K.length===0)return!1;let X=this.extractFilePathsFromPRBody(Q.body);if(X.length===0)return!1;let{Glob:$}=globalThis.Bun,z=X.filter((Z)=>{let O=Z.replace(/^\.\//,"");return K.some((W)=>{try{return new $(W).match(O)}catch(S){return this.logger.debug(`Failed to match path ${O} against pattern ${W}: ${S}`),!1}})});if(z.length>0)return this.logger.debug(`PR #${Q.number} contains files now in ignorePaths: ${z.join(", ")}`),!0;return!1}shouldAutoCloseForRemovedFiles(Q){try{let K=this.extractFilePathsFromPRBody(Q.body);if(K.length===0)return!1;let X=E("fs"),$=E("path"),z=K.filter((Z)=>{let O=$.join(this.projectPath,Z);return!X.existsSync(O)});if(z.length>0){if(this.logger.info(`PR #${Q.number} references removed files: ${z.join(", ")}`),this.config.packages?.ignorePaths&&this.config.packages.ignorePaths.length>0){let{Glob:q}=globalThis.Bun;if(z.filter((A)=>{return!this.config.packages.ignorePaths.some((I)=>{try{return new q(I).match(A)}catch(H){return this.logger.warn(`Invalid glob pattern '${I}':`,H),!1}})}).length>0)return this.logger.info(`Some removed files are outside ignored paths - not auto-closing PR #${Q.number}`),!1}if(z.some((q)=>q.endsWith("composer.json")))return this.logger.info(`composer.json was removed - PR #${Q.number} should be auto-closed`),!0;let O=Q.body.toLowerCase(),W=O.includes("composer")||z.some((q)=>q.includes("composer")),S=O.includes("package.json")||z.some((q)=>q.includes("package.json")),Y=z.some((q)=>q.endsWith("deps.yaml")||q.endsWith("deps.yml")||q.endsWith("dependencies.yaml")||q.endsWith("dependencies.yml"));if(W||S||Y)return this.logger.info(`PR #${Q.number} is about removed dependency system - should be auto-closed`),!0}return!1}catch(K){return this.logger.debug(`Failed to check for removed files in PR #${Q.number}: ${K}`),!1}}extractFilePathsFromPRBody(Q){let K=[],X=/\|\s*\[[^\]]+\]\([^)]*\)\s*\|[^|]*\|\s*\*\*([^*]+)\*\*\s*\|/g,$;while(($=X.exec(Q))!==null){let W=$[1].trim();if(W&&!K.includes(W))K.push(W)}let z=/\*\*([^*]+\.(?:json|yaml|yml|lock))\*\*/g;while(($=z.exec(Q))!==null){let W=$[1].trim();if(W&&!K.includes(W))K.push(W)}let Z=/\|[^|]+\|[^|]+\|([^|]+)\|[^|]*\|/g;while(($=Z.exec(Q))!==null){let W=$[1].trim();if(W&&(W.includes("/")||/\.(?:json|yaml|yml|lock)$/.test(W))&&!K.includes(W))K.push(W)}let O=/(?:^|\s)([\w-]+(?:\/[\w.-]+)*\/[\w.-]+\.(?:json|yaml|yml|lock))(?:\s|$)/gm;while(($=O.exec(Q))!==null){let W=$[1].trim();if(W&&!K.includes(W))K.push(W)}return K}extractPackagesFromPRBody(Q){let K=[],X=Q.match(/\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|/g);if(X)for(let z of X){let Z=z.match(/\[([^\]]+)\]\([^)]*\)/);if(Z)K.push(Z[1])}let $=Q.match(/<summary>([^<]+)<\/summary>/g);if($)for(let z of $){let Z=z.replace(/<summary>/,"").replace(/<\/summary>/,"").trim();if(Z&&!K.includes(Z))K.push(Z)}return K}getConfig(){return this.config}getProjectPath(){return this.projectPath}async checkAndCloseObsoletePRs(Q,K=!1){try{this.logger.info("\uD83D\uDD0D Scanning for obsolete PRs due to removed dependency files...");let $=(await Q.getPullRequests("open")).filter((Z)=>Z.head.startsWith("buddy-bot/")||Z.author==="github-actions[bot]"||Z.labels.includes("dependencies")||Z.labels.includes("dependency")||Z.title.toLowerCase().includes("update")||Z.title.toLowerCase().includes("chore(deps)")||Z.title.toLowerCase().includes("composer"));this.logger.info(`Found ${$.length} dependency-related PRs to check`);let z=0;for(let Z of $)try{if(this.shouldAutoCloseForRemovedFiles(Z))if(this.logger.info(`\uD83D\uDD12 PR #${Z.number} should be auto-closed: ${Z.title}`),K)this.logger.info(`\uD83D\uDD0D [DRY RUN] Would auto-close PR #${Z.number}`),z++;else try{let W=this.generateCloseReason(Z);try{await Q.createComment(Z.number,W)}catch(S){this.logger.warn(`\u26A0\uFE0F Could not add close reason comment to PR #${Z.number}:`,S)}if(await Q.closePullRequest(Z.number),Z.head.startsWith("buddy-bot/"))try{await Q.deleteBranch(Z.head),this.logger.success(`\u2705 Auto-closed PR #${Z.number} and deleted branch ${Z.head}`)}catch(S){this.logger.warn(`\u26A0\uFE0F Auto-closed PR #${Z.number} but failed to delete branch: ${S}`)}else this.logger.success(`\u2705 Auto-closed PR #${Z.number}`);z++}catch(W){this.logger.error(`\u274C Failed to auto-close PR #${Z.number}:`,W)}}catch(O){this.logger.warn(`\u26A0\uFE0F Error checking PR #${Z.number}:`,O)}if(z>0)this.logger.success(`\u2705 ${K?"Would auto-close":"Auto-closed"} ${z} obsolete PR(s)`);else this.logger.info("\uD83D\uDCCB No obsolete PRs found")}catch(X){throw this.logger.error("Failed to check for obsolete PRs:",X),X}}generateCloseReason(Q){let X=this.extractFilePathsFromPRBody(Q.body).filter((W)=>{let S=E("fs"),q=E("path").join(this.projectPath,W);return!S.existsSync(q)}),$=X.some((W)=>W.includes("composer")),z=X.some((W)=>W.includes("package.json")),Z=X.some((W)=>W.endsWith("deps.yaml")||W.endsWith("deps.yml")||W.endsWith("dependencies.yaml")||W.endsWith("dependencies.yml")),O=`\uD83E\uDD16 **Auto-closing obsolete PR** `;if($)O+="This PR was automatically closed because `composer.json` has been removed from the project, indicating that Composer is no longer used for dependency management.\n\n";else if(z)O+="This PR was automatically closed because `package.json` has been removed from the project, indicating that npm/yarn/pnpm is no longer used for dependency management.\n\n";else if(Z)O+=`This PR was automatically closed because the dependency files it references have been removed from the project. `;else O+=`This PR was automatically closed because the dependency files it references are no longer present in the project. `;if(X.length>0)O+=`**Removed files:** ${X.map((W)=>`- \`${W}\``).join(` `)} `;return O+="If this was closed in error, please reopen the PR and update the dependency files accordingly.",O}async createOrUpdateDashboard(){try{if(this.logger.info("Creating or updating dependency dashboard..."),!this.config.repository)throw new Error("Repository configuration is required for dashboard");if(this.config.repository.provider!=="github")throw new Error("Dashboard is currently only supported for GitHub repositories");let Q=this.config.repository.token||w.env.BUDDY_BOT_TOKEN||w.env.GITHUB_TOKEN||"",K=!!w.env.BUDDY_BOT_TOKEN,X=new m(Q,this.config.repository.owner,this.config.repository.name,K),$=await this.collectDashboardData(X),z=this.config.dashboard||{},{title:Z,body:O}=this.dashboardGenerator.generateDashboard($,{showOpenPRs:z.showOpenPRs??!0,showDetectedDependencies:z.showDetectedDependencies??!0,showDeprecatedDependencies:z.showDeprecatedDependencies??!0,bodyTemplate:z.bodyTemplate}),W=await this.findExistingDashboard(X,z.issueNumber),S;if(W)this.logger.info(`Updating existing dashboard issue #${W.number}`),S=await X.updateIssue(W.number,{title:z.title||Z,body:O,labels:z.labels||["dependencies","dashboard"],assignees:z.assignees}),this.logger.success(`\u2705 Successfully updated dashboard issue #${S.number}`);else{this.logger.info("Creating new dashboard issue"),this.logger.info("Performing final check for existing dashboards before creation...");let Y=await this.findExistingDashboard(X,z.issueNumber);if(Y)this.logger.info(`Race condition detected! Found existing dashboard #${Y.number} during final check`),S=await X.updateIssue(Y.number,{title:z.title||Z,body:O,labels:z.labels||["dependencies","dashboard"],assignees:z.assignees}),this.logger.success(`\u2705 Updated existing dashboard issue #${S.number} (race condition avoided)`);else S=await X.createIssue({title:z.title||Z,body:O,labels:z.labels||["dependencies","dashboard"],assignees:z.assignees}),this.logger.success(`\u2705 Successfully created new dashboard issue #${S.number}`)}return this.logger.success(`\u2705 Dashboard updated: ${S.url}`),S}catch(Q){throw this.logger.error("Failed to create or update dashboard:",Q),Q}}async collectDashboardData(Q){let[K,X]=await Promise.all([this.scanner.scanProject(),Q.getPullRequests("open")]),$=X.filter((Y)=>Y.labels.includes("dependencies")||Y.labels.includes("dependency")||Y.labels.includes("deps")||Y.title.toLowerCase().includes("update")||Y.title.toLowerCase().includes("chore(deps)")||Y.title.toLowerCase().includes("bump")||Y.title.toLowerCase().includes("upgrade")||Y.title.toLowerCase().includes("renovate")||Y.head.includes("renovate/")||Y.head.includes("dependabot/")||Y.head.includes("buddy-bot/")||Y.head.includes("update-")||Y.head.includes("bump-")),z=K.filter((Y)=>Y.type==="package.json"),Z=K.filter((Y)=>Y.path.includes(".github/workflows/")&&(Y.path.endsWith(".yml")||Y.path.endsWith(".yaml"))),O=K.filter((Y)=>!Y.path.includes(".github/workflows/")&&Y.type!=="package.json"),S=await new k().checkDeprecatedDependencies(K);return{openPRs:$,detectedDependencies:{packageJson:z,dependencyFiles:O,githubActions:Z},deprecatedDependencies:S,repository:{owner:this.config.repository.owner,name:this.config.repository.name,provider:this.config.repository.provider},lastUpdated:new Date}}async findExistingDashboard(Q,K){try{if(this.logger.info("Searching for existing dashboard issue..."),K){this.logger.info(`Looking for specific dashboard issue #${K}`);let W=(await Q.getIssues("open")).find((S)=>S.number===K);if(W)return this.logger.info(`Found specified dashboard issue #${W.number}: ${W.title}`),W;else return this.logger.warn(`Specified dashboard issue #${K} not found`),null}let X=await Q.getIssues("open");this.logger.info(`Found ${X.length} open issues to search through`);for(let O of X){let W=O.labels.includes("dashboard")&&O.labels.includes("dependencies"),S=O.title.toLowerCase().includes("dependency dashboard"),Y=O.body.includes("This issue lists Buddy Bot updates and detected dependencies");if(W&&(S||Y))return this.logger.info(`Found existing dashboard issue #${O.number}: ${O.title}`),this.logger.info(` - Labels: ${O.labels.join(", ")}`),this.logger.info(` - Title matches: ${S}`),this.logger.info(` - Body has marker: ${Y}`),O}let $=X.filter((O)=>O.labels.includes("dashboard")),z=X.filter((O)=>O.labels.includes("dependencies")),Z=X.filter((O)=>O.title.toLowerCase().includes("dependency dashboard"));if(this.logger.info("Dashboard search results:"),this.logger.info(` - Issues with 'dashboard' label: ${$.length}`),this.logger.info(` - Issues with 'dependencies' label: ${z.length}`),this.logger.info(` - Issues with 'dependency dashboard' in title: ${Z.length}`),$.length>0){this.logger.info("Issues with 'dashboard' label:");for(let O of $)this.logger.info(` - #${O.number}: ${O.title} (labels: ${O.labels.join(", ")})`)}return this.logger.info("No existing dashboard issue found"),null}catch(X){return this.logger.warn(`Failed to search for existing dashboard: ${X}`),null}}}export{QK as Buddy}; export{y as c,R as d,QK as e}; //# debugId=20B2A8913623F18664756E2164756E21