Skip to content
Merged
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
27 changes: 27 additions & 0 deletions .github/workflows/check-conflict-markers.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# SPDX-FileCopyrightText: 2025 SecPal Contributors
# SPDX-License-Identifier: AGPL-3.0-or-later

name: Check for Merge Conflict Markers

on:
pull_request:
branches: [main]
push:
branches: [main]

permissions:
contents: read

jobs:
check-conflicts:
name: Check for Git Conflict Markers
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Run conflict marker check
run: |
chmod +x scripts/check-conflict-markers.sh
./scripts/check-conflict-markers.sh
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- **Git Conflict Marker Detection** - Automated check for unresolved merge conflicts
- `scripts/check-conflict-markers.sh` - Scans all tracked files for conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`, `|||||||` with space)
- `.github/workflows/check-conflict-markers.yml` - CI integration (runs on all PRs and pushes to main)
- `docs/scripts/CHECK_CONFLICT_MARKERS.md` - Complete usage guide with examples and troubleshooting
- Exit codes: 0 = clean, 1 = conflicts detected
- Prevents accidental commits of broken code from merge conflicts
- Colored output shows exact file locations and line numbers
- **RBAC Phase 3: API Endpoints & Authorization** - Role management REST API (#107)
- `POST /api/v1/users/{id}/roles` - Assign role with temporal parameters (valid_from, valid_until, auto_revoke)
- `GET /api/v1/users/{id}/roles` - List user roles with expiry info (is_active, is_expired status)
Expand Down
196 changes: 196 additions & 0 deletions docs/scripts/CHECK_CONFLICT_MARKERS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
<!--
SPDX-FileCopyrightText: 2025 SecPal Contributors
SPDX-License-Identifier: CC0-1.0
-->

# Git Conflict Marker Detection

The `check-conflict-markers.sh` script detects unresolved Git merge conflict markers in tracked files to prevent accidental commits of broken code.

## Problem

When resolving merge conflicts, developers sometimes forget to remove conflict markers:

```bash
<<<<<<< HEAD
code from current branch
=======
code from incoming branch
>>>>>>> feature-branch
```

These markers cause:

- **Syntax errors** in source code
- **Runtime failures** in scripts and configuration files
- **Broken builds** in CI/CD pipelines
- **Production incidents** if deployed

## Solution

This script automatically detects conflict markers in all tracked text files before they reach the repository.

## Usage

### Manual Check

```bash
# Check all tracked files
./scripts/check-conflict-markers.sh
```

### CI/CD Integration

The script runs automatically in CI via GitHub Actions on every PR and push to `main`.

### Pre-commit Hook

Add to `.git/hooks/pre-commit`:

```bash
#!/bin/bash
# Run conflict marker check
if [ -f scripts/check-conflict-markers.sh ]; then
./scripts/check-conflict-markers.sh || exit 1
fi
```

## Detected Patterns

The script detects these conflict markers:

| Marker | Meaning |
| -------------------------- | ------------------------------- |
| `<<<<<<<` | Start of conflict (from HEAD) |
| `=======` | Separator between changes |
| `>>>>>>>` | End of conflict (from incoming) |
| `\|\|\|\|\|\|\|` (+ space) | Optional: diff3 style marker |

## Exit Codes

- `0` - No conflict markers found ✅
- `1` - Conflict markers detected ❌

## Example Output

### Success (No Conflicts)

```bash
═══════════════════════════════════════════════════════════════
Git Conflict Marker Detection
═══════════════════════════════════════════════════════════════

─────────────────────────────────────────────────────
Checked files: 111
✓ No conflict markers found
═══════════════════════════════════════════════════════════════
```

### Failure (Conflicts Found)

```bash
═══════════════════════════════════════════════════════════════
Git Conflict Marker Detection
═══════════════════════════════════════════════════════════════

✗ Conflict markers detected:

File: .git/hooks/pre-push
Line 73: <<<<<<< Updated upstream...

File: src/main.py
Line 42: =======...

─────────────────────────────────────────────────────
Checked files: 111
✗ Found conflict markers in files

Action required:
1. Open the affected files
2. Search for conflict markers: <<<<<<<, =======, >>>>>>>
3. Manually resolve the conflicts
4. Remove all conflict markers
5. Test your changes
6. Commit again

═══════════════════════════════════════════════════════════════
```

## How to Resolve Conflicts

1. **Locate the markers** - The script shows file names and line numbers
2. **Review both changes** - Understand code from both branches
3. **Choose or merge** - Keep one version, the other, or combine both
4. **Remove markers** - Delete all `<<<<<<<`, `=======`, `>>>>>>>` lines
5. **Test** - Verify the code works correctly
6. **Commit** - Try committing again

## Common Scenarios

### False Positives

The script only checks lines **starting with** conflict markers, reducing false positives. If you legitimately need these patterns in your code (e.g., documentation), indent them:

```markdown
<!-- This is documentation, not a conflict -->

<<<<<<< This won't trigger (indented)
```

### Binary Files

Binary files are automatically skipped - only text files are checked.

### .git Directory

The `.git` directory itself is excluded from checks (uses `git ls-files`).

## Integration with Other Tools

### Pre-commit Framework

```yaml
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: check-conflict-markers
name: Check for conflict markers
entry: ./scripts/check-conflict-markers.sh
language: script
pass_filenames: false
```

### CI/CD Pipelines

```yaml
# GitHub Actions (already included)
- name: Check conflict markers
run: ./scripts/check-conflict-markers.sh
```

## Troubleshooting

### "Permission denied"

```bash
chmod +x scripts/check-conflict-markers.sh
```

### "file: command not found"

Install the `file` utility:

```bash
# Ubuntu/Debian
sudo apt install file

# macOS
brew install file
```

## See Also

- [Preflight Script Documentation](PREFLIGHT.md) - Quality Gate Checks
- [System Requirements Check](CHECK_SYSTEM_REQUIREMENTS.md) - Development Prerequisites
- [Git Merge Conflicts](https://git-scm.com/docs/git-merge#_how_conflicts_are_presented) - Official Git Documentation
89 changes: 89 additions & 0 deletions scripts/check-conflict-markers.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: 2025 SecPal Contributors
# SPDX-License-Identifier: MIT

# ============================================================================
# Git Conflict Marker Detection
# ============================================================================
# Detects unresolved Git merge conflict markers in tracked files to prevent
# accidental commits of broken code.
#
# Exit codes:
# 0 - No conflict markers found
# 1 - Conflict markers detected
# ============================================================================

set -euo pipefail

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Conflict marker patterns
MARKERS=(
"<<<<<<< " # Start of conflict (from HEAD)
"=======" # Separator between changes
">>>>>>> " # End of conflict (from incoming)
"||||||| " # Optional: diff3 style marker
)

CONFLICTS_FOUND=0
CHECKED_FILES=0

echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
echo -e "${BLUE}Git Conflict Marker Detection${NC}"
echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
echo ""

# Get list of tracked files (excluding .git directory)
while IFS= read -r -d '' file; do
# Skip binary files and non-text files
if file "$file" | grep -q "text"; then
CHECKED_FILES=$((CHECKED_FILES + 1))

# Check each conflict marker pattern
for marker in "${MARKERS[@]}"; do
if grep -n "^${marker}" "$file" > /dev/null 2>&1; then
if [ $CONFLICTS_FOUND -eq 0 ]; then
echo -e "${RED}✗ Conflict markers detected:${NC}"
echo ""
fi

CONFLICTS_FOUND=$((CONFLICTS_FOUND + 1))
echo -e "${YELLOW}File:${NC} $file"

# Show lines with conflict markers
grep -n "^${marker}" "$file" | while IFS=: read -r line_num line_content; do
echo -e " ${RED}Line $line_num:${NC} ${line_content:0:60}..."
done
echo ""
break # Only report once per file
fi
done
fi
done < <(git ls-files -z)

echo -e "${BLUE}─────────────────────────────────────────────────────${NC}"
echo -e "Checked files: ${CHECKED_FILES}"

if [ $CONFLICTS_FOUND -eq 0 ]; then
echo -e "${GREEN}✓ No conflict markers found${NC}"
echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
exit 0
else
echo -e "${RED}✗ Found conflict markers in files${NC}"
echo ""
echo -e "${YELLOW}Action required:${NC}"
echo "1. Open the affected files"
echo "2. Search for conflict markers: <<<<<<<, =======, >>>>>>>"
echo "3. Manually resolve the conflicts"
echo "4. Remove all conflict markers"
echo "5. Test your changes"
echo "6. Commit again"
echo ""
echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
exit 1
fi