diff --git a/.github/workflows/auto-pr-number.yml b/.github/workflows/auto-pr-number.yml new file mode 100644 index 00000000..8c0b8ab9 --- /dev/null +++ b/.github/workflows/auto-pr-number.yml @@ -0,0 +1,89 @@ +name: Auto Add PR Number + +on: + pull_request: + types: [opened, synchronize] + +jobs: + update-commits: + name: Add PR Number to Commits + runs-on: ubuntu-latest + if: github.event.pull_request.draft == false + + permissions: + contents: write + pull-requests: write + + steps: + - name: Checkout PR + uses: actions/checkout@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.ref }} + + - name: Configure Git + run: | + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + + - name: Check and update commit messages + id: update-commits + run: | + PR_NUMBER="${{ github.event.pull_request.number }}" + BASE_BRANCH="origin/${{ github.base_ref }}" + HEAD_BRANCH="${{ github.event.pull_request.head.ref }}" + + echo "Checking commits in PR #${PR_NUMBER}" + + # Get list of commits in this PR + COMMITS=$(git rev-list ${BASE_BRANCH}..HEAD) + NEEDS_UPDATE=false + + for commit in $COMMITS; do + MESSAGE=$(git log -1 --pretty=%s $commit) + # Check if commit message already has a PR number + if ! echo "$MESSAGE" | grep -q "(#[0-9]\+)$"; then + echo "Commit $commit needs PR number: $MESSAGE" + NEEDS_UPDATE=true + fi + done + + if [ "$NEEDS_UPDATE" = true ]; then + echo "needs_update=true" >> $GITHUB_OUTPUT + + # Create a new branch for the updated commits + git checkout -b temp-pr-${PR_NUMBER} + + # Rebase and add PR numbers + export PR_NUMBER + git rebase ${BASE_BRANCH} -x 'git commit --amend -m "$(git log -1 --pretty=%s) (#$PR_NUMBER)"' + + # Force push the updated branch + git push --force-with-lease origin HEAD:${HEAD_BRANCH} + + echo "✅ Updated commit messages with PR #${PR_NUMBER}" + else + echo "needs_update=false" >> $GITHUB_OUTPUT + echo "All commits already have PR numbers" + fi + + - name: Comment on PR + if: steps.update-commits.outputs.needs_update == 'true' + uses: actions/github-script@v6 + with: + script: | + const prNumber = context.payload.pull_request.number; + + const comment = `## ✅ PR Number Added to Commits + + I've automatically added \`(#${prNumber})\` to all commit messages in this PR. + + This ensures that when using "Rebase and merge", all commits will be properly tagged with the PR number in the main branch history.`; + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: comment + }); \ No newline at end of file diff --git a/.github/workflows/badge.yml b/.github/workflows/badge.yml new file mode 100644 index 00000000..d299b8a4 --- /dev/null +++ b/.github/workflows/badge.yml @@ -0,0 +1,13 @@ +name: Build Status + +on: + push: + branches: [ main, master ] + workflow_dispatch: + +jobs: + update-badge: + runs-on: ubuntu-latest + steps: + - name: Update README badge + run: echo "Badge will be automatically available at https://github.com/${{ github.repository }}/actions/workflows/ci.yml/badge.svg" \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..642ad77b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,75 @@ +name: CI + +on: + push: + branches: [ main, master, develop, 'br_dev_*' ] + pull_request: + branches: [ main, master, develop ] + +jobs: + format-check: + name: Format Check + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Install clang-format + run: | + sudo apt-get update + sudo apt-get install -y clang-format-14 + + - name: Check formatting + run: | + echo "Checking code formatting..." + make check-format || { + echo "::error::Code formatting check failed" + echo "Please run 'make format' locally and commit the changes" + exit 1 + } + + build-and-test: + name: Build and Test + runs-on: ${{ matrix.os }} + needs: format-check + + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + build_type: [Debug, Release] + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Install dependencies (Ubuntu) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y cmake g++ clang + + - name: Install dependencies (macOS) + if: runner.os == 'macOS' + run: | + brew update + brew install cmake + + - name: Configure CMake + run: | + cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + + - name: Build + run: | + cmake --build build --config ${{ matrix.build_type }} -j$(nproc 2>/dev/null || sysctl -n hw.ncpu) + + - name: Test + run: | + cd build + ctest -C ${{ matrix.build_type }} --output-on-failure --verbose + + - name: List all tests + if: always() + run: | + make test-list || true \ No newline at end of file diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml new file mode 100644 index 00000000..db912818 --- /dev/null +++ b/.github/workflows/clang-format-check.yml @@ -0,0 +1,43 @@ +name: Clang Format Check + +on: + push: + branches: [ main, master, develop ] + pull_request: + branches: [ main, master, develop ] + +jobs: + formatting-check: + name: Check Code Formatting + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Install clang-format + run: | + sudo apt-get update + sudo apt-get install -y clang-format-14 + + - name: Check formatting + run: | + echo "Checking code formatting with clang-format..." + # Find all C++ source files + find include tests -name "*.h" -o -name "*.cpp" | while read file; do + # Format the file and check if it differs from the original + clang-format-14 --style=file "$file" | diff -u "$file" - || { + echo "::error file=$file::File is not properly formatted" + exit 1 + } + done + echo "All files are properly formatted!" + + - name: Suggest formatting fix + if: failure() + run: | + echo "::warning::Code formatting issues detected. Please run 'make format' locally and commit the changes." + echo "To fix formatting issues, run:" + echo " make format" + echo "or" + echo " find include tests -name '*.h' -o -name '*.cpp' | xargs clang-format -i" \ No newline at end of file diff --git a/.github/workflows/enforce-pr-number.yml b/.github/workflows/enforce-pr-number.yml new file mode 100644 index 00000000..69f1dee5 --- /dev/null +++ b/.github/workflows/enforce-pr-number.yml @@ -0,0 +1,140 @@ +name: Enforce PR Number in Commits + +# This workflow only checks NEW commits that are part of the PR +# It does not check historical commits that might already be in the base branch +on: + pull_request: + types: [opened, edited, synchronize] + +jobs: + check-commit-format: + name: Check Commit Messages + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + checks: write + issues: write + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Check commit messages + uses: actions/github-script@v6 + with: + script: | + const prNumber = context.payload.pull_request.number; + const baseBranch = context.payload.pull_request.base.ref; + + // Get commits in this PR - this API only returns commits unique to the PR + let commits; + try { + commits = await github.rest.pulls.listCommits({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber + }); + } catch (error) { + console.log('::error::Failed to fetch PR commits. Falling back to simple check.'); + console.log('Error:', error.message); + // Exit gracefully if we can't access commits + return; + } + + let hasIssues = false; + const problematicCommits = []; + + for (const commit of commits.data) { + const message = commit.commit.message; + const firstLine = message.split('\n')[0]; + + // Check if commit message has a PR number + if (!firstLine.match(/\(#\d+\)$/)) { + hasIssues = true; + problematicCommits.push({ + sha: commit.sha.substring(0, 7), + message: firstLine + }); + } + } + + if (hasIssues) { + let comment = `## ⚠️ Commit Message Format\n\n`; + comment += `To maintain a clean git history, please add PR numbers to your commit messages.\n\n`; + comment += `### Commits missing PR number:\n\n`; + + for (const commit of problematicCommits) { + comment += `- \`${commit.sha}\`: ${commit.message}\n`; + comment += ` - Suggested: \`${commit.message} (#${prNumber})\`\n`; + } + + comment += `\n### How to fix:\n\n`; + comment += `\`\`\`bash\n`; + comment += `# Interactive rebase to edit commit messages\n`; + comment += `git rebase -i origin/${baseBranch}\n`; + comment += `# Mark commits as 'reword' and add (#${prNumber}) to each message\n`; + comment += `# Then force push\n`; + comment += `git push --force-with-lease\n`; + comment += `\`\`\`\n\n`; + comment += `Or use this automated approach:\n`; + comment += `\`\`\`bash\n`; + comment += `# Add PR number to all commits since base branch\n`; + comment += `git rebase origin/${baseBranch} --exec 'git commit --amend -m "$(git log -1 --pretty=%s) (#${prNumber})"'\n`; + comment += `git push --force-with-lease\n`; + comment += `\`\`\``; + + // Create a check run + await github.rest.checks.create({ + owner: context.repo.owner, + repo: context.repo.repo, + name: 'Commit Message Format', + head_sha: context.payload.pull_request.head.sha, + status: 'completed', + conclusion: 'neutral', + output: { + title: 'Commit messages should include PR number', + summary: comment + } + }); + + // Try to add label, but don't fail if we can't + try { + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + labels: ['needs-pr-number'] + }); + } catch (e) { + console.log('::warning::Could not add label (insufficient permissions)'); + } + } else { + // Remove label if it exists + try { + await github.rest.issues.removeLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + name: 'needs-pr-number' + }); + } catch (e) { + // Label might not exist, that's ok + } + + // Create success check + await github.rest.checks.create({ + owner: context.repo.owner, + repo: context.repo.repo, + name: 'Commit Message Format', + head_sha: context.payload.pull_request.head.sha, + status: 'completed', + conclusion: 'success', + output: { + title: 'All commit messages properly formatted', + summary: 'All commits include PR numbers. Ready for rebase and merge!' + } + }); + } \ No newline at end of file diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml new file mode 100644 index 00000000..b239f2da --- /dev/null +++ b/.github/workflows/pr-checks.yml @@ -0,0 +1,84 @@ +name: PR Checks + +on: + pull_request: + types: [opened, synchronize, reopened] + +jobs: + clang-format: + name: Clang Format + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + + steps: + - name: Checkout PR + uses: actions/checkout@v3 + with: + # Fetch the PR branch and the base branch + fetch-depth: 0 + + - name: Install clang-format + run: | + sudo apt-get update + sudo apt-get install -y clang-format-14 + + - name: Check changed files + id: changed-files + run: | + # Get list of changed C++ files + git diff --name-only origin/${{ github.base_ref }}...HEAD | grep -E '\.(h|cpp)$' > changed_files.txt || true + + if [ -s changed_files.txt ]; then + echo "has_changes=true" >> $GITHUB_OUTPUT + echo "Changed C++ files:" + cat changed_files.txt + else + echo "has_changes=false" >> $GITHUB_OUTPUT + echo "No C++ files changed" + fi + + - name: Check formatting of changed files + if: steps.changed-files.outputs.has_changes == 'true' + run: | + exit_code=0 + while IFS= read -r file; do + if [ -f "$file" ]; then + echo "Checking $file..." + clang-format-14 --style=file --dry-run --Werror "$file" || { + echo "::error file=$file::File is not properly formatted" + exit_code=1 + } + fi + done < changed_files.txt + + if [ $exit_code -ne 0 ]; then + echo "" + echo "::error::Some files are not properly formatted." + echo "To fix, run: make format" + exit 1 + fi + + - name: Post PR comment on failure + if: failure() && steps.changed-files.outputs.has_changes == 'true' + uses: actions/github-script@v6 + with: + script: | + const comment = `## ❌ Code Formatting Check Failed + + Some files in this PR are not properly formatted according to the project's clang-format rules. + + **To fix this issue:** + \`\`\`bash + make format + \`\`\` + + Then commit and push the changes.`; + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: comment + }); \ No newline at end of file diff --git a/.github/workflows/pr-format-simple.yml b/.github/workflows/pr-format-simple.yml new file mode 100644 index 00000000..7d4c7bb4 --- /dev/null +++ b/.github/workflows/pr-format-simple.yml @@ -0,0 +1,68 @@ +name: PR Format Check (Simple) + +on: + pull_request: + types: [opened, synchronize] + +jobs: + check-commits: + name: Check Commit Format + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Check commit messages for PR number + run: | + PR_NUMBER=${{ github.event.pull_request.number }} + BASE_REF=${{ github.event.pull_request.base.ref }} + + echo "Checking commits for PR #${PR_NUMBER}" + echo "Base branch: ${BASE_REF}" + + # Fetch the base branch to ensure we have it + git fetch origin ${BASE_REF} + + # Get only the commits that are unique to this PR + COMMITS=$(git rev-list origin/${BASE_REF}..HEAD) + + if [ -z "$COMMITS" ]; then + echo "No new commits found in this PR" + exit 0 + fi + + MISSING_PR_NUMBER=false + echo "" + echo "New commits in this PR:" + echo "======================" + + for commit in $COMMITS; do + MESSAGE=$(git log -1 --pretty=%s $commit) + SHA_SHORT=$(echo $commit | cut -c1-7) + + # Check if this commit already has ANY PR number (it might be from another PR) + if echo "$MESSAGE" | grep -q "(#[0-9]\+)$"; then + echo "✅ ${SHA_SHORT}: ${MESSAGE}" + else + echo "❌ ${SHA_SHORT}: ${MESSAGE}" + echo " Missing PR number - should end with (#${PR_NUMBER})" + MISSING_PR_NUMBER=true + fi + done + + echo "" + if [ "$MISSING_PR_NUMBER" = true ]; then + echo "::error::Some commits are missing PR numbers. Please add (#${PR_NUMBER}) to commit messages." + echo "" + echo "To fix this, run:" + echo " ./scripts/add-pr-number.sh ${PR_NUMBER}" + echo "or:" + echo " git rebase origin/${{ github.event.pull_request.base.ref }} --exec 'git commit --amend -m \"\$(git log -1 --pretty=%s) (#${PR_NUMBER})\"'" + echo " git push --force-with-lease" + exit 1 + else + echo "::notice::All commits have PR numbers ✅" + fi \ No newline at end of file diff --git a/.github/workflows/pr-title-check.yml b/.github/workflows/pr-title-check.yml new file mode 100644 index 00000000..757e3497 --- /dev/null +++ b/.github/workflows/pr-title-check.yml @@ -0,0 +1,38 @@ +name: PR Title and Commit Check + +on: + pull_request: + types: [opened, edited, synchronize] + +jobs: + check-pr-title: + name: Check PR Title Format + runs-on: ubuntu-latest + permissions: + pull-requests: write + issues: write + + steps: + - name: Check PR title + uses: actions/github-script@v6 + with: + script: | + const prTitle = context.payload.pull_request.title; + const prNumber = context.payload.pull_request.number; + + // Check if PR title already has a PR number + const prNumberPattern = /\(#\d+\)$/; + if (prNumberPattern.test(prTitle)) { + console.log('PR title already contains a PR number'); + return; + } + + // Suggest adding PR number + const suggestedTitle = `${prTitle} (#${prNumber})`; + + // Simply log the suggestion instead of creating a comment + console.log(`::notice title=PR Title Suggestion::Consider updating PR title to: ${suggestedTitle}`); + + // Set output for other steps to use + core.setOutput('suggested_title', suggestedTitle); + core.setOutput('needs_pr_number', 'true'); \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..c2321b75 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,58 @@ +# Contributing to MCP C++ SDK + +## Pull Request Process + +### Merge Strategy + +This repository uses **"Rebase and merge"** as the default merge strategy to maintain a linear git history. + +#### Before Merging + +All commits in your PR should include the PR number. Our CI will check for this automatically. + +To add PR numbers to your commits: + +```bash +# Use our helper script +./scripts/add-pr-number.sh YOUR_PR_NUMBER + +# Or manually +git rebase origin/main --exec 'git commit --amend -m "$(git log -1 --pretty=%s) (#YOUR_PR_NUMBER)"' +git push --force-with-lease +``` + +#### Repository Settings + +The repository is configured to: +- ❌ Disallow merge commits +- ✅ Allow rebase merging (default) +- ✅ Allow squash merging (when appropriate) + +This ensures: +- Clean, linear git history +- Every commit is traceable to a PR +- No merge commit clutter + +### Code Style + +All code must be formatted according to our style guide: + +```bash +# Format your code +make format + +# Check formatting +make check-format +``` + +### Testing + +All tests must pass before merging: + +```bash +# Run all tests +make test + +# List available tests +make test-list +``` \ No newline at end of file diff --git a/scripts/add-pr-number.sh b/scripts/add-pr-number.sh new file mode 100755 index 00000000..5526d6dc --- /dev/null +++ b/scripts/add-pr-number.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# Script to add PR number to commit messages +# Usage: ./scripts/add-pr-number.sh + +set -e + +if [ $# -eq 0 ]; then + echo "Usage: $0 " + echo "Example: $0 123" + echo "" + echo "This script adds (#PR_NUMBER) to all commits since origin/main" + exit 1 +fi + +PR_NUMBER=$1 +BASE_BRANCH=${2:-origin/main} + +echo "Adding (#$PR_NUMBER) to commits since $BASE_BRANCH" +echo "" + +# Show commits that will be modified +echo "New commits in this branch (will add PR number to these):" +git log --oneline $BASE_BRANCH..HEAD | while read line; do + if echo "$line" | grep -q "(#[0-9]\+)$"; then + echo " ✓ $line (already has PR number)" + else + echo " → $line" + fi +done +echo "" + +read -p "Continue? (y/n) " -n 1 -r +echo "" +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Aborted." + exit 1 +fi + +# Perform the rebase +export PR_NUMBER +git rebase $BASE_BRANCH --exec 'git commit --amend -m "$(git log -1 --pretty=%s) (#$PR_NUMBER)"' + +echo "" +echo "✅ Successfully added (#$PR_NUMBER) to all commits!" +echo "" +echo "To push these changes:" +echo " git push --force-with-lease" +echo "" +echo "⚠️ Warning: This will rewrite history. Make sure you're on a feature branch!" \ No newline at end of file