[Self-Heal] Add self-scheduling auto-repair workflow#52
Conversation
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
There was a problem hiding this comment.
Code Review
This pull request introduces an automated self-healing CI pipeline to address code drift, keep dependencies updated, format code, and sync types. It adds documentation, a telemetry-based scheduling script, a healthcheck script, and a main self-healing pipeline script, along with ESLint and Prettier configurations. The code review identified several key areas for improvement: the ESLint configuration currently enables no rules, rendering linting ineffective; the healthcheck evaluation in self_heal.mjs should ignore untracked files to prevent false positives; the schedule updater should verify regex matches before writing to prevent silent failures; and redundant || true command suffixes should be removed to preserve error logging.
| export default [ | ||
| { | ||
| ignores: [ | ||
| "build/**", | ||
| "dist/**", | ||
| "coverage/**", | ||
| "bin/**", | ||
| "node_modules/**" | ||
| ] | ||
| }, | ||
| { | ||
| languageOptions: { | ||
| globals: { | ||
| console: "readonly", | ||
| process: "readonly", | ||
| setTimeout: "readonly", | ||
| clearTimeout: "readonly", | ||
| setInterval: "readonly", | ||
| clearInterval: "readonly", | ||
| __dirname: "readonly", | ||
| __filename: "readonly", | ||
| module: "readonly", | ||
| require: "readonly", | ||
| exports: "readonly", | ||
| Buffer: "readonly" | ||
| } | ||
| } | ||
| } | ||
| ]; |
There was a problem hiding this comment.
The current ESLint configuration does not enable any rules or extend any recommended configurations. As a result, ESLint will run with zero rules enabled, making npx eslint --fix a no-op and rendering the linting step in the self-healing pipeline ineffective.
To fix this, import @eslint/js and extend js.configs.recommended (or your preferred base configuration). Note that you may also want to add @eslint/js to your devDependencies in package.json to ensure it is explicitly tracked.
import js from "@eslint/js";
export default [
{
ignores: [
"build/**",
"dist/**",
"coverage/**",
"bin/**",
"node_modules/**"
]
},
js.configs.recommended,
{
languageOptions: {
globals: {
console: "readonly",
process: "readonly",
setTimeout: "readonly",
clearTimeout: "readonly",
setInterval: "readonly",
clearInterval: "readonly",
__dirname: "readonly",
__filename: "readonly",
module: "readonly",
require: "readonly",
exports: "readonly",
Buffer: "readonly"
}
}
}
];| function hasDiff() { | ||
| const diff = execSync('git status --porcelain').toString().trim(); | ||
| return diff.length > 0; | ||
| } |
There was a problem hiding this comment.
Using git status --porcelain to detect changes will include any untracked files (such as log files like pre-check.log, repair.log, post-check.log mentioned in the PR description, or temporary build artifacts) that are not explicitly ignored in .gitignore. This can lead to false positives where hasDiff() returns true even if no actual code repairs or modifications were made, causing the script to exit prematurely with 0.
To make this more robust, ensure that all log files and build artifacts are added to .gitignore, or refine hasDiff() to only check for changes in tracked files (e.g., using git status --porcelain -uno).
| function hasDiff() { | |
| const diff = execSync('git status --porcelain').toString().trim(); | |
| return diff.length > 0; | |
| } | |
| function hasDiff() { | |
| const diff = execSync('git status --porcelain -uno').toString().trim(); | |
| return diff.length > 0; | |
| } |
| if (fs.existsSync(WORKFLOW_FILE)) { | ||
| let workflowContent = fs.readFileSync(WORKFLOW_FILE, 'utf8'); | ||
| // Regex matches `- cron: "..." # AUTO-UPDATED` | ||
| const updatedWorkflow = workflowContent.replace( | ||
| /- cron: "[^"]+" # AUTO-UPDATED/, | ||
| `- cron: "${newSchedule.cron}" # AUTO-UPDATED` | ||
| ); | ||
| fs.writeFileSync(WORKFLOW_FILE, updatedWorkflow, 'utf8'); | ||
| console.log('Updated workflow file.'); | ||
| } else { | ||
| console.warn('Workflow file not found to update!'); | ||
| } |
There was a problem hiding this comment.
If the workflow file does not contain the exact pattern - cron: "[^"]+" # AUTO-UPDATED, the replace operation will silently fail (returning the original string) and the script will still print "Updated workflow file." and write the unmodified content back to disk.
To prevent this silent failure, verify that the regex pattern matches the workflow content before attempting to replace and write the file.
if (fs.existsSync(WORKFLOW_FILE)) {
const workflowContent = fs.readFileSync(WORKFLOW_FILE, 'utf8');
const cronRegex = /- cron: "[^"]+" # AUTO-UPDATED/;
if (cronRegex.test(workflowContent)) {
const updatedWorkflow = workflowContent.replace(
cronRegex,
`- cron: "${newSchedule.cron}" # AUTO-UPDATED`
);
fs.writeFileSync(WORKFLOW_FILE, updatedWorkflow, 'utf8');
console.log('Updated workflow file.');
} else {
console.warn("Pattern - cron: '...' # AUTO-UPDATED not found in workflow file. Skipping workflow update.");
}
} else {
console.warn('Workflow file not found to update!');
}| // Step 2: Lint/format auto-fix | ||
| console.log('\n--- Step 2: Format & Lint ---'); | ||
| runCmd('npx eslint --fix . || true'); | ||
| runCmd('npx prettier -w . || true'); | ||
| evaluateState('Format & Lint'); | ||
|
|
||
| // Step 3: Snapshot/generated updates | ||
| console.log('\n--- Step 3: Update Test Snapshots ---'); | ||
| runCmd('npx vitest run -u || true'); | ||
| evaluateState('Update Test Snapshots'); | ||
|
|
||
| // Step 4: Type stubs/analyzer config | ||
| console.log('\n--- Step 4: Sync Types ---'); | ||
| runCmd('npx typesync || true'); | ||
| runCmd('npm install || true'); // Install new types if added | ||
| evaluateState('Sync Types'); | ||
|
|
||
| // Step 5: Dependency re-resolve | ||
| console.log('\n--- Step 5: Update Dependencies ---'); | ||
| runCmd('npm update || true'); | ||
| evaluateState('Update Dependencies'); |
There was a problem hiding this comment.
Appending || true to the shell commands prevents execSync from throwing an error when a command fails. Since runCmd already wraps execSync in a try-catch block to gracefully handle and log failures, using || true is redundant and actively suppresses the error logging (Command failed: ...), making debugging much harder.
Removing || true allows runCmd to correctly log which step failed while still allowing the pipeline to proceed to the next step.
// Step 2: Lint/format auto-fix
console.log('\n--- Step 2: Format & Lint ---');
runCmd('npx eslint --fix .');
runCmd('npx prettier -w .');
evaluateState('Format & Lint');
// Step 3: Snapshot/generated updates
console.log('\n--- Step 3: Update Test Snapshots ---');
runCmd('npx vitest run -u');
evaluateState('Update Test Snapshots');
// Step 4: Type stubs/analyzer config
console.log('\n--- Step 4: Sync Types ---');
runCmd('npx typesync');
runCmd('npm install'); // Install new types if added
evaluateState('Sync Types');
// Step 5: Dependency re-resolve
console.log('\n--- Step 5: Update Dependencies ---');
runCmd('npm update');
evaluateState('Update Dependencies');
Overview
This PR implements a robust, self-adapting, self-healing CI pipeline designed to automatically detect code drift, repair formatting/linting issues, update snapshots, sync types, and resolve dependency updates.
Created by Jules/Claude Code. See original chat context for full implementation details.
Triggers
This automation runs based on three distinct triggers:
ciworkflow fails.workflow_dispatchUI.Initial Schedule & Rationale
Initial Schedule:
0 0 * * *Rationale: Standard fallback schedule (daily at midnight). The system will compute real telemetry based on active PRs within a 7-day rolling window, starting on its first automated Sunday computation or manually triggered workflow execution.
Periodic Recompute
A separate workflow (
compute-schedule.yml) evaluates PR merge velocity to intelligently step the daily/weekly cadence up or down based on repository activity, using safe YAML updates to avoid corruption.Manual Overrides
To manually override the schedule:
.github/self-heal-schedule.ymlwith your desiredSCHEDULEandRATIONALE.LAST_UPDATED(in ms since epoch).Reviewer Checklist
When reviewing a future
selfheal-*PR, please verify:pre-check.log,repair.log, andpost-check.logartifacts provide clear explanations for the automated changes.PR created automatically by Jules for task 913983608745794504 started by @badMade