Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/self-heal-schedule.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
schedule: "0 0 * * *"
rationale: "Initial schedule (Fallback: once a day at midnight)"
last_updated: "2024-05-22T00:00:00.000Z"
# AUTO-UPDATED
74 changes: 74 additions & 0 deletions .github/workflows/compute-schedule.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Compute Self-Heal Schedule

on:
schedule:
- cron: "0 2 * * 0" # Weekly on Sunday at 02:00
workflow_dispatch:

jobs:
compute-schedule:
if: "!startsWith(github.ref_name, 'selfheal-')"
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: write
pull-requests: write
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
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Check for duplicate schedule PRs
id: check_pr
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
OPEN_PRS=$(gh pr list --label self-heal-schedule --state open --json number --jq length)
if [ "$OPEN_PRS" -gt 0 ]; then
echo "open_pr_exists=true" >> $GITHUB_OUTPUT
echo "Skipping compute because a self-heal-schedule PR is already open."
else
echo "open_pr_exists=false" >> $GITHUB_OUTPUT
fi

- name: Compute and update schedule
if: steps.check_pr.outputs.open_pr_exists == 'false'
id: compute
run: |
node scripts/compute_schedule.mjs
if [ -n "$(git status --porcelain)" ]; then
echo "has_diff=true" >> $GITHUB_OUTPUT
else
echo "has_diff=false" >> $GITHUB_OUTPUT
fi

- name: Create Pull Request
if: steps.check_pr.outputs.open_pr_exists == 'false' && steps.compute.outputs.has_diff == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
BRANCH_NAME="selfheal-schedule-$(date +%s)"
git checkout -b "$BRANCH_NAME"

git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"

git add .github/self-heal-schedule.yml .github/workflows/self-heal.yml
git commit -m "Update self-heal schedule based on telemetry"
git push origin "$BRANCH_NAME"

gh pr create \
--title "[Self-Heal Schedule] Update cadence" \
--body "Automated PR to update the self-healing pipeline schedule based on recent repository telemetry." \
--label "self-heal-schedule,automation" \
--base main
120 changes: 120 additions & 0 deletions .github/workflows/self-heal.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
name: Self-Heal Pipeline

on:
schedule:
- cron: "0 0 * * *" # AUTO-UPDATED
workflow_run:
workflows: ["ci"]
types:
- completed
workflow_dispatch:

concurrency:
group: selfheal-${{ github.ref }}
cancel-in-progress: true

jobs:
repair:
# Only run scheduled triggers on the default branch
if: >
(!startsWith(github.ref_name, 'selfheal-')) &&
(github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'failure')
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: write
pull-requests: write
actions: read

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
cache: 'npm'

- name: Install Dependencies
run: npm ci

- name: Check for duplicate Self-Heal PRs
id: check_pr
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Close stale PRs older than 7 days
gh pr list --label self-heal --state open --json number,createdAt | jq -r '.[] | select(.createdAt < (now - 604800 | todate)) | .number' | xargs -I {} gh pr close {} --comment "Closing stale self-heal PR" || true

OPEN_PRS=$(gh pr list --label self-heal --state open --json number --jq length)
if [ "$OPEN_PRS" -gt 0 ]; then
echo "open_pr_exists=true" >> $GITHUB_OUTPUT
echo "Skipping repair because a self-heal PR is already open."
else
echo "open_pr_exists=false" >> $GITHUB_OUTPUT
fi

- name: Run Pre-Repair Healthcheck
if: steps.check_pr.outputs.open_pr_exists == 'false'
id: pre_healthcheck
run: node scripts/healthcheck.mjs
continue-on-error: true

- name: Run Self-Heal Repair Pipeline
if: steps.check_pr.outputs.open_pr_exists == 'false'
id: self_heal
run: node scripts/self_heal.mjs

- name: Check git diff and scan for secrets
if: steps.check_pr.outputs.open_pr_exists == 'false' && steps.self_heal.outcome == 'success'
id: verify_diff
run: |
# Ensure there is a diff
if [ -z "$(git status --porcelain)" ]; then
echo "No diff found. Exiting."
exit 0
fi

# Stage files explicitly
git add src/ tests/ scripts/ package*.json .github/self-heal-schedule.yml .github/workflows/self-heal.yml 2>/dev/null || true

# Scan for entropy (basic secret heuristic: strings with 20+ base64 chars that might be keys)
if git diff --staged | grep -qiE "(secret|token|api_key|password).*['\"][a-zA-Z0-9_-]{20,}['\"]"; then
echo "❌ ERROR: Potential secrets found in diff. Aborting."
git reset
exit 1
fi

echo "has_diff=true" >> $GITHUB_OUTPUT

- name: Create Pull Request
if: steps.verify_diff.outputs.has_diff == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
BRANCH_NAME="selfheal-$(date +%s)"
git checkout -b "$BRANCH_NAME"

git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"

git commit -m "Auto-repair CI and drift issues"
git push origin "$BRANCH_NAME"

TITLE="[Self-Heal] Repair CI and drift issues"
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"
elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
TITLE="[Self-Heal Manual] Repair"
fi

gh pr create \
--title "$TITLE" \
--body "Automated self-healing PR triggered by \`${{ github.event_name }}\`. Please review the changes." \
--label "self-heal,automation" \
--base main
39 changes: 39 additions & 0 deletions SELF_HEAL_SETUP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Self-Healing CI Pipeline Setup

This repository is equipped with an automated self-healing CI pipeline that detects code drift, formatting issues, and test failures, and automatically proposes fixes via Pull Requests.

## Triggers
The self-healing pipeline runs under three conditions:
1. **Scheduled:** Proactively runs based on a self-computed schedule (to catch drift).
2. **CI Failure:** Reactively runs when the main `ci` workflow fails.
3. **Manual Dispatch:** Can be manually triggered via the GitHub Actions tab.

## Self-Scheduling Logic
The pipeline dynamically computes its own optimal run frequency based on repository telemetry (commit frequency, pull request velocity).
- Active repositories will be checked more frequently.
- Dormant repositories will be checked less frequently to save CI minutes.
- The schedule is recomputed weekly via the `Compute Self-Heal Schedule` workflow, which creates a PR if the schedule needs to change.

## Repair Pipeline Steps
The pipeline is idempotent and executes the following steps:
1. **Rebuild/Reinstall:** Clean install of tooling and dependencies.
2. **Lint/Format:** Auto-fix linting and formatting issues (via `eslint` and `prettier`).
3. **Snapshot Updates:** Updates missing or outdated test snapshots (via `vitest -u`).
4. **Type Stubs:** Acquires missing type definitions.
5. **Dependency Re-resolve:** Refreshes the package lockfile.
6. **Static Assets:** Regenerates documentation or code-gen assets (if applicable).

After each step, a healthcheck is executed. If the healthcheck passes and there is a diff, the pipeline stops and creates a PR immediately to provide the minimal fix.

## Customization and Overrides
To override the schedule:
1. Manually edit `.github/self-heal-schedule.yml`.
2. Ensure you keep the `# AUTO-UPDATED` comment at the bottom.
3. Commit and push the changes. The pipeline will respect this change, although it will eventually be re-evaluated.

## Reviewer Checklist
When reviewing a `[Self-Heal]` PR, ensure:
- [ ] No unwanted dependency changes were introduced.
- [ ] Formatting changes align with project standards.
- [ ] Any updated test snapshots are logically correct and not masking a true bug.
- [ ] There are no exposed secrets or tokens in the diff.
Loading