Skip to content
Open
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ Simply move your mouse around the screen and watch the rabbits chase after it! E
- Multiple rabbits with varying speeds
- Responsive design
- Green background theme
- Pause/play button to control animation
232 changes: 232 additions & 0 deletions create-pr.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
#!/bin/bash

# Script to create a branch, commit changes, push to all remotes, and create PRs
# Usage: ./create-pr.sh <branch-name> <commit-message> <pr-title> [pr-description]
#
# Prerequisites:
# - Bitbucket: API key at ~/.bitbucket-api-key (required for BB PRs)
# - GitHub: gh CLI (preferred) or token at ~/.github-token
# - GitLab: glab CLI (required for GL PRs)
# - Azure DevOps: az CLI (preferred) or PAT token at ~/.ado-token

set -e

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

# Check if required arguments are provided
if [ $# -lt 3 ]; then
echo -e "${RED}Error: Missing required arguments${NC}"
echo "Usage: $0 <branch-name> <commit-message> <pr-title> [pr-description]"
echo ""
echo "Example:"
echo " $0 feature/new-ui 'Add new UI components' 'Add New UI Components' 'This PR adds new UI components for better UX'"
exit 1
fi

BRANCH_NAME="$1"
COMMIT_MESSAGE="$2"
PR_TITLE="$3"
PR_DESCRIPTION="${4:-$COMMIT_MESSAGE}"

# Add Claude Code signature to commit message and PR description
COMMIT_MESSAGE_FULL="$COMMIT_MESSAGE

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>"

PR_DESCRIPTION_FULL="$PR_DESCRIPTION

🤖 Generated with [Claude Code](https://claude.com/claude-code)"

echo -e "${GREEN}Starting automated PR creation workflow...${NC}"
echo ""

# Step 1: Create and checkout new branch
echo -e "${YELLOW}Step 1: Creating branch '$BRANCH_NAME'...${NC}"
git checkout -b "$BRANCH_NAME"
echo -e "${GREEN}✓ Branch created and checked out${NC}"
echo ""

# Step 2: Stage all changes
echo -e "${YELLOW}Step 2: Staging changes...${NC}"
git add .
echo -e "${GREEN}✓ Changes staged${NC}"
echo ""

# Step 3: Commit changes
echo -e "${YELLOW}Step 3: Committing changes...${NC}"
git commit -m "$COMMIT_MESSAGE_FULL"
echo -e "${GREEN}✓ Changes committed${NC}"
echo ""

# Step 4: Push to all remotes
echo -e "${YELLOW}Step 4: Pushing to all remotes...${NC}"

# Get base branch (default to main)
BASE_BRANCH="${BASE_BRANCH:-main}"

# Push to each remote
for remote in ado bb gh gl; do
echo -e " Pushing to ${YELLOW}$remote${NC}..."
git push -u "$remote" "$BRANCH_NAME"
echo -e " ${GREEN}✓ Pushed to $remote${NC}"
done
echo ""

# Step 5: Create PRs
echo -e "${YELLOW}Step 5: Creating pull requests...${NC}"
echo ""

# Load Bitbucket API key
if [ -f ~/.bitbucket-api-key ]; then
BB_API_KEY=$(cat ~/.bitbucket-api-key)
else
echo -e "${RED}Warning: Bitbucket API key not found at ~/.bitbucket-api-key${NC}"
BB_API_KEY=""
fi

# Load GitHub token
if [ -f ~/.github-token ]; then
GH_TOKEN=$(cat ~/.github-token)
else
echo -e "${YELLOW}Info: GitHub token not found at ~/.github-token. Attempting to use gh CLI...${NC}"
GH_TOKEN=""
fi

# Create PR on Bitbucket
if [ -n "$BB_API_KEY" ]; then
echo -e " Creating PR on ${YELLOW}Bitbucket${NC}..."
# Escape special characters in description for JSON
PR_DESC_ESCAPED=$(echo "$PR_DESCRIPTION_FULL" | sed 's/"/\\"/g' | awk '{printf "%s\\n", $0}' | sed '$ s/\\n$//')
PR_TITLE_ESCAPED=$(echo "$PR_TITLE" | sed 's/"/\\"/g')

BB_RESPONSE=$(curl -s -X POST \
-u "john@turbulent.cloud:$BB_API_KEY" \
-H "Content-Type: application/json" \
https://api.bitbucket.org/2.0/repositories/turbulentcloud/rabbit-followers/pullrequests \
-d "{\"title\":\"$PR_TITLE_ESCAPED\",\"source\":{\"branch\":{\"name\":\"$BRANCH_NAME\"}},\"destination\":{\"branch\":{\"name\":\"$BASE_BRANCH\"}},\"description\":\"$PR_DESC_ESCAPED\"}")
Comment on lines +109 to +112
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Hardcoded credentials and repository paths expose sensitive information.

The script contains hardcoded email addresses, repository paths, and organization names that expose user identity and make the script non-reusable.

Replace hardcoded values with environment variables or script parameters:

-    BB_RESPONSE=$(curl -s -X POST \
-      -u "john@turbulent.cloud:$BB_API_KEY" \
-      -H "Content-Type: application/json" \
-      https://api.bitbucket.org/2.0/repositories/turbulentcloud/rabbit-followers/pullrequests \
+    BB_RESPONSE=$(curl -s -X POST \
+      -u "${BB_USERNAME}:$BB_API_KEY" \
+      -H "Content-Type: application/json" \
+      "https://api.bitbucket.org/2.0/repositories/${BB_REPO}/pullrequests" \

Similar changes needed for GitHub (lines 128, 143), GitLab (line 164), and Azure DevOps (lines 183-186, 211).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
-u "john@turbulent.cloud:$BB_API_KEY" \
-H "Content-Type: application/json" \
https://api.bitbucket.org/2.0/repositories/turbulentcloud/rabbit-followers/pullrequests \
-d "{\"title\":\"$PR_TITLE_ESCAPED\",\"source\":{\"branch\":{\"name\":\"$BRANCH_NAME\"}},\"destination\":{\"branch\":{\"name\":\"$BASE_BRANCH\"}},\"description\":\"$PR_DESC_ESCAPED\"}")
-u "${BB_USERNAME}:$BB_API_KEY" \
-H "Content-Type: application/json" \
"https://api.bitbucket.org/2.0/repositories/${BB_REPO}/pullrequests" \
-d "{\"title\":\"$PR_TITLE_ESCAPED\",\"source\":{\"branch\":{\"name\":\"$BRANCH_NAME\"}},\"destination\":{\"branch\":{\"name\":\"$BASE_BRANCH\"}},\"description\":\"$PR_DESC_ESCAPED\"}")
🤖 Prompt for AI Agents
In create-pr.sh around lines 109 to 112, the Bitbucket API call contains
hardcoded credentials and repo path; replace the hardcoded
"john@turbulent.cloud:$BB_API_KEY" and the fixed repository URL with environment
variables or script parameters (e.g., BB_API_USER, BB_API_KEY, BB_REPO_OWNER,
BB_REPO_SLUG) and build the API URL from those variables; ensure the script
validates required vars and fails with a clear message if missing. Also apply
the same pattern to the other provider calls referenced in the review: GitHub
(lines 128, 143), GitLab (line 164) and Azure DevOps (lines 183-186, 211) so
credentials, organization/project/repo names and user information are not
hardcoded but read from environment variables or passed-in parameters.


BB_PR_URL=$(echo "$BB_RESPONSE" | python3 -c "import sys, json; data=json.load(sys.stdin); print(data.get('links', {}).get('html', {}).get('href', ''))" 2>/dev/null)
if [ -n "$BB_PR_URL" ]; then
echo -e " ${GREEN}✓ Bitbucket PR created: $BB_PR_URL${NC}"
else
echo -e " ${RED}✗ Failed to create Bitbucket PR${NC}"
echo -e " ${RED}Response: $BB_RESPONSE${NC}"
fi
else
echo -e " ${YELLOW}⊘ Skipping Bitbucket PR (no API key)${NC}"
fi

# Create PR on GitHub
echo -e " Creating PR on ${YELLOW}GitHub${NC}..."
if command -v gh &> /dev/null; then
GH_PR_URL=$(gh pr create --repo coderabbit-demo/rabbit-followers \
--title "$PR_TITLE" \
--body "$PR_DESCRIPTION_FULL" \
--base "$BASE_BRANCH" \
--head "$BRANCH_NAME" 2>&1)

if [[ $GH_PR_URL == http* ]]; then
echo -e " ${GREEN}✓ GitHub PR created: $GH_PR_URL${NC}"
else
echo -e " ${RED}✗ Failed to create GitHub PR: $GH_PR_URL${NC}"
fi
elif [ -n "$GH_TOKEN" ]; then
GH_RESPONSE=$(curl -s -X POST \
-H "Authorization: token $GH_TOKEN" \
-H "Content-Type: application/json" \
https://api.github.com/repos/coderabbit-demo/rabbit-followers/pulls \
-d "{
\"title\": \"$PR_TITLE\",
\"body\": \"$PR_DESCRIPTION_FULL\",
\"head\": \"$BRANCH_NAME\",
\"base\": \"$BASE_BRANCH\"
}")

GH_PR_URL=$(echo "$GH_RESPONSE" | grep -o '"html_url":"[^"]*"' | head -1 | sed 's/"html_url":"//;s/"$//')
if [ -n "$GH_PR_URL" ]; then
echo -e " ${GREEN}✓ GitHub PR created: $GH_PR_URL${NC}"
else
echo -e " ${RED}✗ Failed to create GitHub PR${NC}"
fi
else
echo -e " ${YELLOW}⊘ Skipping GitHub PR (no gh CLI or token found)${NC}"
fi

# Create PR on GitLab
echo -e " Creating PR on ${YELLOW}GitLab${NC}..."
if command -v glab &> /dev/null; then
GLAB_PR_URL=$(glab mr create --repo turbulent-cloud-group/rabbit-followers \
--title "$PR_TITLE" \
--description "$PR_DESCRIPTION_FULL" \
--source-branch "$BRANCH_NAME" \
--target-branch "$BASE_BRANCH" 2>&1 | grep -o 'https://[^ ]*')

if [ -n "$GLAB_PR_URL" ]; then
echo -e " ${GREEN}✓ GitLab MR created: $GLAB_PR_URL${NC}"
else
echo -e " ${RED}✗ Failed to create GitLab MR${NC}"
fi
else
echo -e " ${YELLOW}⊘ Skipping GitLab MR (glab CLI not found)${NC}"
fi

# Create PR on Azure DevOps
echo -e " Creating PR on ${YELLOW}Azure DevOps${NC}..."
if command -v az &> /dev/null; then
ADO_PR_RESPONSE=$(az repos pr create \
--organization https://dev.azure.com/turbulentcloud \
--project turbulentcloud \
--repository rabbit-followers \
--source-branch "$BRANCH_NAME" \
--target-branch "$BASE_BRANCH" \
--title "$PR_TITLE" \
--description "$PR_DESCRIPTION_FULL" \
--output json 2>&1)

# Extract the PR ID and construct proper web URL
ADO_PR_ID=$(echo "$ADO_PR_RESPONSE" | python3 -c "import sys, json; data=json.load(sys.stdin); print(data.get('pullRequestId', ''))" 2>/dev/null)

if [ -n "$ADO_PR_ID" ]; then
ADO_PR_URL="https://dev.azure.com/turbulentcloud/turbulentcloud/_git/rabbit-followers/pullrequest/$ADO_PR_ID"
echo -e " ${GREEN}✓ Azure DevOps PR created: $ADO_PR_URL${NC}"
else
echo -e " ${RED}✗ Failed to create Azure DevOps PR${NC}"
echo -e " ${RED}Response: $ADO_PR_RESPONSE${NC}"
fi
elif [ -f ~/.ado-token ]; then
# Fallback to REST API if az CLI not available
ADO_TOKEN=$(cat ~/.ado-token)
PR_DESC_ESCAPED=$(echo "$PR_DESCRIPTION_FULL" | sed 's/"/\\"/g' | awk '{printf "%s\\n", $0}' | sed '$ s/\\n$//')
PR_TITLE_ESCAPED=$(echo "$PR_TITLE" | sed 's/"/\\"/g')

ADO_RESPONSE=$(curl -s -X POST \
-u ":$ADO_TOKEN" \
-H "Content-Type: application/json" \
https://dev.azure.com/turbulentcloud/turbulentcloud/_apis/git/repositories/rabbit-followers/pullrequests?api-version=7.0 \
-d "{\"sourceRefName\":\"refs/heads/$BRANCH_NAME\",\"targetRefName\":\"refs/heads/$BASE_BRANCH\",\"title\":\"$PR_TITLE_ESCAPED\",\"description\":\"$PR_DESC_ESCAPED\"}")

ADO_PR_ID=$(echo "$ADO_RESPONSE" | python3 -c "import sys, json; data=json.load(sys.stdin); print(data.get('pullRequestId', ''))" 2>/dev/null)
if [ -n "$ADO_PR_ID" ]; then
ADO_PR_URL="https://dev.azure.com/turbulentcloud/turbulentcloud/_git/rabbit-followers/pullrequest/$ADO_PR_ID"
echo -e " ${GREEN}✓ Azure DevOps PR created: $ADO_PR_URL${NC}"
else
echo -e " ${RED}✗ Failed to create Azure DevOps PR${NC}"
echo -e " ${RED}Response: $ADO_RESPONSE${NC}"
fi
else
echo -e " ${YELLOW}⊘ Skipping Azure DevOps PR (az CLI not found and no token at ~/.ado-token)${NC}"
fi

echo ""
echo -e "${GREEN}Workflow completed!${NC}"
echo ""
echo "Summary:"
echo " Branch: $BRANCH_NAME"
echo " Pushed to: ado, bb, gh, gl"
echo " PRs created on available platforms"
62 changes: 57 additions & 5 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
height: 100vh;
overflow: hidden;
background:
linear-gradient(135deg, rgba(34, 139, 34, 0.1) 0%, rgba(0, 100, 0, 0.1) 100%),
radial-gradient(circle at 20% 50%, rgba(60, 179, 113, 0.3) 0%, transparent 50%),
radial-gradient(circle at 80% 50%, rgba(46, 125, 50, 0.3) 0%, transparent 50%),
linear-gradient(to bottom, #5cb85c 0%, #4a9d4a 50%, #3d8b3d 100%);
linear-gradient(135deg, rgba(30, 144, 255, 0.1) 0%, rgba(0, 0, 139, 0.1) 100%),
radial-gradient(circle at 20% 50%, rgba(70, 130, 180, 0.3) 0%, transparent 50%),
radial-gradient(circle at 80% 50%, rgba(65, 105, 225, 0.3) 0%, transparent 50%),
linear-gradient(to bottom, #4a90e2 0%, #357abd 50%, #2e6da4 100%);
background-size: 100% 100%, 200% 200%, 200% 200%, 100% 100%;
cursor: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><text y="28" font-size="28">🥕</text></svg>') 16 16, auto;
position: relative;
Expand Down Expand Up @@ -40,7 +40,8 @@
position: absolute;
font-size: 2rem;
transform: translate(-50%, -50%);
pointer-events: none;
pointer-events: auto;
cursor: pointer;
user-select: none;
filter: drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.3));
transition: transform 0.1s ease;
Expand All @@ -49,3 +50,54 @@
.rabbit:hover {
transform: translate(-50%, -50%) scale(1.2);
}

.explosion-particle {
position: absolute;
font-size: 1.5rem;
transform: translate(-50%, -50%);
pointer-events: none;
user-select: none;
z-index: 100;
}

.eagle {
position: absolute;
font-size: 2.5rem;
transform: translate(-50%, -50%);
pointer-events: none;
user-select: none;
filter: drop-shadow(3px 3px 6px rgba(0, 0, 0, 0.4));
transition: transform 0.2s ease, filter 0.2s ease;
z-index: 50;
}

.eagle.idle {
animation: eagleIdle 3s ease-in-out infinite;
}

.eagle.hunting {
transform: translate(-50%, -50%) scale(1.1);
filter: drop-shadow(3px 3px 8px rgba(255, 215, 0, 0.5));
}

.eagle.eating {
animation: eagleEating 0.5s ease-in-out;
}

@keyframes eagleIdle {
0%, 100% {
transform: translate(-50%, -50%) rotate(-5deg);
}
50% {
transform: translate(-50%, -50%) rotate(5deg);
}
}

@keyframes eagleEating {
0%, 100% {
transform: translate(-50%, -50%) scale(1);
}
50% {
transform: translate(-50%, -50%) scale(1.3);
}
}
Loading