Skip to content

Conversation

@Bhav-ikkk
Copy link

@Bhav-ikkk Bhav-ikkk commented Dec 23, 2025

Problem

Fixes #153

The validate-scripts job was failing when commit history changed (e.g. via force push) because git merge-base couldn't find a common ancestor with replaced commits.

Solution

  • For PRs: Now directly compares against the base branch (origin/main) to completely avoid merge-base issues
  • For push events: Added error handling with fallback to HEAD^ comparison

Testing

The fix ensures that script validation only fails when scripts actually change, not when history is rewritten through force pushes or amended commits.

Summary by CodeRabbit

  • Chores
    • CI baseline calculation now uses a merge-base against the main branch and reports the computed base for clearer diagnostics.
    • Detects added/renamed JavaScript script files under script directories and exposes a flag when such scripts change.
    • Script validation step runs only when script changes are detected.
    • Workflow logging adjusted for clearer script-change visibility; existing setup and dependency steps preserved.

✏️ Tip: You can customize this high-level summary in your review settings.

Copilot AI review requested due to automatic review settings December 23, 2025 05:56
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 23, 2025

📝 Walkthrough

Walkthrough

Replaces prior BASE_SHA derivation with a merge-base against origin/main; detects added/renamed JavaScript script files under scripts/ and docs/scripts/ via git diff against the computed BASE, outputs changed scripts and sets scripts_changed; keeps script-validation step and runs it only when scripts_changed is true.

Changes

Cohort / File(s) Summary
CI/CD workflow configuration
.github/workflows/ci.yml
Rewrote base determination to use git merge-base against origin/main; added detection of added/renamed JS script files under scripts/ and docs/scripts/ via git diff against the computed BASE; outputs changed scripts list and sets scripts_changed; removed previous complex BASE_SHA/ancestor logic; retained conditional script-validation step that runs only when scripts_changed is true.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

"I hopped through commits and found the base,
I sniffed new scripts in folders, left no trace.
A careful merge-base, no false alarm,
Validators sleep easy, safe from harm.
— 🐇🎉"

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title correctly describes the main change: fixing CI to gracefully handle force pushes in script validation workflow.
Linked Issues check ✅ Passed The PR implementation addresses issue #153 by replacing merge-base logic with direct origin/main comparison for PRs, eliminating false failures from force pushes or amended commits.
Out of Scope Changes check ✅ Passed All changes are scoped to CI workflow logic for script validation; no unrelated modifications to other systems or features were introduced.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes the validate-scripts job that was failing when commit history changed due to force pushes. The fix differentiates between pull request and push events, implementing appropriate strategies for each to avoid git merge-base failures.

Key changes:

  • For PRs: Changed from using the base commit SHA to comparing directly against the base branch reference (origin/main)
  • For push events: Added error handling around git merge-base with a fallback to HEAD^ when the common ancestor cannot be found

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
.github/workflows/ci.yml (2)

39-49: Good fallback logic; consider using GitHub Actions warning annotations.

The error handling and fallback to HEAD^ is a reasonable approach. However, the warning messages on lines 44-45 use plain echo which only appears in the step log. For better visibility in the GitHub Actions UI (surfaced in the summary), use the annotation format:

Suggested improvement for warning visibility
            if ! COMMON_ANCESTOR=$(git merge-base "$BASE_SHA" "$HEAD_SHA" 2>/dev/null); then
-              echo "Could not find common ancestor (likely due to force push)"
-              echo "Comparing with HEAD^ instead"
+              echo "::warning::Could not find common ancestor (likely due to force push). Comparing with HEAD^ instead."
              BASE_SHA="HEAD^"

Minor edge case: If HEAD^ doesn't exist (e.g., initial commit), this would fail. While unlikely for pushes to main, you could add a safeguard:

if ! git rev-parse HEAD^ >/dev/null 2>&1; then
  echo "::warning::No parent commit available, skipping diff"
  echo "scripts_changed=false" >> "$GITHUB_OUTPUT"
  exit 0
fi

41-41: Minor note: redundant HEAD_SHA assignment.

HEAD_SHA is assigned here (line 41) for use in the merge-base call, but it's also assigned unconditionally on line 52. This works correctly but is slightly redundant for push events. Not blocking—just noting for awareness.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9e0e1e1 and 36da0cb.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml
🔇 Additional comments (1)
.github/workflows/ci.yml (1)

36-37: LGTM! Robust solution for PR force-push scenarios.

Using origin/${{ github.event.pull_request.base.ref }} directly references the base branch tip, which always exists and sidesteps the merge-base issue entirely. This is a clean fix for the PR case.

Repository owner deleted a comment from Copilot AI Dec 23, 2025
Repository owner deleted a comment from Copilot AI Dec 23, 2025
Repository owner deleted a comment from Copilot AI Dec 23, 2025
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this can be simplified to the below, but I'm not sure since I haven't had a chance to test it. Please will you test it in various ways on your fork and share screenshots with me of the commands you ran in your shell and the outputs in the GitHub Actions tab.

      - name: Determine if scripts changed
        id: check_changes
        shell: bash
        run: |
          # Find merge base with main (safe for force pushes)
          BASE=$(git merge-base HEAD origin/main)

          # List added/renamed scripts
          matches=$(git diff --name-status "$BASE" HEAD | grep -E '^[AR]\s+(scripts/|docs/scripts/).+\.js$')

          if [ -n "$matches" ]; then
            echo "Changed scripts: $matches"
            echo "scripts_changed=true" >> "$GITHUB_OUTPUT"
          else
            echo "scripts_changed=false" >> "$GITHUB_OUTPUT"
          fi

I'm sorry for not immediately replying like I do with most pull requests - I had to wait until I was available to review this one properly because my Bash skills are a bit rusty and this runs in a completely different environment.


Code Breakdown

You can use this breakdown when writing docs about the changes.

# Find merge base with main (safe for force pushes)
BASE=$(git merge-base HEAD origin/main)
  1. git merge-base HEAD origin/main finds the most recent common commit between the branch you’re on (HEAD) and the main branch on the remote.
  2. This gives us a stable reference point for comparing changes, even if main was force-pushed.
  3. The result is stored in the variable BASE.
# List added/renamed scripts
matches=$(git diff --name-status "$BASE" HEAD | grep -E '^[AR]\s+(scripts/|docs/scripts/).+\.js$')
  1. git diff --name-status "$BASE" HEAD lists all files changed between the base commit and the current commit.

    • Output looks like:

      A scripts/build.js
      M scripts/validate.js
      R docs/scripts/old.js -> docs/scripts/new.js
      
      • A → added
      • R → renamed
      • M → modified
  2. grep -E '^[AR]\s+(scripts/|docs/scripts/).+\.js$' filters the output so we only get files we care about:

    • ^[AR] → the line must start with A (added) or R (renamed).
    • \s+ → one or more spaces separating the status letter and filename.
    • (scripts/|docs/scripts/) → the file must be in the scripts/ or docs/scripts/ folder.
    • .+\.js$ → the filename must end with .js.
  3. Combining steps 1 and 2 with a pipe (|) sends the output of step 1 into the input of step 2, outputting the results of step 2.

  4. The filtered results are stored in matches - meaning it filtered out all files that were neither added nor modified.

if [ -n "$matches" ]; then
  echo "Changed scripts: $matches"
  echo "scripts_changed=true" >> "$GITHUB_OUTPUT"
else
  echo "scripts_changed=false" >> "$GITHUB_OUTPUT"
fi
  1. -n "$matches" checks if matches is not empty.

  2. If there are changed scripts:

    • Print the list for debugging: Changed scripts: $matches
    • Set the GitHub Actions output variable scripts_changed to true.
  3. If no scripts were added or renamed, set scripts_changed to false.

$GITHUB_OUTPUT is how GitHub Actions communicates variables between steps.

Key points

  • Only added or renamed scripts trigger validation; modified scripts are ignored.
  • The code doesn’t rely on GitHub event variables (like github.event.before), making it safe for force-pushed main.
  • The regex is just a precise filter to detect the exact files we care about.

Comparison of the changes

The new Determine if scripts changed step simplifies the previous implementation in a few key ways:

  1. Simplification and maintainability:

    • The logic now directly computes the merge base with origin/main using git merge-base HEAD origin/main.
    • It eliminates the branching and looping over changed files with multiple if statements.
    • The code is shorter, easier to read, and easier to maintain.
  2. Removal of GitHub environment-specific variables:

    • Previously, ${{ github.event.before }} and ${{ github.event.pull_request.base.sha }} were used to find the base commit.
    • These variables can be unreliable, especially with force pushes, as the referenced commits may no longer exist.
    • The new approach relies entirely on local Git history, which is robust across force pushes and PR merges.
  3. Focus on relevant changes:

    • The simplified code detects added or renamed scripts in scripts/ and docs/scripts/, which is exactly what the validation step cares about.
    • It no longer attempts to detect changes in package.json scripts, which simplifies logic while still correctly triggering validation for script files.

Summary:
The update improves robustness, reduces complexity, and avoids relying on GitHub-specific event data that can fail in edge cases like force pushes. It also makes the CI step much easier to maintain going forward.

@Ryan-Millard
Copy link
Owner

Hi, @Bhav-ikkk. Just checking in - do you still want to continue working on this pull request? There's no pressure, I'd just like an update from you since it's been open for a while and I don't want it to sit around for too long.

Unfortunately, I can't offer to work on it if you are unable to, so this PR will be closed if you can't.

@Bhav-ikkk
Copy link
Author

Hi @Ryan-Millard , thanks for the follow-up and sorry for the delay.
I definitely intend to continue with this PR. I was unexpectedly stuck on another project and couldn’t focus on this for a bit.
I’m planning to wrap this up over the weekend and will post an update soon.
Appreciate your patience

@Ryan-Millard
Copy link
Owner

Hi @Ryan-Millard , thanks for the follow-up and sorry for the delay.
I definitely intend to continue with this PR. I was unexpectedly stuck on another project and couldn’t focus on this for a bit.
I’m planning to wrap this up over the weekend and will post an update soon.
Appreciate your patience

Thank you for the update! There's no rush - I just wanted to check up on this to see if you were still interested.

Have a good day!

Replace complex event-based logic with simpler merge-base approach:
- Use git merge-base HEAD origin/main for stable comparison
- Filter only added (A) or renamed (R) scripts with grep
- Remove dependency on GitHub event variables
- Works reliably with force pushes

Addresses owner feedback in PR review.
@Bhav-ikkk Bhav-ikkk force-pushed the fix/ci-force-push-validation branch from 36da0cb to a9c14a4 Compare January 9, 2026 04:14
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @.github/workflows/ci.yml:
- Line 38: The current git diff name-status filter uses the regex
'^[AR]\s+(scripts/|docs/scripts/).+\.js$' so modified files (status 'M') are
skipped; update that regex in the matches assignment (the git diff --name-status
command) to include 'M' (e.g., '^[ARM]\s+(scripts/|docs/scripts/).+\.js$') so
Added, Renamed and Modified script files are detected by the CI check.
- Around line 33-36: The workflow currently always runs BASE=$(git merge-base
HEAD origin/main) with no event detection or error handling; update the script
to detect GITHUB_EVENT_NAME (PR vs push) and branch: for pull_request use git
merge-base HEAD origin/main as before, but for push events wrap the git
merge-base call with error handling and if it fails or when on main fall back to
using HEAD^ (or HEAD~1) as the BASE; ensure the variable name BASE and any
downstream uses remain unchanged and log a clear message when falling back so
failures are visible.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 36da0cb and a9c14a4.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml
🔇 Additional comments (1)
.github/workflows/ci.yml (1)

40-47: LGTM with dependency on line 38 fix.

The conditional logic and output handling are correct. Once line 38 is fixed to include Modified files, this segment will work as intended.

@Bhav-ikkk
Copy link
Author

Hi @Ryan-Millard!

Sorry for the delayed response - I was a bit busy this past week but I'm back on track now.

I've implemented the simplified script detection logic you suggested! The changes look great and the code is much cleaner now.

What I changed:

Replaced the complex event-based logic with git merge-base HEAD origin/main
Simplified to a single grep filter for added/renamed scripts
Removed all dependencies on GitHub event variables
Now only triggers on added (A) or renamed (R) .js files in scripts or scripts
Testing completed:
I've thoroughly tested the new logic locally and created a comprehensive test script to verify all scenarios:

TEST 1: Verified git merge-base HEAD origin/main finds common ancestor correctly
TEST 2: Confirmed git diff --name-status shows all changed files
TEST 3: Tested grep filter with current state (no scripts = scripts_changed=false)
TEST 4: Validated regex pattern with simulated data (A, M, R, D statuses) - correctly filters only A and R
TEST 5: Created actual test script in scripts folder - correctly detected (scripts_changed=true)
TEST 6: Created actual test script in scripts folder - correctly detected (scripts_changed=true)

Key benefits confirmed:

Works reliably with force pushes (no dependency on github.event.before)
Much simpler and more maintainable
Only triggers validation on added/renamed scripts (not modified)
Handles both PR and push events identically
The new implementation is exactly as you described, and all tests are passing. Thank you for the detailed breakdown - it really helped me understand why this approach is better

Img2num.ci.yml.mp4

Copy link
Owner

@Ryan-Millard Ryan-Millard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great. Thank you so much!

Please will you write some documentation for this workflow so others can understand how it works in the future. You can find the current documentation here.

To make it easier to write that documentation, you can use some of the contents of my previous review.

Have a great day!

@Ryan-Millard
Copy link
Owner

Hi @Bhav-ikkk, are you still working on this PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(ci.yml): false positive triggers script validation failure on force push

2 participants