Skip to content

feat: add Git repository check and automatic .gitignore append safety#257

Open
sandman-sh wants to merge 2 commits into
TestSprite:mainfrom
sandman-sh:feature/git-and-gitignore-checks
Open

feat: add Git repository check and automatic .gitignore append safety#257
sandman-sh wants to merge 2 commits into
TestSprite:mainfrom
sandman-sh:feature/git-and-gitignore-checks

Conversation

@sandman-sh

@sandman-sh sandman-sh commented Jul 16, 2026

Copy link
Copy Markdown

This Pull Request introduces workspace diagnostics for Git repositories and automatic onboarding safety for .gitignore.

Closes #258

Key Features Implemented:

  1. Git Repository Check (testsprite doctor): Adds a diagnostic check verifying if the workspace is located inside a Git repository. Warns the user if not, since Git tracking is required for agent skills to operate properly.
  2. Gitignore Safety Check (testsprite doctor): Inspects the .gitignore file to verify that the .testsprite/ folder is ignored to avoid committing large logs or failure artifacts.
  3. Automatic .gitignore Setup (testsprite setup): Automatically appends .testsprite/ to the .gitignore file during initialization (respecting the --no-agent option to bypass modification).
  4. Negation & Whitespace Handling: Fully parses .gitignore entries, accounting for comments, whitespace, and negation patterns (e.g. !.testsprite/ correctly counts as not ignored).

Verification:

  • Verified by a complete suite of unit and integration tests (all green).
  • Manually verified across multiple directory layouts (non-git directories and existing repositories).

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Git utilities now detect Git repositories and manage .testsprite/ entries in .gitignore. init ensures the entry, while doctor reports repository and ignore status as warnings without failing. GitHub workflows are updated to actions/github-script@v8.

Changes

Gitignore safety flow

Layer / File(s) Summary
Git utilities and behavior coverage
src/lib/git-utils.ts, src/lib/git-utils.test.ts
Adds repository detection, .gitignore parsing, ignore-rule creation/update, dependency injection, and tests for these behaviors.
Init gitignore integration
src/commands/init.ts
Checks ignore status during dry runs and ensures .testsprite/ is ignored after installation, reporting updates or non-blocking warnings.
Doctor diagnostics and warning coverage
src/commands/doctor.ts, src/commands/doctor.test.ts
Adds Git repository and .testsprite/ ignore checks that produce warnings without increasing doctor failures, with updated filesystem mocks and tests.

GitHub Script workflow updates

Layer / File(s) Summary
GitHub Script action upgrades
.github/workflows/ci-nudge.yml, .github/workflows/issue-triage.yml, .github/workflows/pr-triage.yml
Updates workflow action references to actions/github-script@v8 and changes app-token Octokit initialization to use the direct getOctokit helper where applicable.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Init
  participant GitUtils
  participant Filesystem
  User->>Init: run init
  Init->>GitUtils: checkTestspriteIgnored(projectDir)
  GitUtils->>Filesystem: read .gitignore
  Filesystem-->>GitUtils: ignore-rule content
  GitUtils-->>Init: ignored status
  Init->>GitUtils: ensureTestspriteIgnored(projectDir)
  GitUtils->>Filesystem: update .gitignore when needed
  Filesystem-->>GitUtils: write result
  GitUtils-->>Init: modification status
Loading

Possibly related PRs

Suggested reviewers: ruili-testsprite

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 54.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: Git repository diagnostics and safe .gitignore updates.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/git-utils.ts`:
- Around line 45-60: Update the ignore-pattern classification in git-utils.ts
around the existing cleaned, isNegation, and pattern logic so only rules
protecting the complete .testsprite directory are accepted; remove the arbitrary
descendant-prefix checks while preserving negation handling. Update the
corresponding expectations in src/lib/git-utils.test.ts lines 40-43 so specific
child rules such as **/.testsprite/runs return false.
- Around line 100-105: Update the `.gitignore` handling in the
`exists(gitignorePath)` branch to propagate read failures instead of assigning
an empty string in the catch block. Remove the fallback that could overwrite
existing content, allowing the init caller’s existing error handling to emit a
non-blocking warning.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 18277f47-a9ad-4b59-b42d-0d472eee8512

📥 Commits

Reviewing files that changed from the base of the PR and between 60d55e4 and 89178f5.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json, !package-lock.json
📒 Files selected for processing (5)
  • src/commands/doctor.test.ts
  • src/commands/doctor.ts
  • src/commands/init.ts
  • src/lib/git-utils.test.ts
  • src/lib/git-utils.ts

Comment thread src/lib/git-utils.ts
Comment on lines +45 to +60
const cleaned = line.replace(/#.*$/, '').trim();
if (!cleaned) continue;

// A negation pattern like `!.testsprite/` un-ignores the directory.
const isNegation = cleaned.startsWith('!');
const pattern = isNegation ? cleaned.slice(1) : cleaned;

if (
pattern === '.testsprite' ||
pattern === '.testsprite/' ||
pattern === '**/.testsprite' ||
pattern === '**/.testsprite/' ||
pattern.startsWith('.testsprite/') ||
pattern.startsWith('**/.testsprite/')
) {
ignored = !isNegation;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Partial descendant rules are incorrectly classified as complete .testsprite/ protection. This can leave artifacts committable while setup and doctor report the workspace as safe.

  • src/lib/git-utils.ts#L45-L60: only classify patterns that protect the complete .testsprite directory; do not accept arbitrary descendant prefixes.
  • src/lib/git-utils.test.ts#L40-L43: expect specific child rules such as **/.testsprite/runs to return false.

As per path instructions, focus on “Correctness and clear error handling.”

📍 Affects 2 files
  • src/lib/git-utils.ts#L45-L60 (this comment)
  • src/lib/git-utils.test.ts#L40-L43
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/git-utils.ts` around lines 45 - 60, Update the ignore-pattern
classification in git-utils.ts around the existing cleaned, isNegation, and
pattern logic so only rules protecting the complete .testsprite directory are
accepted; remove the arbitrary descendant-prefix checks while preserving
negation handling. Update the corresponding expectations in
src/lib/git-utils.test.ts lines 40-43 so specific child rules such as
**/.testsprite/runs return false.

Source: Path instructions

Comment thread src/lib/git-utils.ts
Comment on lines +100 to +105
if (await exists(gitignorePath)) {
try {
content = await read(gitignorePath);
} catch {
content = '';
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Preserve .gitignore when reading it fails.

The catch changes existing content to ''; if the subsequent write succeeds, the entire file is replaced with only the TestSprite entry. Propagate the read error—the init caller already converts it into a non-blocking warning.

Proposed fix
   if (await exists(gitignorePath)) {
-    try {
-      content = await read(gitignorePath);
-    } catch {
-      content = '';
-    }
+    content = await read(gitignorePath);
   }

As per path instructions, focus on “Correctness and clear error handling.”

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (await exists(gitignorePath)) {
try {
content = await read(gitignorePath);
} catch {
content = '';
}
if (await exists(gitignorePath)) {
content = await read(gitignorePath);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/git-utils.ts` around lines 100 - 105, Update the `.gitignore`
handling in the `exists(gitignorePath)` branch to propagate read failures
instead of assigning an empty string in the catch block. Remove the fallback
that could overwrite existing content, allowing the init caller’s existing error
handling to emit a non-blocking warning.

Source: Path instructions

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci-nudge.yml:
- Line 44: Replace the mutable actions/github-script@v8 reference with the exact
40-character commit SHA for the selected v8 release in
.github/workflows/ci-nudge.yml:44, .github/workflows/issue-triage.yml:52, and
.github/workflows/pr-triage.yml:64.
- Around line 53-55: Both workflows call unavailable getOctokit directly under
actions/github-script@v8. In .github/workflows/ci-nudge.yml lines 53-55 and
.github/workflows/pr-triage.yml lines 78-80, restore access through
require('`@actions/github`').getOctokit(...) or upgrade actions/github-script to
v9+; apply the same fix at both sites.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: fa03111b-68c7-4a07-9af4-dac938346b40

📥 Commits

Reviewing files that changed from the base of the PR and between 89178f5 and 7c9af7b.

📒 Files selected for processing (3)
  • .github/workflows/ci-nudge.yml
  • .github/workflows/issue-triage.yml
  • .github/workflows/pr-triage.yml

private-key: ${{ secrets.TESTSPRITE_HOB_PRIVATE_KEY || secrets.ALFHEIM_AGENT_PRIVATE_KEY }}

- uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.0.1
- uses: actions/github-script@v8

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Pin all external action references immutably.

Each workflow uses the mutable actions/github-script@v8 tag:

  • .github/workflows/ci-nudge.yml#L44-L44: pin actions/github-script to the exact 40-character SHA for the selected v8 release.
  • .github/workflows/issue-triage.yml#L52-L52: pin actions/github-script to the exact 40-character SHA for the selected v8 release.
  • .github/workflows/pr-triage.yml#L64-L64: pin actions/github-script to the exact 40-character SHA for the selected v8 release.
📍 Affects 3 files
  • .github/workflows/ci-nudge.yml#L44-L44 (this comment)
  • .github/workflows/issue-triage.yml#L52-L52
  • .github/workflows/pr-triage.yml#L64-L64
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci-nudge.yml at line 44, Replace the mutable
actions/github-script@v8 reference with the exact 40-character commit SHA for
the selected v8 release in .github/workflows/ci-nudge.yml:44,
.github/workflows/issue-triage.yml:52, and .github/workflows/pr-triage.yml:64.

Source: Path instructions

Comment on lines 53 to 55
const appClient = process.env.APP_TOKEN
? require('@actions/github').getOctokit(process.env.APP_TOKEN)
? getOctokit(process.env.APP_TOKEN)
: null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Workflow references ==\n'
rg -n "actions/github-script|getOctokit|APP_TOKEN" .github/workflows/ci-nudge.yml .github/workflows/pr-triage.yml

printf '\n== Relevant workflow snippets ==\n'
for f in .github/workflows/ci-nudge.yml .github/workflows/pr-triage.yml; do
  echo "--- $f ---"
  nl -ba "$f" | sed -n '1,220p' | rg -n "actions/github-script|getOctokit|APP_TOKEN|script:|with:"
done

printf '\n== Upstream github-script v8 source check ==\n'
python3 - <<'PY'
import urllib.request, re
url = "https://raw.githubusercontent.com/actions/github-script/v8.0.0/dist/index.js"
data = urllib.request.urlopen(url, timeout=20).read().decode()
for needle in ["getOctokit", "github-token", "script:", "context", "core"]:
    print(f"{needle}: {'yes' if needle in data else 'no'}")
m = re.search(r"getOctokit", data)
print("first getOctokit index:", m.start() if m else None)
PY

Repository: TestSprite/testsprite-cli

Length of output: 974


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Workflow excerpts =="
python3 - <<'PY'
from pathlib import Path
for f in [Path('.github/workflows/ci-nudge.yml'), Path('.github/workflows/pr-triage.yml')]:
    print(f"\n--- {f} ---")
    lines = f.read_text().splitlines()
    for i, line in enumerate(lines, 1):
        if 'actions/github-script@v8' in line or 'APP_TOKEN' in line or 'getOctokit' in line:
            start = max(1, i-4)
            end = min(len(lines), i+4)
            for j in range(start, end+1):
                print(f"{j:4d}: {lines[j-1]}")
            print()
PY

echo "== github-script v8 source: helper injection =="
python3 - <<'PY'
import urllib.request, re, sys
url = "https://raw.githubusercontent.com/actions/github-script/v8.0.0/dist/index.js"
data = urllib.request.urlopen(url, timeout=20).read().decode()
for needle in ["getOctokit", "context", "github-token", "with:", "script"]:
    print(f"{needle}: {'found' if needle in data else 'missing'}")
for pat in [r"const\s+getOctokit", r"getOctokit\s*[:=]", r"inputs\.github-token"]:
    m = re.search(pat, data)
    print(pat, "->", bool(m), m.start() if m else None)
PY

Repository: TestSprite/testsprite-cli

Length of output: 6451


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import urllib.request, re
url = "https://raw.githubusercontent.com/actions/github-script/v8.0.0/dist/index.js"
data = urllib.request.urlopen(url, timeout=20).read().decode()

# Print short surrounding context around getOctokit occurrences.
for m in re.finditer(r'getOctokit', data):
    s = max(0, m.start() - 220)
    e = min(len(data), m.end() + 220)
    print("..."+data[s:e]+"...\n")
    break
PY

Repository: TestSprite/testsprite-cli

Length of output: 2757


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
for f in [Path('.github/workflows/ci-nudge.yml'), Path('.github/workflows/pr-triage.yml')]:
    print(f"\n--- {f} ---")
    lines = f.read_text().splitlines()
    for i, line in enumerate(lines, 1):
        if 'actions/github-script@v8' in line or 'APP_TOKEN' in line or 'getOctokit' in line:
            start = max(1, i-3)
            end = min(len(lines), i+3)
            for j in range(start, end+1):
                print(f"{j:4d}: {lines[j-1]}")
            print()
PY

python3 - <<'PY'
import urllib.request, re
url = "https://raw.githubusercontent.com/actions/github-script/v8.0.0/dist/index.js"
data = urllib.request.urlopen(url, timeout=20).read().decode()
for needle in ["getOctokit", "github-token", "script", "context"]:
    print(f"{needle}: {'found' if needle in data else 'missing'}")
m = re.search(r'getOctokit', data)
if m:
    s = max(0, m.start()-200)
    e = min(len(data), m.end()+300)
    print("\ncontext:\n")
    print(data[s:e])
PY

Repository: TestSprite/testsprite-cli

Length of output: 5416


🌐 Web query:

actions/github-script v8 getOctokit script parameters documentation

💡 Result:

In actions/github-script v8, the getOctokit function is not available as an injected factory function [1][2]. The getOctokit functionality was introduced in v9 [3][4]. In v8, users typically access the GitHub API via the pre-authenticated github client provided in the script context [5]. If you need a client with a different token in v8, you must manually import the Octokit library or the @actions/github package, though users should be aware that the v9 upgrade (which introduced the built-in getOctokit factory) specifically addressed the limitations and breaking changes associated with manual imports in newer versions of the action [3][6][4]. If you require the getOctokit factory function, you should upgrade to actions/github-script v9 or later [3][4]. In v9, getOctokit is available directly in the script context and accepts the following parameters [7][8]: 1. token (string, required): A GitHub token (e.g., PAT, GitHub App token). 2. opts (object, optional): Octokit constructor options (e.g., userAgent, baseUrl, request, retry). Note that in v9, getOctokit is injected as a function parameter, so attempting to declare it with const or let will result in a SyntaxError [8][4].

Citations:


getOctokit is unavailable in actions/github-script@v8

Both workflows will hit ReferenceError when APP_TOKEN is set:

  • .github/workflows/ci-nudge.yml
  • .github/workflows/pr-triage.yml

Restore require('@actions/github').getOctokit(...), or upgrade actions/github-script to v9+.

📍 Affects 2 files
  • .github/workflows/ci-nudge.yml#L53-L55 (this comment)
  • .github/workflows/pr-triage.yml#L78-L80
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci-nudge.yml around lines 53 - 55, Both workflows call
unavailable getOctokit directly under actions/github-script@v8. In
.github/workflows/ci-nudge.yml lines 53-55 and .github/workflows/pr-triage.yml
lines 78-80, restore access through require('`@actions/github`').getOctokit(...)
or upgrade actions/github-script to v9+; apply the same fix at both sites.

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.

feat: add Git repository check and automatic .gitignore append safety

1 participant