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
76 changes: 76 additions & 0 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Contributing Guide

Thank you for considering contributing to Laravel Commit Lint!

## Prerequisites

- PHP >= 8.0
- Composer
- Git
- PHPUnit
- Bash (for hook scripts)

Ensure these are installed and available in your environment before contributing.

## How to Contribute

### Conventional Commits Example

```
feat(auth): add login functionality

fix(hook): correct regex for commit message validation
```

See [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for more details.

## Code of Conduct
Please be respectful and follow the [Contributor Covenant](https://www.contributor-covenant.org/).

## Reporting Issues
Open an issue with details and steps to reproduce.

For questions, reach out via GitHub Discussions or open an issue.


## Development

- Requires Bash, PHP, and PHPUnit to be available in your environment.

## Testing

Run tests and generate coverage reports and clover XML file:
```bash
vendor/bin/phpunit --colors=always --testdox
```

Coverage enforcement: The pre-push hook will block pushes if coverage is below 80%. Review the HTML report in the `coverage/` directory for details.

## Development Setup

To contribute to this project, please set up the git hooks to ensure commit message linting and test coverage enforcement:

1. **Copy hooks to your local git hooks directory:**
```sh
cp .github/hooks/* .git/hooks/
chmod +x .git/hooks/commit-msg .git/hooks/pre-push
```

2. **Verify hooks are executable:**
```sh
ls -l .git/hooks/commit-msg .git/hooks/pre-push
```

3. **Contribute as usual:**
- Make your changes.
- Commit with a message following the Conventional Commits format.
- Push your branch. The pre-push hook will run tests and enforce minimum 80% coverage.

If you encounter any issues with the hooks, ensure you have the required dependencies installed and that your environment allows execution of shell scripts.

## Resources

- [Laravel Package Development Documentation](https://laravel.com/docs/12.x/packages)
- [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
- [PHPUnit Documentation](https://phpunit.de/documentation.html)

13 changes: 13 additions & 0 deletions .github/SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Security Policy

## Reporting a Vulnerability
If you discover a security vulnerability, please email hello@mubbi.me or open a private issue on GitHub.

- Do not disclose vulnerabilities publicly until they are resolved.
- Provide details and steps to reproduce the issue.

## Supported Versions
Only the latest major version is supported for security updates.

## Responsible Disclosure
We appreciate responsible disclosure and will respond promptly to valid reports.
85 changes: 85 additions & 0 deletions .github/hooks/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/bin/sh
# commitlint.sh - Enforce Conventional Commits in commit messages
set -e

# Set up cleanup trap for any temporary files
trap 'rm -f /tmp/commitlint-* 2>/dev/null || true' EXIT

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

echo "${YELLOW}🔍 Validating commit message format...${NC}"

# Check if this is a merge commit
if [ -f .git/MERGE_HEAD ]; then
echo "${GREEN}✅ Merge commit detected, skipping validation${NC}"
exit 0
fi

# Read the commit message
COMMIT_MSG_FILE="$1"
if [ -z "$COMMIT_MSG_FILE" ]; then
echo "Usage: $0 <commit-msg-file>"
exit 1
fi

if [ ! -f "$COMMIT_MSG_FILE" ]; then
echo "${RED}❌ Commit message file not found: $COMMIT_MSG_FILE${NC}"
exit 1
fi

COMMIT_MSG=$(head -n1 "$COMMIT_MSG_FILE")

# Skip empty commits
if [ -z "$COMMIT_MSG" ] || [ "$COMMIT_MSG" = "" ]; then
echo "${RED}❌ Empty commit message${NC}"
exit 1
fi

# Skip special commits that don't need conventional format validation
# Also skip versioned commits like 10.x or [10.x] (to match workflow logic)
if printf "%s" "$COMMIT_MSG" | grep -Eq "^(Merge|WIP|Revert)"; then
echo "${GREEN}✅ Special commit type detected, skipping conventional format validation${NC}"
exit 0
fi

# Conventional Commits regex (type[optional scope]: subject)
CONVENTIONAL_REGEX='^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\([a-zA-Z0-9_-]+\))?: .{1,}'

# Log the commit message for debugging (safely quoted)
printf "${YELLOW}Checking message: %s${NC}\n" "$COMMIT_MSG"

if printf "%s" "$COMMIT_MSG" | grep -Eq "$CONVENTIONAL_REGEX"; then
echo "${GREEN}✅ Commit message format is valid${NC}"
exit 0
else
echo ""
echo "${RED}❌ Commit message does not follow Conventional Commits format!${NC}"
echo ""
echo "${YELLOW}Expected format:${NC}"
echo " ${GREEN}type(scope): description${NC}"
echo ""
echo "${YELLOW}Valid types:${NC}"
echo " feat: A new feature"
echo " fix: A bug fix"
echo " docs: Documentation only changes"
echo " style: Changes that do not affect the meaning of the code"
echo " refactor: A code change that neither fixes a bug nor adds a feature"
echo " test: Adding missing tests or correcting existing tests"
echo " chore: Changes to the build process or auxiliary tools"
echo " perf: A code change that improves performance"
echo " ci: Changes to CI configuration files and scripts"
echo " build: Changes that affect the build system"
echo " revert: Reverts a previous commit"
echo ""
echo "${YELLOW}Examples:${NC}"
echo " ${GREEN}feat(auth): add user authentication${NC}"
echo " ${GREEN}fix(api): resolve validation error in user endpoint${NC}"
echo " ${GREEN}docs: update API documentation${NC}"
echo ""
echo "See: https://www.conventionalcommits.org/en/v1.0.0/"
exit 1
fi
57 changes: 57 additions & 0 deletions .github/hooks/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/bin/sh
# Git pre-push hook to run PHPUnit tests and enforce minimum coverage threshold

set -e

COVERAGE_FILE="coverage/coverage.xml"
MIN_COVERAGE=80

# Run PHPUnit with Clover coverage
vendor/bin/phpunit

if [ ! -f "$COVERAGE_FILE" ]; then
echo "[pre-push] ❌ Coverage report not found at $COVERAGE_FILE. Push aborted."
exit 1
fi

# Extract total statements and covered statements using POSIX-compliant sed
STATEMENTS=$(sed -n 's/.*statements="\([0-9][0-9]*\)".*/\1/p' "$COVERAGE_FILE" | head -n 1)
COVERED=$(sed -n 's/.*coveredstatements="\([0-9][0-9]*\)".*/\1/p' "$COVERAGE_FILE" | head -n 1)

# Debug info (optional)
echo "[pre-push] Detected Statements: ${STATEMENTS:-<missing>}, Covered: ${COVERED:-<missing>}"

# Validate parsed values
if ! echo "$STATEMENTS" | grep -Eq '^[0-9]+$'; then
echo "[pre-push] ❌ Invalid or missing statements count. Push aborted."
exit 1
fi
if ! echo "$COVERED" | grep -Eq '^[0-9]+$'; then
echo "[pre-push] ❌ Invalid or missing covered statements count. Push aborted."
exit 1
fi

if [ "$STATEMENTS" -eq 0 ]; then
echo "[pre-push] ❌ Zero statements found. Coverage meaningless. Push aborted."
exit 1
fi

# Calculate percentage with POSIX awk
COVERAGE_PERCENT=$(awk "BEGIN {printf \"%.2f\", ($COVERED / $STATEMENTS) * 100}")

# Validate percentage output
if [ -z "$COVERAGE_PERCENT" ]; then
echo "[pre-push] ❌ Could not calculate coverage percent. Push aborted."
exit 1
fi

# Float comparison using awk (portable)
IS_BELOW=$(awk "BEGIN {print ($COVERAGE_PERCENT < $MIN_COVERAGE) ? 1 : 0}")

if [ "$IS_BELOW" -eq 1 ]; then
echo "[pre-push] ❌ Test coverage ${COVERAGE_PERCENT}% is below required ${MIN_COVERAGE}%. Push aborted."
exit 1
else
echo "[pre-push] ✅ Test coverage ${COVERAGE_PERCENT}% meets minimum ${MIN_COVERAGE}%. Push allowed."
exit 0
fi
Loading