forked from makenotion/notion-mcp-server
-
Notifications
You must be signed in to change notification settings - Fork 0
[Self-Heal] Add self-scheduling auto-repair workflow #54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
badMade
wants to merge
1
commit into
main
Choose a base branch
from
self-heal-2758855860826736662
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| schedule: "0 0 * * 1" | ||
| rationale: "Initial fallback schedule due to low commit volume. Computed by self-healing automation." | ||
| LAST_UPDATED: "2025-05-31T00:00:00Z" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| name: Compute Self-Heal Schedule | ||
|
|
||
| on: | ||
| schedule: | ||
| - cron: "0 0 1 * *" # Runs monthly | ||
| workflow_dispatch: | ||
|
|
||
| concurrency: | ||
| group: compute-schedule-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| permissions: | ||
| contents: write | ||
| pull-requests: write | ||
| actions: read | ||
|
|
||
| jobs: | ||
| compute-schedule: | ||
| runs-on: ubuntu-latest | ||
| if: !startsWith(github.ref_name, 'selfheal-') | ||
| timeout-minutes: 5 | ||
| steps: | ||
| - name: Checkout Code | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
|
|
||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: 20 | ||
|
|
||
| - name: Install Dependencies | ||
| run: npm ci | ||
|
|
||
| - name: Configure Git Committer | ||
| run: | | ||
| git config --global user.name "github-actions[bot]" | ||
| git config --global user.email "github-actions[bot]@users.noreply.github.com" | ||
|
|
||
| - name: Run Compute Schedule | ||
| run: | | ||
| chmod +x scripts/compute_schedule.mjs | ||
| node scripts/compute_schedule.mjs || true | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Check for Duplicate PRs | ||
| id: duplicate_check | ||
| run: | | ||
| RECENT_PR=$(gh pr list --label self-heal-schedule --state open --json createdAt -q '.[0].createdAt' || echo "null") | ||
| if [ ! -z "$RECENT_PR" ] && [ "$RECENT_PR" != "null" ]; then | ||
| echo "duplicate=true" >> $GITHUB_OUTPUT | ||
| echo "Found open self-heal-schedule PR, skipping new PR creation." | ||
| else | ||
| echo "duplicate=false" >> $GITHUB_OUTPUT | ||
| fi | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Create PR | ||
| if: steps.duplicate_check.outputs.duplicate == 'false' | ||
| run: | | ||
| if [ -n "$(git status --porcelain)" ]; then | ||
| BRANCH_NAME="selfheal-schedule-$(date +%s)" | ||
| git checkout -b $BRANCH_NAME | ||
|
|
||
| git add .github/self-heal-schedule.yml .github/workflows/self-heal.yml | ||
| git commit -m "Update self-heal schedule" | ||
| git push origin $BRANCH_NAME | ||
|
|
||
| gh pr create \ | ||
| --title "[Self-Heal Schedule] Update cadence" \ | ||
| --body "Automated update to self-healing CI schedule based on telemetry." \ | ||
| --label "automation,self-heal-schedule" \ | ||
| --base main | ||
| fi | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| name: Self-Heal Auto-Repair | ||
|
|
||
| on: | ||
| schedule: | ||
| - cron: "0 0 * * 1" # AUTO-UPDATED | ||
| workflow_run: | ||
| workflows: ["ci"] | ||
| types: | ||
| - completed | ||
| workflow_dispatch: | ||
|
|
||
| concurrency: | ||
| group: selfheal-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| permissions: | ||
| contents: write | ||
| pull-requests: write | ||
| actions: read | ||
|
|
||
| jobs: | ||
| repair: | ||
| runs-on: ubuntu-latest | ||
| if: >- | ||
| !startsWith(github.ref_name, 'selfheal-') && | ||
| ( | ||
| github.event_name == 'schedule' || | ||
| github.event_name == 'workflow_dispatch' || | ||
| (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'failure') | ||
| ) | ||
| timeout-minutes: 15 | ||
| steps: | ||
| - name: Checkout Code | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
|
|
||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: 20 | ||
|
|
||
| - name: Configure Git Committer | ||
| run: | | ||
| git config --global user.name "github-actions[bot]" | ||
| git config --global user.email "github-actions[bot]@users.noreply.github.com" | ||
|
|
||
| - name: Cleanup Stale PRs | ||
| run: | | ||
| STALE_DATE=$(date -d "7 days ago" --iso-8601=seconds) | ||
| for pr in $(gh pr list --label self-heal --search "created:<$STALE_DATE" --json number -q '.[].number'); do | ||
| gh pr close "$pr" --comment "Auto-closing stale self-heal PR" || true | ||
| done | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Run Self-Heal Repair | ||
| id: repair | ||
| continue-on-error: true | ||
| run: | | ||
| chmod +x scripts/self_heal.mjs scripts/healthcheck.mjs | ||
| node scripts/self_heal.mjs | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Upload Logs | ||
| uses: actions/upload-artifact@v4 | ||
| if: always() | ||
| with: | ||
| name: self-heal-logs | ||
| path: '*.log' | ||
|
|
||
| - name: Check for Duplicate PRs | ||
| id: duplicate_check | ||
| run: | | ||
| RECENT_PR=$(gh pr list --label self-heal --state open --json createdAt -q '.[0].createdAt' || echo "null") | ||
| if [ ! -z "$RECENT_PR" ] && [ "$RECENT_PR" != "null" ]; then | ||
| echo "duplicate=true" >> $GITHUB_OUTPUT | ||
| echo "Found open self-heal PR, skipping new PR creation." | ||
| else | ||
| echo "duplicate=false" >> $GITHUB_OUTPUT | ||
| fi | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Create PR | ||
| if: steps.duplicate_check.outputs.duplicate == 'false' && steps.repair.outcome == 'success' | ||
| run: | | ||
| if [ -n "$(git status --porcelain)" ]; then | ||
| BRANCH_NAME="selfheal-repair-$(date +%s)" | ||
| git checkout -b $BRANCH_NAME | ||
|
|
||
| # Entropy check: if we see suspicious patterns, we can abort, but we rely on git add paths as primary safeguard. | ||
| if git diff | grep -iE '(api_key|token|password|secret)[^a-zA-Z0-9]*='; then | ||
| echo "Potential secrets detected in diff, aborting PR creation." | ||
| # Return true so workflow doesnt completely fail, but no PR is made | ||
| exit 0 | ||
| fi | ||
|
|
||
| # Add specified files only to prevent pushing forbidden changes | ||
| for path in src/ tests/ package.json package-lock.json eslint.config.mjs; do | ||
| git add "$path" 2>/dev/null || true | ||
| done | ||
|
|
||
| git commit -m "Auto-repair dependencies and code formatting" | ||
| git push origin $BRANCH_NAME | ||
|
|
||
| TITLE="[Self-Heal Manual] Repair" | ||
| if [ "${{ github.event_name }}" == "schedule" ]; then | ||
| TITLE="[Self-Heal Scheduled] Drift fixes" | ||
| elif [ "${{ github.event_name }}" == "workflow_run" ]; then | ||
| TITLE="[Self-Heal Reactive] CI fix" | ||
| fi | ||
|
|
||
| PR_BODY="Automated self-healing PR to fix code drift and CI failures. Review logs in actions.\n\n" | ||
| PR_BODY+="Triggers explained:\n" | ||
| PR_BODY+="1. Scheduled: Proactive drift detection\n" | ||
| PR_BODY+="2. CI Failure: Reactive repair trigger\n" | ||
| PR_BODY+="3. Manual: workflow_dispatch\n\n" | ||
| PR_BODY+="Checklist:\n" | ||
| PR_BODY+="- [ ] No unintended source logic changes\n" | ||
| PR_BODY+="- [ ] All health checks pass\n" | ||
| PR_BODY+="- [ ] Review workflow logs\n" | ||
|
|
||
| gh pr create \ | ||
| --title "$TITLE" \ | ||
| --body "$PR_BODY" \ | ||
| --label "automation,self-heal" \ | ||
| --base main | ||
| fi | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,3 +10,4 @@ bin/ | |
| .cursor | ||
|
|
||
| .DS_Store | ||
| *.log | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| # Self-Heal Auto-Repair Setup | ||
|
|
||
| This document describes the self-healing automation integrated into the repository to automatically detect, fix, and submit PRs for code drift and formatting issues. | ||
|
|
||
| ## System Components | ||
|
|
||
| 1. **scripts/healthcheck.mjs**: Validates the project health using tests, linters, types, and build scripts. | ||
| 2. **scripts/self_heal.mjs**: Executes a sequential 6-step idempotent repair process (e.g. `npm ci`, `eslint --fix`, `prettier -w`, `vitest -u`, `typesync`, `npm update`). It validates health after each step and will exit immediately if a step succeeds and produces a git diff. | ||
| 3. **scripts/compute_schedule.mjs**: Dynamically computes an optimal run cadence based on recent git telemetry (commit volume). | ||
| 4. **GitHub Actions**: | ||
| - `self-heal.yml`: Runs the repair pipeline triggered by schedule, manual dispatch, or CI failures. Submits an automated PR using `gh pr create`. | ||
| - `compute-schedule.yml`: Recomputes the ideal schedule periodically and submits a PR if adjustments are needed. | ||
|
|
||
| ## Triggers | ||
|
|
||
| The self-healing workflow is triggered via three different mechanisms: | ||
| 1. **Scheduled (`schedule`)**: Proactive monitoring that runs on a cadence determined by `compute_schedule.mjs` to detect codebase drift. | ||
| 2. **Reactive (`workflow_run`)**: Triggered immediately when the main `ci` workflow fails on the default branch. | ||
| 3. **Manual (`workflow_dispatch`)**: Can be manually triggered from the Actions tab for immediate execution and repair. | ||
|
|
||
| ## Self-Scheduling Logic | ||
|
|
||
| The `scripts/compute_schedule.mjs` script gathers recent git commit frequency (e.g., last 30 days) and assigns a tiered schedule cadence: | ||
| - **Dormant** (0 commits): Monthly. | ||
| - **Low-churn** (< 10 commits): Weekly. | ||
| - **Standard** (10 - 50 commits): Twice a week. | ||
| - **Active** (50 - 150 commits): Daily. | ||
| - **High** (> 150 commits): Every 12 hours. | ||
|
|
||
| This automatically optimizes compute resources during inactive periods and maintains responsiveness during active development. | ||
|
|
||
| ## Manual Override Instructions | ||
|
|
||
| If you need to forcefully override the schedule and prevent the automatic recalculation from changing it back, you can: | ||
| 1. Modify `.github/self-heal-schedule.yml` directly with your desired `schedule` value. | ||
| 2. Modify `.github/workflows/self-heal.yml` schedule cron line to match the value above, preserving the `# AUTO-UPDATED` inline comment marker at the end. | ||
| 3. The oscillation guard or logic in `scripts/compute_schedule.mjs` can be adjusted if needed to ignore future automated updates. | ||
|
|
||
| ## Reviewer Checklist | ||
|
|
||
| When reviewing a self-heal or schedule update PR, human reviewers must verify the following: | ||
| - [ ] **No unintended source logic changes**: The self-heal process is only intended for formatting, snapshots, and tooling updates. Confirm no business logic was modified. | ||
| - [ ] **All health checks pass**: Ensure all CI status checks (tests, linters, types) are green for the self-heal branch. | ||
| - [ ] **Review workflow logs**: Examine the artifact logs attached to the PR action run to understand why the drift occurred. | ||
| - [ ] **Check for duplicate PRs**: Ensure this does not duplicate a previously opened PR. (The automation attempts to catch this, but manual verification provides redundancy). | ||
| - [ ] **Verify valid schedule format**: For schedule update PRs, verify the cron expression is standard and valid. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| import eslintJs from "@eslint/js"; | ||
| import tseslint from "typescript-eslint"; | ||
| import globals from "globals"; | ||
|
|
||
| export default tseslint.config( | ||
| eslintJs.configs.recommended, | ||
| ...tseslint.configs.recommended, | ||
| { | ||
| ignores: ["build/**", "dist/**", "coverage/**", "bin/**", "node_modules/**"], | ||
| }, | ||
| { | ||
| languageOptions: { | ||
| globals: { | ||
| ...globals.node, | ||
| ...globals.browser, | ||
| }, | ||
| }, | ||
| rules: { | ||
| "@typescript-eslint/no-explicit-any": "off", | ||
| "@typescript-eslint/no-unused-vars": "off", | ||
| "@typescript-eslint/ban-ts-comment": "off", | ||
| "preserve-caught-error": "off" | ||
| } | ||
| } | ||
| ); | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The rule
preserve-caught-erroris not a standard ESLint or@typescript-eslintrule. Including an invalid or unrecognized rule in the flat configuration will cause ESLint to throw an error and fail to run entirely, which will break the linting step in your healthchecks and CI. If you want to configure how unused caught errors are handled, you should configure thecaughtErrorsoption of the standardno-unused-varsrule instead.