Initial commit
This commit is contained in:
15
.claude-plugin/plugin.json
Normal file
15
.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "git",
|
||||||
|
"description": "Git Development Workflows",
|
||||||
|
"version": "1.1.1",
|
||||||
|
"author": {
|
||||||
|
"name": "Meng Yan",
|
||||||
|
"email": "myan@redhat.com"
|
||||||
|
},
|
||||||
|
"skills": [
|
||||||
|
"./skills"
|
||||||
|
],
|
||||||
|
"commands": [
|
||||||
|
"./commands"
|
||||||
|
]
|
||||||
|
}
|
||||||
35
commands/commit-push.md
Normal file
35
commands/commit-push.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
argument-hint: "[--no-push] or leave empty to commit and push"
|
||||||
|
description: "Commit changes with sign-off and push to origin by default"
|
||||||
|
allowed-tools: [Bash]
|
||||||
|
---
|
||||||
|
|
||||||
|
Commit staged and unstaged changes with proper sign-off, and push to origin branch by default.
|
||||||
|
|
||||||
|
## Implementation Steps
|
||||||
|
|
||||||
|
1. **Check Repository Status**: Run git status and git diff to review changes that will be committed
|
||||||
|
2. **Review Recent Commits**: Check git log to follow existing commit message style
|
||||||
|
3. **Stage and Commit Changes**: Add relevant files and create signed commit with descriptive message following conventional format
|
||||||
|
4. **Push to Origin** (unless --no-push specified): Push committed changes to the current origin branch
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
- `/git:commit-push` - Commit with sign-off and push to origin (default)
|
||||||
|
- `/git:commit-push --no-push` - Commit with sign-off only, skip push
|
||||||
|
|
||||||
|
## Implementation Details
|
||||||
|
|
||||||
|
The command will:
|
||||||
|
- Analyze changes with `git status` and `git diff`
|
||||||
|
- Check recent commits with `git log --oneline -5` for style consistency
|
||||||
|
- Stage relevant modified and new files (avoiding unnecessary config files)
|
||||||
|
- Create commit with `-s` flag for sign-off and conventional format
|
||||||
|
- Push to origin by default unless `--no-push` parameter is provided
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- Follows conventional commit format with descriptive messages
|
||||||
|
- Always includes sign-off as per user preferences
|
||||||
|
- Avoids staging unnecessary files like configuration files
|
||||||
|
- Verifies commit success before push (unless disabled with --no-push)
|
||||||
|
- Respects current branch for push operations
|
||||||
23
commands/compact-commits.md
Normal file
23
commands/compact-commits.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
argument-hint: [pr_number]
|
||||||
|
description: Compact all commits in a GitHub PR into a single commit with comprehensive message and DCO sign-off
|
||||||
|
allowed-tools: [Bash]
|
||||||
|
---
|
||||||
|
|
||||||
|
Compact multiple commits in a GitHub Pull Request into a single commit while preserving all changes and creating a comprehensive commit message with proper DCO sign-off.
|
||||||
|
|
||||||
|
## Implementation Steps
|
||||||
|
|
||||||
|
1. **Examine PR Structure**: View the PR details and commit history to understand what needs to be compacted using `gh pr view $1`
|
||||||
|
2. **Checkout PR Branch**: Switch to the PR branch using `gh pr checkout $1` to work with the commits locally
|
||||||
|
3. **Compact Commits**: Use `git reset --soft HEAD~N` (where N is number of commits) to stage all changes from multiple commits, then create a single comprehensive commit with `git commit --signoff`
|
||||||
|
4. **Force Push Update**: Push the compacted commit back to the PR branch using `git push --force-with-lease` to update the remote PR
|
||||||
|
5. **Verify Results**: Confirm the PR now shows only one commit with all the original changes preserved and proper DCO sign-off
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- This command is useful for cleaning up PR history before merging
|
||||||
|
- Preserves all code changes while creating a clean, single commit
|
||||||
|
- Includes DCO sign-off required by many projects
|
||||||
|
- The comprehensive commit message includes all relevant details from the original commits
|
||||||
|
- Uses `--force-with-lease` for safer force pushing
|
||||||
|
- Works with any GitHub repository that has PR access
|
||||||
77
commands/create-pr.md
Normal file
77
commands/create-pr.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# Create PR to $ARGUMENTS
|
||||||
|
|
||||||
|
Create a pull request following Git best practices:
|
||||||
|
|
||||||
|
## Steps:
|
||||||
|
|
||||||
|
1. **Sync & Branch**: Update main + create feature branch with appropriate prefix
|
||||||
|
2. **Commit**: Stage relevant files + commit with conventional message and sign-off
|
||||||
|
3. **Push & PR**: Push branch + create ready PR with detailed description
|
||||||
|
|
||||||
|
## Target:
|
||||||
|
- If $ARGUMENTS provided: use as target repo/branch
|
||||||
|
- If empty: default to upstream/main
|
||||||
|
|
||||||
|
## Commands to execute:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set target
|
||||||
|
TARGET=${ARGUMENTS:-upstream/main}
|
||||||
|
|
||||||
|
# 1. Sync & Branch
|
||||||
|
CURRENT_BRANCH=$(git branch --show-current)
|
||||||
|
if [[ "$CURRENT_BRANCH" == "main" || "$CURRENT_BRANCH" == "master" ]]; then
|
||||||
|
# Sync with upstream first
|
||||||
|
git fetch origin
|
||||||
|
git pull origin main
|
||||||
|
|
||||||
|
# Create feature branch with prefix
|
||||||
|
# Choose: feature/, fix/, docs/, chore/, refactor/, test/
|
||||||
|
git checkout -b feature/descriptive-name
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Stage & Commit with conventional format
|
||||||
|
git add path/to/relevant/files
|
||||||
|
git commit -s -m "feat: descriptive title
|
||||||
|
|
||||||
|
Detailed description of what and why this change is made.
|
||||||
|
|
||||||
|
- List specific changes
|
||||||
|
- Reference issue numbers if applicable"
|
||||||
|
|
||||||
|
# 3. Push & Create PR
|
||||||
|
git push -u origin $(git branch --show-current)
|
||||||
|
gh pr create --base ${TARGET#*/} --title "Title" --body "$(cat <<'EOF'
|
||||||
|
## Summary
|
||||||
|
Brief description of the change and its purpose
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
- Specific change 1
|
||||||
|
- Specific change 2
|
||||||
|
- Reference any related issues
|
||||||
|
|
||||||
|
## Test plan
|
||||||
|
- [ ] Unit tests pass
|
||||||
|
- [ ] Integration tests pass
|
||||||
|
- [ ] Manual testing completed
|
||||||
|
- [ ] Documentation updated if needed
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
- [ ] Code follows project conventions
|
||||||
|
- [ ] Tests added/updated
|
||||||
|
- [ ] Documentation updated
|
||||||
|
- [ ] Breaking changes documented
|
||||||
|
|
||||||
|
🤖 Generated with [Claude Code](https://claude.ai/code)
|
||||||
|
EOF
|
||||||
|
)"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conventional Commit Types:
|
||||||
|
- **feat**: New features
|
||||||
|
- **fix**: Bug fixes
|
||||||
|
- **docs**: Documentation changes
|
||||||
|
- **style**: Code style changes (formatting, etc.)
|
||||||
|
- **refactor**: Code refactoring
|
||||||
|
- **test**: Adding/updating tests
|
||||||
|
- **chore**: Maintenance tasks
|
||||||
57
commands/create-worktree.md
Normal file
57
commands/create-worktree.md
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
argument-hint: [worktree-name-or-description] [source-branch] [--cursor|--code|--tmux]
|
||||||
|
description: Create a git worktree with intelligent naming and branch creation
|
||||||
|
allowed-tools: [Bash, TodoWrite]
|
||||||
|
---
|
||||||
|
|
||||||
|
Creates a new git worktree with automatic naming conventions and branch management. Supports optional parameters for customization and integration with editors/tmux.
|
||||||
|
|
||||||
|
## Implementation Steps
|
||||||
|
|
||||||
|
1. **Plan Worktree Creation**: Create todo list to track worktree creation and navigation tasks
|
||||||
|
|
||||||
|
2. **Parse Arguments**:
|
||||||
|
- Check if arguments contain `--cursor`, `--code`, or `--tmux` flags
|
||||||
|
- Extract worktree name/description from remaining arguments (filter out flags)
|
||||||
|
- Extract source branch if provided (second non-flag argument)
|
||||||
|
|
||||||
|
3. **Determine Worktree Configuration**:
|
||||||
|
- If worktree name provided: Use as worktree name/description to generate appropriate directory name and branch name
|
||||||
|
- If not provided: Analyze current git status and staged changes to auto-generate meaningful worktree and branch names
|
||||||
|
- Use source branch if provided, otherwise default to current branch
|
||||||
|
|
||||||
|
4. **Create Worktree with Smart Naming**:
|
||||||
|
- Generate worktree directory using format: `../project-name__feature-name` (double underscore separator)
|
||||||
|
- Create new branch with descriptive name based on the worktree purpose
|
||||||
|
- Use command: `git worktree add ../worktree-directory -b branch-name source-branch`
|
||||||
|
|
||||||
|
5. **Open in Editor/Terminal** (if flag specified):
|
||||||
|
- If `--cursor` flag: Run `cursor <worktree-absolute-path>` to open in Cursor editor
|
||||||
|
- If `--code` flag: Run `code <worktree-absolute-path>` to open in VS Code
|
||||||
|
- If `--tmux` flag: Run `tmux split-window -h -c <worktree-absolute-path>` to split pane in current tmux session and cd to worktree
|
||||||
|
- If no flag: Skip this step
|
||||||
|
|
||||||
|
6. **Complete Task Tracking**: Mark all todo items as completed
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- Uses double underscore (`__`) separator between project name and feature name for clear visual distinction
|
||||||
|
- Automatically handles branch name conflicts by generating unique names
|
||||||
|
- If no parameters provided, analyzes git diff and status to suggest appropriate names
|
||||||
|
- Worktree is created in parent directory to avoid conflicts with current repository
|
||||||
|
- Source branch defaults to current branch if not specified
|
||||||
|
|
||||||
|
## Editor/Terminal Integration
|
||||||
|
- `--cursor`: Opens the worktree in Cursor editor after creation
|
||||||
|
- `--code`: Opens the worktree in VS Code after creation
|
||||||
|
- `--tmux`: Creates a horizontal split pane in current tmux session and navigates to worktree
|
||||||
|
- Flags can be placed anywhere in the command arguments
|
||||||
|
- Only one editor/terminal flag should be used at a time
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
- `/git:create-worktree integration-logs` - Creates worktree for integration log work
|
||||||
|
- `/git:create-worktree "fix auth bug" develop` - Creates worktree from develop branch for auth fix
|
||||||
|
- `/git:create-worktree` - Auto-generates name based on current changes
|
||||||
|
- `/git:create-worktree fix-e2e --cursor` - Creates worktree and opens in Cursor editor
|
||||||
|
- `/git:create-worktree "add feature" --code` - Creates worktree and opens in VS Code
|
||||||
|
- `/git:create-worktree refactor develop --tmux` - Creates worktree from develop and opens in new tmux pane
|
||||||
|
- `/git:create-worktree --cursor bug-fix` - Creates worktree and opens in Cursor (flag can be anywhere)
|
||||||
118
commands/rebase-pr.md
Normal file
118
commands/rebase-pr.md
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
---
|
||||||
|
argument-hint: [pr_number] [remote] - PR number (optional, defaults to all PRs) and remote name (optional, defaults to PR's target remote)
|
||||||
|
description: Rebase a PR or all open PRs against their target base branch and force push updates
|
||||||
|
allowed-tools: [Bash, TodoWrite, Read, Edit]
|
||||||
|
---
|
||||||
|
|
||||||
|
Rebase a pull request against its target base branch (from the PR's merge target) and force push the updates. If no PR number is specified, rebases all open PRs.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Rebase All Open PRs
|
||||||
|
```bash
|
||||||
|
/git:rebase-pr [remote]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rebase Single PR
|
||||||
|
```bash
|
||||||
|
/git:rebase-pr <pr_number> [remote]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation Steps
|
||||||
|
|
||||||
|
### Determine Mode
|
||||||
|
|
||||||
|
1. **Parse Arguments**: Check if first argument is a PR number or a remote name
|
||||||
|
- If first argument is numeric: Single PR mode
|
||||||
|
- If first argument is non-numeric or empty: Batch mode (all PRs)
|
||||||
|
- Extract remote name from appropriate position (optional)
|
||||||
|
|
||||||
|
### Single PR Mode (when pr_number is provided)
|
||||||
|
|
||||||
|
1. **Get PR Information**: Use `gh pr view <pr_number> --json baseRefName,headRepository,isCrossRepository` to get:
|
||||||
|
- `baseRefName`: The target branch this PR will merge into (e.g., "main", "develop")
|
||||||
|
- `headRepository`: The source repository info
|
||||||
|
- `isCrossRepository`: Whether this is a fork PR
|
||||||
|
2. **Determine Remote and Base Branch**:
|
||||||
|
- If `remote` argument is provided: Use `<remote>/<baseRefName>`
|
||||||
|
- If PR is cross-repository (fork): Use `upstream/<baseRefName>`
|
||||||
|
- Otherwise: Use `origin/<baseRefName>`
|
||||||
|
3. **Fetch Latest Changes**: Run `git fetch <remote>` to ensure we have the latest commits from the target remote
|
||||||
|
4. **Checkout PR Branch**: Switch to the PR branch using `gh pr checkout <pr_number>`
|
||||||
|
5. **Rebase Against Target Base**: Rebase current branch against `<remote>/<baseRefName>`
|
||||||
|
6. **Handle Conflicts**: If conflicts occur, stop and report to user for manual resolution
|
||||||
|
7. **Force Push Updates**: Push the rebased branch with `--force-with-lease` to update the PR safely
|
||||||
|
|
||||||
|
### Batch Mode (when no pr_number is provided)
|
||||||
|
|
||||||
|
1. **Create Todo List**: Create a todo list to track all PRs that need rebasing
|
||||||
|
2. **List All Open PRs**: Use `gh pr list --author "@me" --state open --json number,headRefName,baseRefName,isCrossRepository` to get all user's open PRs with their target branches
|
||||||
|
3. **Check Worktrees**: Use `git worktree list` to identify which PRs are in worktrees vs main directory
|
||||||
|
4. **Process Each PR Sequentially**:
|
||||||
|
- Update todo status to "in_progress" for current PR
|
||||||
|
- Get PR's `baseRefName` and `isCrossRepository` from the PR list
|
||||||
|
- Determine remote: Use provided remote, or `upstream` for forks, or `origin` for same-repo PRs
|
||||||
|
- Fetch latest changes: `git fetch <remote>`
|
||||||
|
- For worktree branches: Navigate to worktree directory and run `git rebase <remote>/<baseRefName>`
|
||||||
|
- For non-worktree branches: Use `gh pr checkout <pr_number>` then rebase against `<remote>/<baseRefName>`
|
||||||
|
- If rebase succeeds and branch is up-to-date: Mark as completed, continue to next PR
|
||||||
|
- If rebase succeeds with changes: Force push with `--force-with-lease`, mark as completed
|
||||||
|
- If rebase has conflicts:
|
||||||
|
- Read the conflicted files
|
||||||
|
- Resolve conflicts using Edit tool
|
||||||
|
- Stage resolved files with `git add`
|
||||||
|
- Continue rebase with `git rebase --continue`
|
||||||
|
- Force push after successful resolution
|
||||||
|
- Mark PR as completed in todo list
|
||||||
|
- Handle push failures (e.g., network issues) with retry logic
|
||||||
|
5. **Summary Report**: Display final status of all PRs (success/failed/skipped)
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
- `pr_number`: The GitHub PR number to rebase (optional, if not provided will rebase all open PRs)
|
||||||
|
- `remote`: The remote name to fetch and rebase against (optional, defaults to `upstream` for fork PRs or `origin` for same-repo PRs)
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Rebase all open PRs against their target branches (auto-detects upstream/origin)
|
||||||
|
/git:rebase-pr
|
||||||
|
|
||||||
|
# Rebase all open PRs, explicitly using upstream remote
|
||||||
|
/git:rebase-pr upstream
|
||||||
|
|
||||||
|
# Rebase all open PRs using origin remote
|
||||||
|
/git:rebase-pr origin
|
||||||
|
|
||||||
|
# Rebase single PR #2067 against its target branch (auto-detects remote)
|
||||||
|
/git:rebase-pr 2067
|
||||||
|
|
||||||
|
# Rebase single PR #2067 using upstream remote
|
||||||
|
/git:rebase-pr 2067 upstream
|
||||||
|
|
||||||
|
# Rebase single PR #2067 using origin remote
|
||||||
|
/git:rebase-pr 2067 origin
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- **Automatic Mode Detection**: The command automatically detects whether to rebase a single PR or all PRs based on whether a numeric PR number is provided
|
||||||
|
- **Target Branch Detection**: Each PR is rebased against its actual target branch (from `baseRefName`) rather than a hardcoded branch. For example:
|
||||||
|
- PR targeting `main` → rebases against `upstream/main` or `origin/main`
|
||||||
|
- PR targeting `develop` → rebases against `upstream/develop` or `origin/develop`
|
||||||
|
- PR targeting `release-1.0` → rebases against `upstream/release-1.0` or `origin/release-1.0`
|
||||||
|
- **Remote Selection**:
|
||||||
|
- For fork PRs (`isCrossRepository: true`): Defaults to `upstream` remote
|
||||||
|
- For same-repo PRs: Defaults to `origin` remote
|
||||||
|
- User can override by providing explicit remote name
|
||||||
|
- **Conflict Handling**: When conflicts occur in batch mode, the tool will attempt to resolve simple conflicts automatically using the Edit tool. For complex conflicts, it will stop and ask user for guidance
|
||||||
|
- **Worktree Support**: The command automatically detects and handles PRs in git worktrees by navigating to the worktree directory before rebasing
|
||||||
|
- **Force Push Safety**: Uses `--force-with-lease` for safer force pushing that prevents overwriting unexpected changes
|
||||||
|
- **Todo Tracking**: Uses TodoWrite tool to track progress when rebasing multiple PRs
|
||||||
|
- **Network Issues**: If push fails due to network issues, the command will retry once before reporting failure
|
||||||
|
- **Skip Up-to-date PRs**: PRs that are already up-to-date with the base branch are quickly marked as completed without requiring a push
|
||||||
|
- **Prerequisites**:
|
||||||
|
- GitHub CLI (`gh`) must be authenticated
|
||||||
|
- Must have appropriate permissions to push to PR branch
|
||||||
|
- The appropriate remote (`upstream` or `origin`) must be configured
|
||||||
|
- **Sign-off**: Remember to sign-off commits if making any new commits during the conflict resolution process
|
||||||
49
commands/review-pr.md
Normal file
49
commands/review-pr.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# Review PR $ARGUMENTS
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Show open PRs if no argument
|
||||||
|
[[ -z "$ARGUMENTS" ]] && gh pr list --state open && exit 0
|
||||||
|
|
||||||
|
PR_NUMBER="$ARGUMENTS"
|
||||||
|
REPO_INFO=$(gh repo view --json owner,name --jq '.owner.login + "/" + .name')
|
||||||
|
COMMIT_SHA=$(gh api repos/$REPO_INFO/pulls/$PR_NUMBER --jq '.head.sha')
|
||||||
|
|
||||||
|
# Quick overview
|
||||||
|
gh pr view $PR_NUMBER
|
||||||
|
gh pr checks $PR_NUMBER
|
||||||
|
gh pr diff $PR_NUMBER
|
||||||
|
```
|
||||||
|
|
||||||
|
## What to Look For
|
||||||
|
|
||||||
|
🔒 **Security first**: Auth, input validation, secrets, injection attacks
|
||||||
|
🐛 **Logic bugs**: Edge cases, race conditions, error handling
|
||||||
|
🏗️ **Code quality**: Patterns, duplication, naming, complexity
|
||||||
|
~~ 🧪 **Tests**: Coverage, meaningful scenarios, integration tests ~~
|
||||||
|
📝 **Docs**: Clear code, comments where needed, breaking changes
|
||||||
|
|
||||||
|
## Leave Comments
|
||||||
|
|
||||||
|
**For issues (blocking):**
|
||||||
|
```bash
|
||||||
|
gh api repos/$REPO_INFO/pulls/$PR_NUMBER/comments --method POST \
|
||||||
|
--field body="🔒 Security risk: [issue]. Fix: [solution]" \
|
||||||
|
--field commit_id="$COMMIT_SHA" --field path="file.js" --field line=42 --field side="RIGHT"
|
||||||
|
```
|
||||||
|
|
||||||
|
**For suggestions:**
|
||||||
|
```bash
|
||||||
|
gh api repos/$REPO_INFO/pulls/$PR_NUMBER/comments --method POST \
|
||||||
|
--field body="💡 Consider [improvement] for [benefit]" \
|
||||||
|
--field commit_id="$COMMIT_SHA" --field path="file.js" --field line=42 --field side="RIGHT"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Final Decision
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Block it
|
||||||
|
gh pr review $PR_NUMBER --request-changes --body "Security/bug issues found"
|
||||||
|
|
||||||
|
# Ship it
|
||||||
|
gh pr review $PR_NUMBER --approve --body "LGTM"
|
||||||
|
```
|
||||||
85
plugin.lock.json
Normal file
85
plugin.lock.json
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
{
|
||||||
|
"$schema": "internal://schemas/plugin.lock.v1.json",
|
||||||
|
"pluginId": "gh:yanmxa/cc-plugins:plugins/git",
|
||||||
|
"normalized": {
|
||||||
|
"repo": null,
|
||||||
|
"ref": "refs/tags/v20251128.0",
|
||||||
|
"commit": "f86166bff717c5adfe7ed70c74ef93ef5bc08c2c",
|
||||||
|
"treeHash": "b1a3174a75d23221475a521800af84cfd3914d957e9a7eda050eb11169276fa0",
|
||||||
|
"generatedAt": "2025-11-28T10:29:09.576060Z",
|
||||||
|
"toolVersion": "publish_plugins.py@0.2.0"
|
||||||
|
},
|
||||||
|
"origin": {
|
||||||
|
"remote": "git@github.com:zhongweili/42plugin-data.git",
|
||||||
|
"branch": "master",
|
||||||
|
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
|
||||||
|
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
|
||||||
|
},
|
||||||
|
"manifest": {
|
||||||
|
"name": "git",
|
||||||
|
"description": "Git Development Workflows",
|
||||||
|
"version": "1.1.1"
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"path": "README.md",
|
||||||
|
"sha256": "332bd388871352973e11a3ea0d915231d553a6e2e95fc4cd6c0f03ab31da12bb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": ".claude-plugin/plugin.json",
|
||||||
|
"sha256": "cd69ce3b3e39045d5da0c08a34a78e0118b34af6171aa6a91acbbac305486299"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "commands/review-pr.md",
|
||||||
|
"sha256": "f7011605ade6baa1ce978c3f185bbbe7b0e8e9c775217472f69ffcd3780481b9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "commands/compact-commits.md",
|
||||||
|
"sha256": "a31bce01d000dc13acc8926341c3079bdd13cf36b1c3977c116d32bc9d03f54a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "commands/create-pr.md",
|
||||||
|
"sha256": "1aa2b5fc19c9c28d0a9d73886c04f1272c636ac09f18ad6d497486108b672005"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "commands/create-worktree.md",
|
||||||
|
"sha256": "3a12abcf95442df516044073848888a8e829538e878891a28c2474c49db18c46"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "commands/commit-push.md",
|
||||||
|
"sha256": "d2b4215bd7b2a5e2a43873507d1d918b6635081fe7d9191f208cb45cb5b2c2b4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "commands/rebase-pr.md",
|
||||||
|
"sha256": "fb9dadb83a1f31c40bb1de0b83468050ddb0f0118da9c972750e0ac8da32a266"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/pr/SKILL.md",
|
||||||
|
"sha256": "b53e79a949eae4cda7f6ba1457c037eabce6669a7f4d24fe5c192d7dae36c3a2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/pr/scripts/03-create-pr.sh",
|
||||||
|
"sha256": "2dc070d899acc9f8eb199b7b933f10dec7bd633637a1ec9c1d9c0d29538a145c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/pr/scripts/00-pr-workflow.sh",
|
||||||
|
"sha256": "49d95e9bf05172502dd4221188c06c3fa7f086fa6f40e472d75bb603ca54b7e4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/pr/scripts/01-fork-and-setup.sh",
|
||||||
|
"sha256": "5b84acf7f71038881285c3b3ec3ea7ba9df49f56db143d9c47f4f812c6a98a08"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/pr/scripts/02-check-guidelines.sh",
|
||||||
|
"sha256": "ba49dccb5d4b470111bf6b6620d9f63b5682496f3916a4ffb96ea0854ef759dd"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dirSha256": "b1a3174a75d23221475a521800af84cfd3914d957e9a7eda050eb11169276fa0"
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"scannedAt": null,
|
||||||
|
"scannerVersion": null,
|
||||||
|
"flags": []
|
||||||
|
}
|
||||||
|
}
|
||||||
212
skills/pr/SKILL.md
Normal file
212
skills/pr/SKILL.md
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
---
|
||||||
|
name: pr
|
||||||
|
description: Automate creating pull requests - fork repos, create branches, commit changes, and submit PRs. Works in current directory or creates new clone. Idempotent and safe to re-run. Keywords - PR, pull request, fork, contribute, upstream.
|
||||||
|
allowed-tools: [Bash, Read, Write, Edit, Glob, Grep]
|
||||||
|
---
|
||||||
|
|
||||||
|
# PR - Pull Request Automation
|
||||||
|
|
||||||
|
Automate creating pull requests from start to finish.
|
||||||
|
|
||||||
|
## What This Does
|
||||||
|
|
||||||
|
1. Fork the repository (if needed)
|
||||||
|
2. Clone or use existing local repo
|
||||||
|
3. Create feature branch from upstream
|
||||||
|
4. Auto-commit your changes
|
||||||
|
5. Push and create PR
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
**Basic workflow:**
|
||||||
|
```bash
|
||||||
|
# 1. Fork, clone, and create branch
|
||||||
|
bash ~/.claude/skills/pr/scripts/01-fork-and-setup.sh \
|
||||||
|
owner/repo \
|
||||||
|
~/code \
|
||||||
|
1 \
|
||||||
|
main \
|
||||||
|
my-feature
|
||||||
|
|
||||||
|
# 2. Make your changes (use Edit tool)...
|
||||||
|
|
||||||
|
# 3. Auto-commit and create PR
|
||||||
|
bash ~/.claude/skills/pr/scripts/03-create-pr.sh \
|
||||||
|
main \
|
||||||
|
"PR title" \
|
||||||
|
"PR description"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Current directory mode:**
|
||||||
|
```bash
|
||||||
|
cd /path/to/your/repo
|
||||||
|
bash ~/.claude/skills/pr/scripts/01-fork-and-setup.sh owner/repo . "" main my-feature
|
||||||
|
# Make changes...
|
||||||
|
bash ~/.claude/skills/pr/scripts/03-create-pr.sh main "Fix bug"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Scripts
|
||||||
|
|
||||||
|
### `01-fork-and-setup.sh` - Setup repo and branch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
01-fork-and-setup.sh <repo> [work_dir] [depth] [base_branch] [feature_branch]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `repo` - Repository to fork (e.g., `owner/repo`)
|
||||||
|
- `work_dir` - Where to clone (default: `~/tmp/contribute`, use `.` for current dir)
|
||||||
|
- `depth` - Clone depth (default: full, use `1` for 10x faster shallow clone)
|
||||||
|
- `base_branch` - Branch to base work on (e.g., `main`)
|
||||||
|
- `feature_branch` - Your new branch name (e.g., `fix/bug-123`)
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
- Creates fork if needed (detects existing forks, even with different names)
|
||||||
|
- Clones repo or uses existing local copy
|
||||||
|
- Sets up `upstream` (original) and `origin` (fork) remotes
|
||||||
|
- Creates feature branch from latest upstream
|
||||||
|
- **Idempotent** - Safe to re-run multiple times
|
||||||
|
|
||||||
|
### `03-create-pr.sh` - Commit and create PR
|
||||||
|
|
||||||
|
```bash
|
||||||
|
03-create-pr.sh <base_branch> <pr_title> [pr_body] [commit_message]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `base_branch` - Target branch for PR (e.g., `main`)
|
||||||
|
- `pr_title` - PR title
|
||||||
|
- `pr_body` - PR description (optional)
|
||||||
|
- `commit_message` - Commit message (optional, defaults to PR title)
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
- Auto-commits any uncommitted changes with DCO sign-off
|
||||||
|
- Pushes branch to your fork
|
||||||
|
- Checks for existing PRs (avoids duplicates)
|
||||||
|
- Creates PR to upstream
|
||||||
|
- Returns PR URL
|
||||||
|
- **Idempotent** - Won't create duplicate PRs
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Example 1: Fix bug in upstream repo
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# User request: "Fix version 2.9 to 2.15 in stolostron/multicluster-global-hub"
|
||||||
|
|
||||||
|
# Step 1: Setup
|
||||||
|
bash ~/.claude/skills/pr/scripts/01-fork-and-setup.sh \
|
||||||
|
stolostron/multicluster-global-hub \
|
||||||
|
~/tmp/contribute \
|
||||||
|
1 \
|
||||||
|
main \
|
||||||
|
docs/fix-version
|
||||||
|
|
||||||
|
# Step 2: Make changes (using Edit tool to modify files)
|
||||||
|
# ... Edit files to change 2.9 to 2.15 ...
|
||||||
|
|
||||||
|
# Step 3: Create PR
|
||||||
|
bash ~/.claude/skills/pr/scripts/03-create-pr.sh \
|
||||||
|
main \
|
||||||
|
"docs: update version from 2.9 to 2.15" \
|
||||||
|
"Update documentation links to point to 2.15 instead of 2.9"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: Work in current directory
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# User is already in a git repo and wants to work there
|
||||||
|
|
||||||
|
cd /path/to/existing/repo
|
||||||
|
|
||||||
|
bash ~/.claude/skills/pr/scripts/01-fork-and-setup.sh \
|
||||||
|
owner/repo \
|
||||||
|
. \
|
||||||
|
"" \
|
||||||
|
main \
|
||||||
|
fix/issue-123
|
||||||
|
|
||||||
|
# Make changes...
|
||||||
|
|
||||||
|
bash ~/.claude/skills/pr/scripts/03-create-pr.sh \
|
||||||
|
main \
|
||||||
|
"Fix issue #123"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 3: Handles fork name mismatches
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Scenario: Your fork is named "hub-of-hubs" but upstream is "multicluster-global-hub"
|
||||||
|
# The script automatically detects this and handles it
|
||||||
|
|
||||||
|
bash ~/.claude/skills/pr/scripts/01-fork-and-setup.sh \
|
||||||
|
stolostron/multicluster-global-hub \
|
||||||
|
~/code \
|
||||||
|
1 \
|
||||||
|
main \
|
||||||
|
fix/bug
|
||||||
|
|
||||||
|
# Output: "Found existing fork with different name: yanmxa/hub-of-hubs"
|
||||||
|
# Script uses the correct fork name automatically
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
|
||||||
|
✅ **Idempotent** - Safe to re-run, won't duplicate work
|
||||||
|
✅ **Smart fork detection** - Finds forks even with different names
|
||||||
|
✅ **Auto-fork** - Creates fork if it doesn't exist
|
||||||
|
✅ **Auto-commit** - Commits changes with DCO sign-off
|
||||||
|
✅ **No duplicates** - Checks for existing PRs before creating
|
||||||
|
✅ **Current dir mode** - Can work in existing repos (use `.` as work_dir)
|
||||||
|
✅ **Fast clone** - Shallow clone by default (10x faster)
|
||||||
|
✅ **Proper remotes** - Sets up upstream (HTTPS) and origin (SSH)
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
**Clone speed with depth=1:**
|
||||||
|
| Repository | Full Clone | Shallow (depth=1) | Speedup |
|
||||||
|
|------------|------------|-------------------|---------|
|
||||||
|
| kubernetes | ~3GB | ~300MB | 10x |
|
||||||
|
| Linux kernel | ~4GB | ~400MB | 10x |
|
||||||
|
| Typical repo | ~500MB | ~50MB | 10x |
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- **GitHub CLI (`gh`)** - Must be installed and authenticated
|
||||||
|
```bash
|
||||||
|
brew install gh
|
||||||
|
gh auth login
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Git** - Configured with name and email
|
||||||
|
```bash
|
||||||
|
git config --global user.name "Your Name"
|
||||||
|
git config --global user.email "your.email@example.com"
|
||||||
|
```
|
||||||
|
|
||||||
|
- **SSH Keys** - Setup for GitHub (for cloning via SSH)
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**Fork already exists**
|
||||||
|
→ Script handles automatically, uses existing fork
|
||||||
|
|
||||||
|
**Repository already cloned**
|
||||||
|
→ Script verifies and updates remotes
|
||||||
|
|
||||||
|
**PR already exists**
|
||||||
|
→ Script shows existing PR URL instead of creating duplicate
|
||||||
|
|
||||||
|
**Fork has different name**
|
||||||
|
→ Script detects via GitHub API and uses correct name
|
||||||
|
|
||||||
|
**Current directory not a git repo**
|
||||||
|
→ Don't use `.` as work_dir, or cd to a git repo first
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- All commits are signed with DCO (`-s` flag)
|
||||||
|
- Uses `git add -u` (only modified/deleted files, not new untracked files)
|
||||||
|
- Creates PRs with proper `username:branch` format
|
||||||
|
- Handles both HTTPS (upstream) and SSH (origin) remotes
|
||||||
|
- Color-coded output for easy reading
|
||||||
191
skills/pr/scripts/00-pr-workflow.sh
Executable file
191
skills/pr/scripts/00-pr-workflow.sh
Executable file
@@ -0,0 +1,191 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# 00-pr-workflow.sh - Complete PR workflow (fork → modify → PR)
|
||||||
|
# Usage: ./00-pr-workflow.sh <upstream_repo> <base_branch> <feature_branch> <pr_title> [work_dir] [depth]
|
||||||
|
# Example: ./00-pr-workflow.sh stolostron/multicluster-global-hub release-1.6 fix/bug-123 "Fix bug in handler" ~/tmp/contribute 1
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Function to print colored messages
|
||||||
|
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||||
|
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
||||||
|
step() { echo -e "${BLUE}[STEP]${NC} $*"; }
|
||||||
|
|
||||||
|
# Check arguments
|
||||||
|
if [ $# -lt 4 ]; then
|
||||||
|
error "Usage: $0 <upstream_repo> <base_branch> <feature_branch> <pr_title> [work_dir] [depth]"
|
||||||
|
error "Example: $0 stolostron/multicluster-global-hub release-1.6 fix/bug-123 \"Fix bug\" ~/tmp/contribute 1"
|
||||||
|
error ""
|
||||||
|
error "Parameters:"
|
||||||
|
error " upstream_repo - Repository to fork (owner/repo)"
|
||||||
|
error " base_branch - Base branch for PR (e.g., main, release-1.6)"
|
||||||
|
error " feature_branch - Your feature branch name"
|
||||||
|
error " pr_title - Pull request title"
|
||||||
|
error " work_dir - Working directory (default: ~/tmp/contribute)"
|
||||||
|
error " depth - Git clone depth (default: 1 for fast shallow clone)"
|
||||||
|
error " Use 'full' for complete history if needed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
UPSTREAM_REPO="$1"
|
||||||
|
BASE_BRANCH="$2"
|
||||||
|
FEATURE_BRANCH="$3"
|
||||||
|
PR_TITLE="$4"
|
||||||
|
WORK_DIR="${5:-$HOME/tmp/contribute}"
|
||||||
|
CLONE_DEPTH="${6:-1}" # Default to shallow clone (depth=1) for speed
|
||||||
|
REPO_NAME=$(basename "$UPSTREAM_REPO")
|
||||||
|
|
||||||
|
info "========================================="
|
||||||
|
info "Contribution Workflow Started"
|
||||||
|
info "========================================="
|
||||||
|
info "Upstream repository: $UPSTREAM_REPO"
|
||||||
|
info "Base branch: $BASE_BRANCH"
|
||||||
|
info "Feature branch: $FEATURE_BRANCH"
|
||||||
|
info "PR title: $PR_TITLE"
|
||||||
|
info "Work directory: $WORK_DIR"
|
||||||
|
if [ "$CLONE_DEPTH" = "1" ]; then
|
||||||
|
info "Clone depth: 1 (shallow clone - faster)"
|
||||||
|
elif [ -n "$CLONE_DEPTH" ]; then
|
||||||
|
info "Clone depth: $CLONE_DEPTH"
|
||||||
|
else
|
||||||
|
info "Clone depth: full history"
|
||||||
|
fi
|
||||||
|
info "========================================="
|
||||||
|
|
||||||
|
# Check prerequisites
|
||||||
|
step "1/7: Checking prerequisites..."
|
||||||
|
if ! command -v gh &> /dev/null; then
|
||||||
|
error "GitHub CLI (gh) is not installed. Please install it first:"
|
||||||
|
error " brew install gh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! gh auth status &> /dev/null; then
|
||||||
|
error "Not authenticated with GitHub CLI. Please run:"
|
||||||
|
error " gh auth login"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
GH_USER=$(gh api user --jq '.login')
|
||||||
|
info "GitHub username: $GH_USER"
|
||||||
|
|
||||||
|
# Step 1: Fork and setup
|
||||||
|
step "2/7: Forking and setting up repository..."
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
if ! bash "$SCRIPT_DIR/01-fork-and-setup.sh" "$UPSTREAM_REPO" "$WORK_DIR" "$CLONE_DEPTH"; then
|
||||||
|
error "Failed to fork and setup repository"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
REPO_PATH="$WORK_DIR/$REPO_NAME"
|
||||||
|
cd "$REPO_PATH"
|
||||||
|
|
||||||
|
# Step 2: Check for contribution guidelines
|
||||||
|
step "3/7: Checking for contribution guidelines..."
|
||||||
|
if [ -f "CONTRIBUTING.md" ]; then
|
||||||
|
info "Found CONTRIBUTING.md - Please review contribution guidelines:"
|
||||||
|
head -20 CONTRIBUTING.md
|
||||||
|
echo ""
|
||||||
|
warn "Press Enter to continue after reviewing guidelines..."
|
||||||
|
# For automation, we'll skip the wait
|
||||||
|
# read -r
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 3: Create feature branch
|
||||||
|
step "4/7: Creating feature branch from latest upstream..."
|
||||||
|
git fetch upstream
|
||||||
|
if git rev-parse --verify "$FEATURE_BRANCH" &> /dev/null; then
|
||||||
|
warn "Branch '$FEATURE_BRANCH' already exists. Switching to it..."
|
||||||
|
git checkout "$FEATURE_BRANCH"
|
||||||
|
# Optionally rebase on latest upstream
|
||||||
|
git rebase "upstream/$BASE_BRANCH" || {
|
||||||
|
error "Failed to rebase. Please resolve conflicts manually."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
git checkout -b "$FEATURE_BRANCH" "upstream/$BASE_BRANCH"
|
||||||
|
info "Created branch '$FEATURE_BRANCH' from 'upstream/$BASE_BRANCH'"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 4: Pause for manual changes
|
||||||
|
step "5/7: Ready for code changes..."
|
||||||
|
info ""
|
||||||
|
info "Repository path: $REPO_PATH"
|
||||||
|
info "Current branch: $(git branch --show-current)"
|
||||||
|
info ""
|
||||||
|
info "Next steps:"
|
||||||
|
info " 1. Make your code changes"
|
||||||
|
info " 2. Commit your changes with: git commit -s -m \"message\""
|
||||||
|
info " 3. Return here and we'll create the PR"
|
||||||
|
info ""
|
||||||
|
warn "This script will now pause. Press Enter when you're ready to create the PR..."
|
||||||
|
# For automation in Claude, we'll skip this pause
|
||||||
|
# read -r
|
||||||
|
|
||||||
|
# Step 5: Verify changes are committed
|
||||||
|
step "6/7: Verifying changes are committed..."
|
||||||
|
if git diff-index --quiet HEAD --; then
|
||||||
|
# Check if there are any commits on this branch
|
||||||
|
COMMITS_AHEAD=$(git rev-list --count "upstream/$BASE_BRANCH..$FEATURE_BRANCH")
|
||||||
|
if [ "$COMMITS_AHEAD" -eq 0 ]; then
|
||||||
|
error "No changes committed. Please make changes and commit them first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
info "Found $COMMITS_AHEAD commit(s) ready to push"
|
||||||
|
else
|
||||||
|
error "You have uncommitted changes. Please commit or stash them first."
|
||||||
|
info "Run: git status"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 6: Run checks (if applicable)
|
||||||
|
step "7/7: Running pre-PR checks..."
|
||||||
|
# Check for common files that indicate tests should be run
|
||||||
|
if [ -f "Makefile" ]; then
|
||||||
|
if grep -q "^test:" Makefile; then
|
||||||
|
info "Found 'test' target in Makefile. Consider running: make test"
|
||||||
|
fi
|
||||||
|
if grep -q "^lint:" Makefile; then
|
||||||
|
info "Found 'lint' target in Makefile. Consider running: make lint"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 7: Create PR
|
||||||
|
info ""
|
||||||
|
info "========================================="
|
||||||
|
info "Creating Pull Request"
|
||||||
|
info "========================================="
|
||||||
|
|
||||||
|
# Get commit messages for PR body
|
||||||
|
COMMIT_MESSAGES=$(git log --pretty=format:"- %s" "upstream/$BASE_BRANCH..$FEATURE_BRANCH")
|
||||||
|
PR_BODY="## Changes
|
||||||
|
|
||||||
|
$COMMIT_MESSAGES
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
- [x] Code changes are committed with sign-off
|
||||||
|
- [ ] Tests pass locally (if applicable)
|
||||||
|
- [ ] Documentation updated (if needed)
|
||||||
|
|
||||||
|
---
|
||||||
|
*Generated via contribute script*"
|
||||||
|
|
||||||
|
# Use the fixed create-pr script
|
||||||
|
if bash "$SCRIPT_DIR/03-create-pr.sh" "$BASE_BRANCH" "$PR_TITLE" "$PR_BODY"; then
|
||||||
|
info ""
|
||||||
|
info "========================================="
|
||||||
|
info "✅ Contribution workflow completed!"
|
||||||
|
info "========================================="
|
||||||
|
else
|
||||||
|
error "Failed to create PR. You can create it manually with:"
|
||||||
|
error " gh pr create --repo $UPSTREAM_REPO --base $BASE_BRANCH --head $GH_USER:$FEATURE_BRANCH"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
255
skills/pr/scripts/01-fork-and-setup.sh
Executable file
255
skills/pr/scripts/01-fork-and-setup.sh
Executable file
@@ -0,0 +1,255 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# 01-fork-and-setup.sh - Fork repository and setup local environment
|
||||||
|
# Usage: ./01-fork-and-setup.sh <upstream_repo> [work_dir] [depth] [base_branch] [feature_branch]
|
||||||
|
# Example: ./01-fork-and-setup.sh stolostron/multicluster-global-hub ~/tmp/contribute 1 main fix/version
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Function to print colored messages
|
||||||
|
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||||
|
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
||||||
|
|
||||||
|
# Check arguments
|
||||||
|
if [ $# -lt 1 ]; then
|
||||||
|
error "Usage: $0 <upstream_repo> [work_dir] [depth] [base_branch] [feature_branch]"
|
||||||
|
error "Example: $0 stolostron/multicluster-global-hub ~/tmp/contribute 1 main fix/version"
|
||||||
|
error "Example (current dir): cd /path/to/repo && $0 stolostron/multicluster-global-hub . 1 main fix/version"
|
||||||
|
error ""
|
||||||
|
error "Parameters:"
|
||||||
|
error " upstream_repo - Repository to fork (owner/repo)"
|
||||||
|
error " work_dir - Working directory (default: ~/tmp/contribute)"
|
||||||
|
error " Use '.' to work in current directory (must be a git repo)"
|
||||||
|
error " depth - Git clone depth (default: full history)"
|
||||||
|
error " Use 1 for shallow clone (faster, less history)"
|
||||||
|
error " base_branch - Base branch to branch from (optional)"
|
||||||
|
error " feature_branch - Feature branch to create (optional, requires base_branch)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
UPSTREAM_REPO="$1"
|
||||||
|
WORK_DIR="${2:-$HOME/tmp/contribute}"
|
||||||
|
CLONE_DEPTH="${3:-}"
|
||||||
|
BASE_BRANCH="${4:-}"
|
||||||
|
FEATURE_BRANCH="${5:-}"
|
||||||
|
REPO_NAME=$(basename "$UPSTREAM_REPO")
|
||||||
|
|
||||||
|
# Support current directory mode
|
||||||
|
USE_CURRENT_DIR=false
|
||||||
|
if [ "$WORK_DIR" = "." ]; then
|
||||||
|
USE_CURRENT_DIR=true
|
||||||
|
WORK_DIR="$(pwd)"
|
||||||
|
info "Using current directory mode"
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Upstream repository: $UPSTREAM_REPO"
|
||||||
|
info "Work directory: $WORK_DIR"
|
||||||
|
info "Repository name: $REPO_NAME"
|
||||||
|
if [ -n "$CLONE_DEPTH" ]; then
|
||||||
|
info "Clone depth: $CLONE_DEPTH (shallow clone for faster download)"
|
||||||
|
else
|
||||||
|
info "Clone depth: full history"
|
||||||
|
fi
|
||||||
|
if [ -n "$BASE_BRANCH" ] && [ -n "$FEATURE_BRANCH" ]; then
|
||||||
|
info "Will create feature branch: $FEATURE_BRANCH from $BASE_BRANCH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if gh CLI is available
|
||||||
|
if ! command -v gh &> /dev/null; then
|
||||||
|
error "GitHub CLI (gh) is not installed. Please install it first:"
|
||||||
|
error " brew install gh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if user is authenticated with gh
|
||||||
|
if ! gh auth status &> /dev/null; then
|
||||||
|
error "Not authenticated with GitHub CLI. Please run:"
|
||||||
|
error " gh auth login"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get current GitHub username
|
||||||
|
GH_USER=$(gh api user --jq '.login')
|
||||||
|
info "GitHub username: $GH_USER"
|
||||||
|
|
||||||
|
# Check if fork already exists (with possible different name)
|
||||||
|
info "Checking if fork already exists..."
|
||||||
|
FORK_EXISTS=false
|
||||||
|
FORK_REPO_NAME=""
|
||||||
|
|
||||||
|
# First, try the expected name
|
||||||
|
if gh repo view "$GH_USER/$REPO_NAME" &> /dev/null; then
|
||||||
|
warn "Fork already exists: $GH_USER/$REPO_NAME"
|
||||||
|
FORK_EXISTS=true
|
||||||
|
FORK_REPO_NAME="$REPO_NAME"
|
||||||
|
else
|
||||||
|
# Check if there's a fork with a different name by querying the upstream repo
|
||||||
|
info "Checking for fork with different name..."
|
||||||
|
POTENTIAL_FORK=$(gh api "repos/$UPSTREAM_REPO/forks" --jq ".[] | select(.owner.login == \"$GH_USER\") | .name" 2>/dev/null | head -1)
|
||||||
|
|
||||||
|
if [ -n "$POTENTIAL_FORK" ]; then
|
||||||
|
warn "Found existing fork with different name: $GH_USER/$POTENTIAL_FORK"
|
||||||
|
warn "Original repo: $REPO_NAME, Fork name: $POTENTIAL_FORK"
|
||||||
|
FORK_EXISTS=true
|
||||||
|
FORK_REPO_NAME="$POTENTIAL_FORK"
|
||||||
|
else
|
||||||
|
info "Fork does not exist. Creating fork..."
|
||||||
|
FORK_OUTPUT=$(gh repo fork "$UPSTREAM_REPO" --clone=false 2>&1)
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
# Extract the actual fork name from output or check again
|
||||||
|
FORK_REPO_NAME=$(gh api "repos/$UPSTREAM_REPO/forks" --jq ".[] | select(.owner.login == \"$GH_USER\") | .name" 2>/dev/null | head -1)
|
||||||
|
if [ -z "$FORK_REPO_NAME" ]; then
|
||||||
|
FORK_REPO_NAME="$REPO_NAME" # Fallback to expected name
|
||||||
|
fi
|
||||||
|
info "Fork created successfully: $GH_USER/$FORK_REPO_NAME"
|
||||||
|
FORK_EXISTS=true
|
||||||
|
else
|
||||||
|
error "Failed to create fork"
|
||||||
|
error "$FORK_OUTPUT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use the actual fork name for subsequent operations
|
||||||
|
if [ -n "$FORK_REPO_NAME" ]; then
|
||||||
|
info "Using fork: $GH_USER/$FORK_REPO_NAME"
|
||||||
|
else
|
||||||
|
error "Could not determine fork repository name"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Determine repository path
|
||||||
|
if [ "$USE_CURRENT_DIR" = true ]; then
|
||||||
|
# In current directory mode, check if we're already in a git repo
|
||||||
|
if git rev-parse --git-dir &> /dev/null; then
|
||||||
|
REPO_PATH="$WORK_DIR"
|
||||||
|
info "Using current directory as repository: $REPO_PATH"
|
||||||
|
else
|
||||||
|
error "Current directory mode requires you to be in a git repository"
|
||||||
|
error "Either cd to the repository or specify a different work_dir"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Create work directory if it doesn't exist
|
||||||
|
mkdir -p "$WORK_DIR"
|
||||||
|
REPO_PATH="$WORK_DIR/$REPO_NAME"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if repository already cloned (using the original repo name for local directory)
|
||||||
|
if [ -d "$REPO_PATH/.git" ]; then
|
||||||
|
warn "Repository already exists at: $REPO_PATH"
|
||||||
|
info "Checking remotes..."
|
||||||
|
|
||||||
|
cd "$REPO_PATH"
|
||||||
|
|
||||||
|
# Check current remotes
|
||||||
|
if git remote get-url upstream &> /dev/null; then
|
||||||
|
CURRENT_UPSTREAM=$(git remote get-url upstream)
|
||||||
|
info "Current upstream: $CURRENT_UPSTREAM"
|
||||||
|
else
|
||||||
|
warn "No upstream remote found. Adding it..."
|
||||||
|
git remote add upstream "https://github.com/$UPSTREAM_REPO.git"
|
||||||
|
info "Added upstream: https://github.com/$UPSTREAM_REPO.git"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if git remote get-url origin &> /dev/null; then
|
||||||
|
CURRENT_ORIGIN=$(git remote get-url origin)
|
||||||
|
info "Current origin: $CURRENT_ORIGIN"
|
||||||
|
else
|
||||||
|
warn "No origin remote found. Adding it..."
|
||||||
|
git remote add origin "git@github.com:$GH_USER/$FORK_REPO_NAME.git"
|
||||||
|
info "Added origin: git@github.com:$GH_USER/$FORK_REPO_NAME.git"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ "$USE_CURRENT_DIR" = true ]; then
|
||||||
|
error "Current directory is not a git repository and current directory mode is enabled"
|
||||||
|
error "Either cd to the repository or use a different work_dir"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Cloning fork to: $REPO_PATH"
|
||||||
|
cd "$WORK_DIR"
|
||||||
|
|
||||||
|
# Clone the fork with optional depth
|
||||||
|
CLONE_CMD="git clone"
|
||||||
|
if [ -n "$CLONE_DEPTH" ]; then
|
||||||
|
CLONE_CMD="$CLONE_CMD --depth $CLONE_DEPTH"
|
||||||
|
info "Using shallow clone (depth=$CLONE_DEPTH) for faster download..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clone using the actual fork name, but rename directory to original repo name
|
||||||
|
if [ "$FORK_REPO_NAME" = "$REPO_NAME" ]; then
|
||||||
|
# Fork name matches, no need to rename
|
||||||
|
if $CLONE_CMD "git@github.com:$GH_USER/$FORK_REPO_NAME.git"; then
|
||||||
|
info "Fork cloned successfully"
|
||||||
|
cd "$REPO_NAME"
|
||||||
|
|
||||||
|
# Add upstream remote
|
||||||
|
info "Adding upstream remote..."
|
||||||
|
git remote add upstream "https://github.com/$UPSTREAM_REPO.git"
|
||||||
|
info "Added upstream: https://github.com/$UPSTREAM_REPO.git"
|
||||||
|
else
|
||||||
|
error "Failed to clone fork"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Fork name differs, clone and rename
|
||||||
|
if $CLONE_CMD "git@github.com:$GH_USER/$FORK_REPO_NAME.git" "$REPO_NAME"; then
|
||||||
|
info "Fork cloned successfully (renamed from $FORK_REPO_NAME to $REPO_NAME)"
|
||||||
|
cd "$REPO_NAME"
|
||||||
|
|
||||||
|
# Add upstream remote
|
||||||
|
info "Adding upstream remote..."
|
||||||
|
git remote add upstream "https://github.com/$UPSTREAM_REPO.git"
|
||||||
|
info "Added upstream: https://github.com/$UPSTREAM_REPO.git"
|
||||||
|
else
|
||||||
|
error "Failed to clone fork"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fetch latest from upstream
|
||||||
|
info "Fetching latest from upstream..."
|
||||||
|
if [ -n "$CLONE_DEPTH" ]; then
|
||||||
|
# For shallow clones, also use depth when fetching
|
||||||
|
git fetch --depth "$CLONE_DEPTH" upstream
|
||||||
|
else
|
||||||
|
git fetch upstream
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create feature branch if specified
|
||||||
|
if [ -n "$BASE_BRANCH" ] && [ -n "$FEATURE_BRANCH" ]; then
|
||||||
|
info "Creating feature branch..."
|
||||||
|
|
||||||
|
# Check if feature branch already exists
|
||||||
|
if git rev-parse --verify "$FEATURE_BRANCH" &> /dev/null; then
|
||||||
|
warn "Branch '$FEATURE_BRANCH' already exists. Switching to it..."
|
||||||
|
git checkout "$FEATURE_BRANCH"
|
||||||
|
else
|
||||||
|
info "Creating branch '$FEATURE_BRANCH' from 'upstream/$BASE_BRANCH'..."
|
||||||
|
git checkout -b "$FEATURE_BRANCH" "upstream/$BASE_BRANCH"
|
||||||
|
info "Created and switched to branch '$FEATURE_BRANCH'"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "✅ Setup complete!"
|
||||||
|
info ""
|
||||||
|
info "Repository path: $REPO_PATH"
|
||||||
|
info "Remotes configured:"
|
||||||
|
git remote -v
|
||||||
|
if [ -n "$FEATURE_BRANCH" ]; then
|
||||||
|
info "Current branch: $(git branch --show-current)"
|
||||||
|
fi
|
||||||
|
info ""
|
||||||
|
info "Next steps:"
|
||||||
|
info " 1. cd $REPO_PATH"
|
||||||
|
info " 2. Make your changes"
|
||||||
|
info " 3. Run create-pr script to submit PR"
|
||||||
153
skills/pr/scripts/02-check-guidelines.sh
Executable file
153
skills/pr/scripts/02-check-guidelines.sh
Executable file
@@ -0,0 +1,153 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# 02-check-guidelines.sh - Check repository contribution guidelines and requirements
|
||||||
|
# Usage: ./02-check-guidelines.sh [repo_path]
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Function to print colored messages
|
||||||
|
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||||
|
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
||||||
|
check() { echo -e "${BLUE}[CHECK]${NC} $*"; }
|
||||||
|
|
||||||
|
REPO_PATH="${1:-.}"
|
||||||
|
cd "$REPO_PATH"
|
||||||
|
|
||||||
|
info "Checking contribution guidelines for repository: $(basename "$PWD")"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check for CONTRIBUTING.md
|
||||||
|
check "Looking for CONTRIBUTING.md..."
|
||||||
|
if [ -f "CONTRIBUTING.md" ]; then
|
||||||
|
info "✅ Found CONTRIBUTING.md"
|
||||||
|
echo ""
|
||||||
|
echo "===== Contribution Guidelines (first 30 lines) ====="
|
||||||
|
head -30 CONTRIBUTING.md
|
||||||
|
echo "===== (see CONTRIBUTING.md for full details) ====="
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
warn "No CONTRIBUTING.md found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for CODE_OF_CONDUCT.md
|
||||||
|
check "Looking for CODE_OF_CONDUCT.md..."
|
||||||
|
if [ -f "CODE_OF_CONDUCT.md" ]; then
|
||||||
|
info "✅ Found CODE_OF_CONDUCT.md - Please review community guidelines"
|
||||||
|
else
|
||||||
|
warn "No CODE_OF_CONDUCT.md found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for CLA
|
||||||
|
check "Looking for CLA requirements..."
|
||||||
|
CLA_FOUND=false
|
||||||
|
for file in CONTRIBUTING.md README.md .github/CONTRIBUTING.md docs/CONTRIBUTING.md; do
|
||||||
|
if [ -f "$file" ] && grep -qi "CLA\|contributor license agreement" "$file"; then
|
||||||
|
warn "⚠️ CLA (Contributor License Agreement) may be required"
|
||||||
|
warn " Check: $file"
|
||||||
|
CLA_FOUND=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ "$CLA_FOUND" = false ]; then
|
||||||
|
info "No CLA requirement detected"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for DCO (Developer Certificate of Origin)
|
||||||
|
check "Looking for DCO requirements..."
|
||||||
|
DCO_FOUND=false
|
||||||
|
for file in CONTRIBUTING.md README.md .github/CONTRIBUTING.md docs/CONTRIBUTING.md; do
|
||||||
|
if [ -f "$file" ] && grep -qi "DCO\|sign-off\|signed-off-by" "$file"; then
|
||||||
|
info "✅ DCO (Developer Certificate of Origin) required"
|
||||||
|
info " Use: git commit -s"
|
||||||
|
DCO_FOUND=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ "$DCO_FOUND" = false ]; then
|
||||||
|
warn "No explicit DCO requirement found, but it's a best practice to use -s flag"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for PR template
|
||||||
|
check "Looking for PR template..."
|
||||||
|
if [ -f ".github/PULL_REQUEST_TEMPLATE.md" ] || [ -f "PULL_REQUEST_TEMPLATE.md" ] || [ -f ".github/pull_request_template.md" ]; then
|
||||||
|
info "✅ Found PR template - Your PR description should follow this format"
|
||||||
|
if [ -f ".github/PULL_REQUEST_TEMPLATE.md" ]; then
|
||||||
|
echo ""
|
||||||
|
echo "===== PR Template ====="
|
||||||
|
cat .github/PULL_REQUEST_TEMPLATE.md
|
||||||
|
echo "===== (end of template) ====="
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "No PR template found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for testing requirements
|
||||||
|
check "Looking for testing requirements..."
|
||||||
|
if [ -f "Makefile" ]; then
|
||||||
|
info "✅ Found Makefile"
|
||||||
|
if grep -q "^test:" Makefile; then
|
||||||
|
info " → Run tests with: make test"
|
||||||
|
fi
|
||||||
|
if grep -q "^lint:" Makefile; then
|
||||||
|
info " → Run linter with: make lint"
|
||||||
|
fi
|
||||||
|
if grep -q "^fmt:" Makefile; then
|
||||||
|
info " → Format code with: make fmt"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for CI configuration
|
||||||
|
check "Looking for CI/CD configuration..."
|
||||||
|
CI_FOUND=false
|
||||||
|
if [ -d ".github/workflows" ]; then
|
||||||
|
info "✅ Found GitHub Actions workflows:"
|
||||||
|
for workflow in .github/workflows/*.{yml,yaml}; do
|
||||||
|
if [ -f "$workflow" ]; then
|
||||||
|
info " → $(basename "$workflow")"
|
||||||
|
CI_FOUND=true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
if [ -f ".travis.yml" ]; then
|
||||||
|
info "✅ Found Travis CI configuration"
|
||||||
|
CI_FOUND=true
|
||||||
|
fi
|
||||||
|
if [ -f ".circleci/config.yml" ]; then
|
||||||
|
info "✅ Found CircleCI configuration"
|
||||||
|
CI_FOUND=true
|
||||||
|
fi
|
||||||
|
if [ "$CI_FOUND" = false ]; then
|
||||||
|
warn "No CI/CD configuration found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check commit message conventions
|
||||||
|
check "Looking for commit message conventions..."
|
||||||
|
for file in CONTRIBUTING.md README.md .github/CONTRIBUTING.md docs/CONTRIBUTING.md; do
|
||||||
|
if [ -f "$file" ]; then
|
||||||
|
if grep -qi "conventional commits\|commit message\|commit format" "$file"; then
|
||||||
|
info "✅ Commit message conventions found - Please review: $file"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
echo ""
|
||||||
|
info "========================================="
|
||||||
|
info "Summary"
|
||||||
|
info "========================================="
|
||||||
|
info "Before submitting your PR, ensure you:"
|
||||||
|
info " 1. ✓ Read CONTRIBUTING.md (if exists)"
|
||||||
|
info " 2. ✓ Sign commits with -s flag (DCO)"
|
||||||
|
info " 3. ✓ Run tests locally (make test)"
|
||||||
|
info " 4. ✓ Follow commit message conventions"
|
||||||
|
info " 5. ✓ Fill out PR template (if exists)"
|
||||||
|
info "========================================="
|
||||||
124
skills/pr/scripts/03-create-pr.sh
Executable file
124
skills/pr/scripts/03-create-pr.sh
Executable file
@@ -0,0 +1,124 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# 03-create-pr.sh - Add, commit, push changes and create PR to upstream
|
||||||
|
# Usage: ./03-create-pr.sh <base_branch> <pr_title> [pr_body] [commit_message]
|
||||||
|
# Example: ./03-create-pr.sh main "Fix bug in sync handler" "This PR fixes..." "fix: resolve bug"
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Function to print colored messages
|
||||||
|
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||||
|
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
||||||
|
|
||||||
|
# Check arguments
|
||||||
|
if [ $# -lt 2 ]; then
|
||||||
|
error "Usage: $0 <base_branch> <pr_title> [pr_body] [commit_message]"
|
||||||
|
error "Example: $0 main \"Fix bug in sync handler\" \"This PR fixes...\" \"fix: resolve bug\""
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BASE_BRANCH="$1"
|
||||||
|
PR_TITLE="$2"
|
||||||
|
PR_BODY="${3:-}"
|
||||||
|
COMMIT_MESSAGE="${4:-$PR_TITLE}"
|
||||||
|
|
||||||
|
# Check if gh CLI is available
|
||||||
|
if ! command -v gh &> /dev/null; then
|
||||||
|
error "GitHub CLI (gh) is not installed. Please install it first:"
|
||||||
|
error " brew install gh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if in a git repository
|
||||||
|
if ! git rev-parse --git-dir &> /dev/null; then
|
||||||
|
error "Not in a git repository"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get current branch
|
||||||
|
CURRENT_BRANCH=$(git branch --show-current)
|
||||||
|
info "Current branch: $CURRENT_BRANCH"
|
||||||
|
|
||||||
|
# Check if current branch is the base branch
|
||||||
|
if [ "$CURRENT_BRANCH" = "$BASE_BRANCH" ]; then
|
||||||
|
error "You are on the base branch ($BASE_BRANCH). Please create a feature branch first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for uncommitted changes and auto-commit if any
|
||||||
|
if ! git diff-index --quiet HEAD --; then
|
||||||
|
info "Found uncommitted changes. Auto-committing..."
|
||||||
|
|
||||||
|
# Add all modified/deleted files (not new untracked files)
|
||||||
|
git add -u
|
||||||
|
|
||||||
|
# Commit with sign-off
|
||||||
|
git commit -s -m "$COMMIT_MESSAGE"
|
||||||
|
|
||||||
|
info "Changes committed: $COMMIT_MESSAGE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get upstream repo info
|
||||||
|
if ! UPSTREAM_URL=$(git remote get-url upstream 2>/dev/null); then
|
||||||
|
error "No upstream remote found. Please run 01-fork-and-setup.sh first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract owner/repo from upstream URL
|
||||||
|
UPSTREAM_REPO=$(echo "$UPSTREAM_URL" | sed -E 's|.*github\.com[:/](.*)\.git|\1|' | sed 's|\.git$||')
|
||||||
|
info "Upstream repository: $UPSTREAM_REPO"
|
||||||
|
|
||||||
|
# Get origin repo info and extract username
|
||||||
|
ORIGIN_URL=$(git remote get-url origin)
|
||||||
|
ORIGIN_REPO=$(echo "$ORIGIN_URL" | sed -E 's|.*github\.com[:/](.*)\.git|\1|' | sed 's|\.git$||')
|
||||||
|
ORIGIN_USER=$(echo "$ORIGIN_REPO" | cut -d'/' -f1)
|
||||||
|
info "Origin repository: $ORIGIN_REPO"
|
||||||
|
info "Origin user: $ORIGIN_USER"
|
||||||
|
|
||||||
|
# Push current branch to origin
|
||||||
|
info "Pushing current branch to origin..."
|
||||||
|
if git push -u origin "$CURRENT_BRANCH" 2>&1 | grep -q "Everything up-to-date"; then
|
||||||
|
info "Branch already up-to-date on origin"
|
||||||
|
else
|
||||||
|
info "Branch pushed to origin"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if PR already exists
|
||||||
|
info "Checking if PR already exists..."
|
||||||
|
EXISTING_PR=$(gh pr list --repo "$UPSTREAM_REPO" --head "$ORIGIN_USER:$CURRENT_BRANCH" --json number,title,url --jq '.[0]')
|
||||||
|
|
||||||
|
if [ -n "$EXISTING_PR" ] && [ "$EXISTING_PR" != "null" ]; then
|
||||||
|
PR_NUMBER=$(echo "$EXISTING_PR" | jq -r '.number')
|
||||||
|
PR_URL=$(echo "$EXISTING_PR" | jq -r '.url')
|
||||||
|
EXISTING_TITLE=$(echo "$EXISTING_PR" | jq -r '.title')
|
||||||
|
|
||||||
|
warn "PR already exists!"
|
||||||
|
info " PR #$PR_NUMBER: $EXISTING_TITLE"
|
||||||
|
info " URL: $PR_URL"
|
||||||
|
info ""
|
||||||
|
info "Would you like to update the existing PR or create a new one?"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create PR
|
||||||
|
info "Creating PR to upstream..."
|
||||||
|
if [ -n "$PR_BODY" ]; then
|
||||||
|
PR_URL=$(gh pr create --repo "$UPSTREAM_REPO" --base "$BASE_BRANCH" --head "$ORIGIN_USER:$CURRENT_BRANCH" --title "$PR_TITLE" --body "$PR_BODY")
|
||||||
|
else
|
||||||
|
# Use interactive editor if no body provided
|
||||||
|
PR_URL=$(gh pr create --repo "$UPSTREAM_REPO" --base "$BASE_BRANCH" --head "$ORIGIN_USER:$CURRENT_BRANCH" --title "$PR_TITLE")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
info "✅ PR created successfully!"
|
||||||
|
info " URL: $PR_URL"
|
||||||
|
else
|
||||||
|
error "Failed to create PR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user