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
60 changes: 60 additions & 0 deletions .github/scripts/check-plugin-version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/bin/bash

# Script to check if plugin files changed and version was bumped appropriately
# Used in CI to enforce version bumping when plugin code changes

set -e

BASE_REF=${1:-"main"}

echo "Comparing changes against origin/$BASE_REF..."

# Check if any files in plugin/ directory changed
PLUGIN_CHANGES=$(git diff --name-only origin/$BASE_REF...HEAD | grep '^plugin/' || true)

if [ -n "$PLUGIN_CHANGES" ]; then
echo "✓ Plugin files have changed:"
echo "$PLUGIN_CHANGES"
echo ""

# Check if plugin.json exists in base branch
if git cat-file -e origin/$BASE_REF:plugin/.claude-plugin/plugin.json 2>/dev/null; then
echo "Checking for version bump in existing plugin..."

# Check if plugin.json version changed
VERSION_CHANGED=$(git diff origin/$BASE_REF...HEAD -- plugin/.claude-plugin/plugin.json | grep '"version"' || true)

if [ -z "$VERSION_CHANGED" ]; then
echo "❌ ERROR: Plugin files were modified but version was not bumped!"
echo ""
echo "Please update the version in: plugin/.claude-plugin/plugin.json"
echo ""
echo "Changed files:"
echo "$PLUGIN_CHANGES"
exit 1
else
echo "✓ Plugin version has been updated:"
echo "$VERSION_CHANGED"

# Extract and display old and new versions
OLD_VERSION=$(git show origin/$BASE_REF:plugin/.claude-plugin/plugin.json | grep '"version"' | sed 's/.*"version": "\(.*\)".*/\1/')
NEW_VERSION=$(cat plugin/.claude-plugin/plugin.json | grep '"version"' | sed 's/.*"version": "\(.*\)".*/\1/')
echo ""
echo "Version change: $OLD_VERSION → $NEW_VERSION"
fi
else
echo "✓ New plugin detected - checking that version is set..."

# For new plugins, just verify a version exists
NEW_VERSION=$(cat plugin/.claude-plugin/plugin.json | grep '"version"' | sed 's/.*"version": "\(.*\)".*/\1/' || true)

if [ -z "$NEW_VERSION" ]; then
echo "❌ ERROR: plugin.json must have a version field!"
exit 1
else
echo "✓ Plugin version is set to: $NEW_VERSION"
fi
fi
else
echo "✓ No plugin files changed, version bump not required"
fi
90 changes: 90 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: Test Plugin

on:
pull_request:

jobs:
# First job: Check version bump on PR
check-version-bump:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Checkout PR branch
uses: actions/checkout@v3
with:
fetch-depth: 0 # Fetch all history for comparison

- name: Check if plugin files changed and version bumped
run: |
# Get the base branch (usually main)
git fetch origin ${{ github.event.pull_request.base.ref }}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we need this even though we aren't doing a shallow checkout?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I think so, because I thought we need this to get the currently version on main. I am not extremely familiar with how this is typically done though, so I am not very sure.

(I might merge this first since it is a CI thing that I can probably fix later. I want to make sure we have both the Claude Plugin ready and the Cursor hooks ready by the end of this week for people to try out. But I am more than happy to update the checks!)


# Run the version check script
chmod +x .github/scripts/check-plugin-version.sh
./.github/scripts/check-plugin-version.sh ${{ github.event.pull_request.base.ref }}

# Second job: Read the minimum version from file
read-version:
runs-on: ubuntu-latest
outputs:
min-version: ${{ steps.read-version.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Read minimum Semgrep version
id: read-version
run: |
VERSION=$(cat semgrep-version)
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Minimum Semgrep version: $VERSION"

# Third job: Test against multiple versions
test:
needs: read-version
runs-on: ubuntu-latest
strategy:
matrix:
semgrep-version:
- ${{ needs.read-version.outputs.min-version }} # Minimum version
- 'latest' # Latest stable version
fail-fast: false # Continue testing other versions even if one fails

name: test (${{ matrix.semgrep-version }})

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'

- name: Install Semgrep
run: |
if [ "${{ matrix.semgrep-version }}" = "latest" ]; then
pip install semgrep
else
pip install semgrep==${{ matrix.semgrep-version }}
fi

- name: Verify Semgrep installation
run: |
semgrep --version
INSTALLED=$(semgrep --version | head -n1 | awk '{print $1}')
echo "Installed version: $INSTALLED"
echo "Required minimum: ${{ needs.read-version.outputs.min-version }}"

- name: Check version compatibility
run: |
export CLAUDE_PLUGIN_ROOT="${GITHUB_WORKSPACE}/plugin"
chmod +x plugin/scripts/check_version.sh
./plugin/scripts/check_version.sh

# Run plugin hook tests
- name: Test plugin functionality
run: |
echo "Testing plugin with Semgrep ${{ matrix.semgrep-version }}"
export CLAUDE_PLUGIN_ROOT="${GITHUB_WORKSPACE}/plugin"
chmod +x tests/test_hooks.sh
./tests/test_hooks.sh
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Change Log
## 0.1.0
Initial release
13 changes: 13 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## Local development
- If you are in the directory of this repo, you can add the marketplace to your Claude Code instance by simply running
```
/plugin marketplace add ./
```

## Versioning
- Update the plugin version (in `plugin/.claude-plugin/plugin.json`) whenever the plugin changes
- Update `semgrep-version` if the change made to the plugin requires a newer version of `semgrep`
- `main` should always work on all versions of `semgrep` greater than the version stored in `semgrep-version`

## Testing
- In addition to the tests in CI, we should manually test that the plugin still works in Claude Code
56 changes: 56 additions & 0 deletions tests/test_hooks.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/bin/bash
set -e

# Main test runner for all hook tests

# Get the directory where this script is located
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"

# Source common utilities
source "${SCRIPT_DIR}/test_utils.sh"

# Print overall test header
print_separator
echo "Testing Claude Code Hooks"
print_separator
echo "CLAUDE_PLUGIN_ROOT: ${CLAUDE_PLUGIN_ROOT}"

# Track overall test status
ALL_TESTS_PASSED=true

# Run SessionStart hook test
echo ""
if bash "${SCRIPT_DIR}/test_session_start_hook.sh"; then
:
else
ALL_TESTS_PASSED=false
fi

# Run PostToolUse hook test
echo ""
if bash "${SCRIPT_DIR}/test_post_tool_use_hook.sh"; then
:
else
ALL_TESTS_PASSED=false
fi

# Run Inject Secure Defaults hook test
echo ""
if bash "${SCRIPT_DIR}/test_secure_defaults_hook.sh"; then
:
else
ALL_TESTS_PASSED=false
fi

# Print final summary
echo ""
print_separator
if [ "$ALL_TESTS_PASSED" = true ]; then
print_success "All hook tests passed!"
print_separator
exit 0
else
print_error "Some hook tests failed"
print_separator
exit 1
fi
Loading