Release v1.0.0: CMake migration and Git branching model #2

Merged
KofTwentyTwo merged 4 commits from develop into main 2025-08-15 22:21:15 +00:00
6 changed files with 540 additions and 233 deletions

No files matched your search

+16
View File
@@ -0,0 +1,16 @@
# Core firmware
/src/ @KofTwentyTwo
/include/ @KofTwentyTwo
/test/ @KofTwentyTwo
/platformio.ini @KofTwentyTwo
# Build system
/CMakeLists.txt @KofTwentyTwo
/CMakePresets.json @KofTwentyTwo
# Documentation
/README.md @KofTwentyTwo
/docs/ @KofTwentyTwo
# GitHub Actions
/.github/ @KofTwentyTwo
+25
View File
@@ -0,0 +1,25 @@
## Bug Description
<!-- A clear and concise description of what the bug is -->
## Steps to Reproduce
1.
2.
3.
## Expected Behavior
<!-- What you expected to happen -->
## Actual Behavior
<!-- What actually happened -->
## Environment
- **OS:** [e.g., macOS, Windows, Linux]
- **CMake Version:** [e.g., 4.1.0]
- **PlatformIO Version:** [e.g., 6.1.0]
- **Arduino IDE Version:** [if applicable]
## Additional Context
<!-- Any other context about the problem -->
## Screenshots
<!-- If applicable, add screenshots to help explain your problem -->
+45
View File
@@ -0,0 +1,45 @@
## Summary
<!-- Provide a brief summary of the changes and why they are needed -->
## Changes
<!-- List the specific changes made in this PR -->
- [ ]
- [ ]
- [ ]
## Testing
<!-- Complete the testing checklist before requesting review -->
- [ ] Native tests pass (`ctest --preset default`)
- [ ] PIO builds: simulide / uno_hw
- [ ] CMake configuration works locally
- [ ] All linting/formatting checks pass
- [ ] Manual testing completed (if applicable)
## Risk / Rollback
<!-- Assess the risk level and describe rollback strategy -->
**Risk Level:** [Low/Medium/High]
**Rollback Strategy:**
<!-- Describe how to rollback these changes if needed -->
## Additional Information
<!-- Any additional context, screenshots, or related issues -->
## Checklist
<!-- Ensure all items are completed before requesting review -->
- [ ] Code follows project style guidelines
- [ ] Self-review completed
- [ ] Code is documented where necessary
- [ ] Tests are included for new functionality
- [ ] All existing tests pass
- [ ] Branch follows naming convention (`feature/`, `bugfix/`, `release/`, `hotfix/`)
- [ ] Branch is up to date with base branch
- [ ] Commit messages follow conventional commit format
## Related Issues
<!-- Link any related issues or discussions -->
Closes #
Related to #
## Screenshots (if applicable)
<!-- Add screenshots for UI changes or visual improvements -->
+131
View File
@@ -0,0 +1,131 @@
#!/bin/bash
# Create Branch Script for Git Branching Model
# Usage: ./create-branch.sh [type] [name]
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Branch types and their source branches
declare -A BRANCH_TYPES=(
["feature"]="develop"
["bugfix"]="main"
["release"]="develop"
["hotfix"]="main"
)
# Function to show usage
show_usage() {
echo -e "${BLUE}🚀 Create Branch Script for Git Branching Model${NC}"
echo "=================================================="
echo ""
echo "Usage: $0 [type] [name]"
echo ""
echo "Branch Types:"
echo " feature - New features (from develop)"
echo " bugfix - Bug fixes (from main)"
echo " release - Release preparation (from develop)"
echo " hotfix - Urgent fixes (from main)"
echo ""
echo "Examples:"
echo " $0 feature arming-siren"
echo " $0 bugfix serial-monitor-timeout"
echo " $0 release 1.2.0"
echo " $0 hotfix 1.2.1-watchdog-crash"
echo ""
echo "Branch Naming Convention:"
echo " feature/arming-siren"
echo " bugfix/serial-monitor-timeout"
echo " release/1.2.0"
echo " hotfix/1.2.1-watchdog-crash"
}
# Check if we have the right number of arguments
if [ $# -ne 2 ]; then
show_usage
exit 1
fi
BRANCH_TYPE="$1"
BRANCH_NAME="$2"
# Validate branch type
if [[ ! ${BRANCH_TYPES[$BRANCH_TYPE]} ]]; then
echo -e "${RED}❌ Invalid branch type: $BRANCH_TYPE${NC}"
echo "Valid types: ${!BRANCH_TYPES[@]}"
exit 1
fi
# Get source branch
SOURCE_BRANCH="${BRANCH_TYPES[$BRANCH_TYPE]}"
# Create full branch name
FULL_BRANCH_NAME="$BRANCH_TYPE/$BRANCH_NAME"
echo -e "${BLUE}🚀 Creating new branch: $FULL_BRANCH_NAME${NC}"
echo "=================================================="
echo ""
# Check if we're in a git repository
if ! git rev-parse --git-dir > /dev/null 2>&1; then
echo -e "${RED}❌ Not in a git repository${NC}"
exit 1
fi
# Check if source branch exists locally
if ! git show-ref --verify --quiet refs/heads/$SOURCE_BRANCH; then
echo -e "${YELLOW}⚠️ Source branch '$SOURCE_BRANCH' doesn't exist locally${NC}"
echo "Fetching from remote..."
git fetch origin $SOURCE_BRANCH:$SOURCE_BRANCH
fi
# Check if source branch exists remotely
if ! git show-ref --verify --quiet refs/remotes/origin/$SOURCE_BRANCH; then
echo -e "${RED}❌ Source branch '$SOURCE_BRANCH' doesn't exist remotely${NC}"
echo "Please ensure the source branch exists on GitHub first."
exit 1
fi
# Check if branch already exists
if git show-ref --verify --quiet refs/heads/$FULL_BRANCH_NAME; then
echo -e "${RED}❌ Branch '$FULL_BRANCH_NAME' already exists locally${NC}"
exit 1
fi
if git show-ref --verify --quiet refs/remotes/origin/$FULL_BRANCH_NAME; then
echo -e "${RED}❌ Branch '$FULL_BRANCH_NAME' already exists remotely${NC}"
exit 1
fi
# Create and switch to new branch
echo -e "${BLUE}📋 Creating branch from $SOURCE_BRANCH...${NC}"
git checkout $SOURCE_BRANCH
echo -e "${BLUE}🔄 Updating $SOURCE_BRANCH from remote...${NC}"
git pull origin $SOURCE_BRANCH
echo -e "${BLUE}🌿 Creating new branch: $FULL_BRANCH_NAME${NC}"
git checkout -b $FULL_BRANCH_NAME
echo -e "${BLUE}📤 Pushing branch to remote...${NC}"
git push -u origin $FULL_BRANCH_NAME
echo ""
echo -e "${GREEN}✅ Successfully created branch: $FULL_BRANCH_NAME${NC}"
echo ""
echo -e "${BLUE}📚 Next steps:${NC}"
echo " 1. Make your changes"
echo " 2. Commit and push: git add . && git commit -m 'your message' && git push"
echo " 3. Create a Pull Request to $SOURCE_BRANCH"
echo ""
echo -e "${BLUE}🔄 Branch flow:${NC}"
echo " $FULL_BRANCH_NAME → $SOURCE_BRANCH → main (via PR)"
echo ""
echo -e "${YELLOW}💡 Tip: Use conventional commit messages:${NC}"
echo " feat: add arming siren functionality"
echo " fix: resolve serial monitor timeout issue"
echo " docs: update README with new features"
+228 -233
View File
@@ -1,9 +1,7 @@
#!/bin/bash
# Luke's Rocket Launcher - Release Helper Script
# This script helps create semantic versioned releases
set -e
# Release Management Script for Git Branching Model
# Usage: ./release.sh [version] [type]
# Colors for output
RED='\033[0;31m'
@@ -12,242 +10,239 @@ YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to print colored output
print_status() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
print_header() {
echo -e "${BLUE}=== $1 ===${NC}"
}
# Function to show usage
show_usage() {
echo "Usage: $0 [OPTIONS]"
echo -e "${BLUE}🚀 Release Management Script for Git Branching Model${NC}"
echo "========================================================"
echo ""
echo "Options:"
echo " -v, --version VERSION Specify exact version (e.g., 1.0.0)"
echo " -t, --type TYPE Release type: patch, minor, major (default: patch)"
echo " -m, --message MESSAGE Custom release message"
echo " -p, --pre-release Mark as pre-release (alpha/beta/rc)"
echo " -d, --dry-run Show what would be done without doing it"
echo " -w, --watch Watch the release workflow progress"
echo " -h, --help Show this help message"
echo "Usage: $0 [version] [type]"
echo ""
echo "Types:"
echo " start - Start a new release (creates release branch)"
echo " finish - Finish a release (merges to main and develop, creates tag)"
echo " hotfix - Create a hotfix release (from main)"
echo ""
echo "Examples:"
echo " $0 --type minor # Auto-increment minor version"
echo " $0 --version 1.0.0 # Release specific version"
echo " $0 --type patch --message 'Bug fix release'"
echo " $0 --version 0.1.0 --pre-release # Create pre-release"
echo " $0 --version 1.0.0 --watch # Create release and watch progress"
echo " $0 1.2.0 start # Start release 1.2.0"
echo " $0 1.2.0 finish # Finish release 1.2.0"
echo " $0 1.2.1 hotfix # Create hotfix 1.2.1"
echo ""
echo "Conventional Commit Messages:"
echo " feat: new feature"
echo " fix: bug fix"
echo " docs: documentation changes"
echo " style: code style changes"
echo " refactor: code refactoring"
echo " test: adding or updating tests"
echo " chore: maintenance tasks"
echo "Release Flow:"
echo " 1. ./release.sh 1.2.0 start # Creates release/1.2.0 from develop"
echo " 2. Make release changes (version, docs, etc.)"
echo " 3. ./release.sh 1.2.0 finish # Merges to main and develop, creates tag"
}
# Parse command line arguments
VERSION=""
RELEASE_TYPE="patch"
MESSAGE=""
PRE_RELEASE=false
DRY_RUN=false
WATCH=false
while [[ $# -gt 0 ]]; do
case $1 in
-v|--version)
VERSION="$2"
shift 2
;;
-t|--type)
RELEASE_TYPE="$2"
shift 2
;;
-m|--message)
MESSAGE="$2"
shift 2
;;
-p|--pre-release)
PRE_RELEASE=true
shift
;;
-d|--dry-run)
DRY_RUN=true
shift
;;
-w|--watch)
WATCH=true
shift
;;
-h|--help)
show_usage
exit 0
;;
*)
echo "Unknown option: $1"
show_usage
exit 1
;;
esac
done
# Validate release type
if [[ ! "$RELEASE_TYPE" =~ ^(patch|minor|major)$ ]]; then
print_error "Invalid release type: $RELEASE_TYPE"
print_error "Must be one of: patch, minor, major"
exit 1
fi
print_header "Luke's Rocket Launcher Release Helper"
# Get current version from git tags
CURRENT_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
CURRENT_VERSION=${CURRENT_TAG#v}
print_status "Current version: $CURRENT_VERSION"
# Calculate new version
if [[ -n "$VERSION" ]]; then
NEW_VERSION="$VERSION"
print_status "Using specified version: $NEW_VERSION"
else
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
case "$RELEASE_TYPE" in
"major")
NEW_VERSION="$((MAJOR + 1)).0.0"
print_status "Incrementing major version: $CURRENT_VERSION → $NEW_VERSION"
;;
"minor")
NEW_VERSION="$MAJOR.$((MINOR + 1)).0"
print_status "Incrementing minor version: $CURRENT_VERSION → $NEW_VERSION"
;;
"patch")
NEW_VERSION="$MAJOR.$MINOR.$((PATCH + 1))"
print_status "Incrementing patch version: $CURRENT_VERSION → $NEW_VERSION"
;;
esac
fi
NEW_TAG="v$NEW_VERSION"
print_status "New version: $NEW_VERSION"
print_status "New tag: $NEW_TAG"
# Check if working directory is clean
if [[ -n $(git status --porcelain) ]]; then
print_warning "Working directory is not clean. Please commit or stash changes first."
git status --short
exit 1
fi
# Check if tag already exists
if git tag -l | grep -q "^$NEW_TAG$"; then
print_error "Tag $NEW_TAG already exists!"
exit 1
fi
# Show what will be done
print_header "Release Plan"
echo "1. Create tag: $NEW_TAG"
if [[ "$PRE_RELEASE" == true ]]; then
echo " ⚠️ This will be marked as a PRE-RELEASE"
fi
echo "2. Push tag to remote"
echo "3. Trigger GitHub Actions release workflow"
echo "4. Create GitHub release with firmware files"
echo "5. Package firmware files for download"
if [[ "$DRY_RUN" == true ]]; then
print_status "DRY RUN - No changes will be made"
exit 0
fi
# Confirm release
echo ""
read -p "Proceed with release? (y/N): " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
print_status "Release cancelled"
exit 0
fi
# Create and push tag
print_status "Creating tag $NEW_TAG..."
if [[ "$PRE_RELEASE" == true ]]; then
TAG_MESSAGE="Pre-release $NEW_TAG
$MESSAGE
⚠️ This is a pre-release version. Not recommended for production use."
else
TAG_MESSAGE="Release $NEW_TAG
$MESSAGE"
fi
git tag -a "$NEW_TAG" -m "$TAG_MESSAGE"
print_status "Pushing tag to remote..."
git push origin "$NEW_TAG"
print_header "Release Complete!"
if [[ "$PRE_RELEASE" == true ]]; then
print_warning "Tag $NEW_TAG has been created and pushed as a PRE-RELEASE"
print_warning "Remember to mark this as a pre-release in GitHub after the workflow completes"
else
print_status "Tag $NEW_TAG has been created and pushed"
fi
print_status "GitHub Actions will automatically:"
echo " • Build and test the firmware"
echo " • Create a GitHub release"
echo " • Package firmware files for download"
echo ""
print_status "Monitor progress at: https://github.com/KofTwentyTwo/Lukes-Rocket-Launcher/actions"
print_status "Download firmware at: https://github.com/KofTwentyTwo/Lukes-Rocket-Launcher/releases"
# Watch workflow if requested
if [[ "$WATCH" == true ]]; then
echo ""
print_status "Watching release workflow progress..."
print_status "Looking for workflow triggered by tag $NEW_TAG..."
# Wait a moment for the workflow to start
sleep 3
# Find the workflow run for this tag using JSON output for reliable parsing
WORKFLOW_RUN=$(gh run list --workflow="Build and Test Arduino Firmware" --limit 10 --json databaseId,headBranch | jq -r ".[] | select(.headBranch == \"$NEW_TAG\") | .databaseId" | head -1)
if [[ -n "$WORKFLOW_RUN" ]]; then
print_status "Found workflow run: $WORKFLOW_RUN"
print_status "Starting workflow monitor..."
echo ""
# Watch the workflow
gh run watch "$WORKFLOW_RUN"
# Show final status
echo ""
print_status "Workflow completed! Final status:"
gh run view "$WORKFLOW_RUN" --json status,conclusion,url --jq '.status + " (" + .conclusion + ")"'
echo ""
print_status "View details at: $(gh run view "$WORKFLOW_RUN" --json url --jq '.url')"
else
print_warning "Could not find workflow run for tag $NEW_TAG"
print_status "You can manually check at: https://github.com/KofTwentyTwo/Lukes-Rocket-Launcher/actions"
# Function to validate version format
validate_version() {
local version=$1
if [[ ! $version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo -e "${RED}❌ Invalid version format: $version${NC}"
echo "Version must be in format: X.Y.Z (e.g., 1.2.0)"
exit 1
fi
}
# Function to check if we're in a git repository
check_git_repo() {
if ! git rev-parse --git-dir > /dev/null 2>&1; then
echo -e "${RED}❌ Not in a git repository${NC}"
exit 1
fi
}
# Function to check if branch exists
branch_exists() {
local branch=$1
git show-ref --verify --quiet refs/heads/$branch || \
git show-ref --verify --quiet refs/remotes/origin/$branch
}
# Function to start a release
start_release() {
local version=$1
local release_branch="release/$version"
echo -e "${BLUE}🚀 Starting release $version${NC}"
echo "=================================="
# Check if release branch already exists
if branch_exists $release_branch; then
echo -e "${RED}❌ Release branch $release_branch already exists${NC}"
exit 1
fi
# Ensure we're on develop and it's up to date
if [[ $(git branch --show-current) != "develop" ]]; then
echo -e "${BLUE}📋 Switching to develop branch...${NC}"
git checkout develop
fi
echo -e "${BLUE}🔄 Updating develop from remote...${NC}"
git pull origin develop
echo -e "${BLUE}🌿 Creating release branch: $release_branch${NC}"
git checkout -b $release_branch
echo -e "${BLUE}📤 Pushing release branch to remote...${NC}"
git push -u origin $release_branch
echo ""
echo -e "${GREEN}✅ Release $version started successfully!${NC}"
echo ""
echo -e "${BLUE}📚 Next steps:${NC}"
echo " 1. Update version numbers in code"
echo " 2. Update CHANGELOG.md"
echo " 3. Update documentation"
echo " 4. Test thoroughly"
echo " 5. Run: $0 $version finish"
}
# Function to finish a release
finish_release() {
local version=$1
local release_branch="release/$version"
local tag="v$version"
echo -e "${BLUE}🎯 Finishing release $version${NC}"
echo "=================================="
# Check if we're on the release branch
if [[ $(git branch --show-current) != $release_branch ]]; then
echo -e "${BLUE}📋 Switching to release branch...${NC}"
git checkout $release_branch
fi
# Ensure release branch is up to date
echo -e "${BLUE}🔄 Updating release branch from remote...${NC}"
git pull origin $release_branch
# Check if there are uncommitted changes
if ! git diff-index --quiet HEAD --; then
echo -e "${YELLOW}⚠️ You have uncommitted changes. Please commit or stash them first.${NC}"
git status --short
exit 1
fi
echo -e "${BLUE}🔍 Checking if tag already exists...${NC}"
if git tag -l | grep -q "^$tag$"; then
echo -e "${RED}❌ Tag $tag already exists${NC}"
exit 1
fi
echo -e "${BLUE}📋 Switching to main branch...${NC}"
git checkout main
git pull origin main
echo -e "${BLUE}🔄 Merging release branch to main...${NC}"
git merge --no-ff $release_branch -m "Release $version"
echo -e "${BLUE}🏷️ Creating tag $tag...${NC}"
git tag -a $tag -m "Release $version"
echo -e "${BLUE}📤 Pushing main and tag to remote...${NC}"
git push origin main
git push origin $tag
echo -e "${BLUE}📋 Switching to develop branch...${NC}"
git checkout develop
git pull origin develop
echo -e "${BLUE}🔄 Merging release branch to develop...${NC}"
git merge --no-ff $release_branch -m "Merge release $version into develop"
echo -e "${BLUE}📤 Pushing develop to remote...${NC}"
git push origin develop
echo -e "${BLUE}🧹 Cleaning up release branch...${NC}"
git branch -d $release_branch
git push origin --delete $release_branch
echo ""
echo -e "${GREEN}✅ Release $version completed successfully!${NC}"
echo ""
echo -e "${BLUE}🎉 What was accomplished:${NC}"
echo " ✅ Release branch merged to main"
echo " ✅ Tag $tag created and pushed"
echo " ✅ Release branch merged to develop"
echo " ✅ Release branch cleaned up"
echo ""
echo -e "${BLUE}📚 Next steps:${NC}"
echo " 1. Create GitHub Release from tag $tag"
echo " 2. Deploy to production"
echo " 3. Announce the release"
}
# Function to create a hotfix
create_hotfix() {
local version=$1
local hotfix_branch="hotfix/$version"
local tag="v$version"
echo -e "${BLUE}🚨 Creating hotfix $version${NC}"
echo "================================"
# Check if hotfix branch already exists
if branch_exists $hotfix_branch; then
echo -e "${RED}❌ Hotfix branch $hotfix_branch already exists${NC}"
exit 1
fi
# Ensure we're on main and it's up to date
if [[ $(git branch --show-current) != "main" ]]; then
echo -e "${BLUE}📋 Switching to main branch...${NC}"
git checkout main
fi
echo -e "${BLUE}🔄 Updating main from remote...${NC}"
git pull origin main
echo -e "${BLUE}🌿 Creating hotfix branch: $hotfix_branch${NC}"
git checkout -b $hotfix_branch
echo -e "${BLUE}📤 Pushing hotfix branch to remote...${NC}"
git push -u origin $hotfix_branch
echo ""
echo -e "${GREEN}✅ Hotfix $version created successfully!${NC}"
echo ""
echo -e "${BLUE}📚 Next steps:${NC}"
echo " 1. Fix the urgent issue"
echo " 2. Test thoroughly"
echo " 3. Commit and push changes"
echo " 4. Create PR to main"
echo " 5. After merge, run: $0 $version finish"
}
# Main script logic
if [ $# -ne 2 ]; then
show_usage
exit 1
fi
VERSION=$1
TYPE=$2
# Validate version format
validate_version $VERSION
# Check if we're in a git repository
check_git_repo
# Execute based on type
case $TYPE in
"start")
start_release $VERSION
;;
"finish")
finish_release $VERSION
;;
"hotfix")
create_hotfix $VERSION
;;
*)
echo -e "${RED}❌ Invalid type: $TYPE${NC}"
echo "Valid types: start, finish, hotfix"
exit 1
;;
esac
+95
View File
@@ -0,0 +1,95 @@
#!/bin/bash
# Setup Branch Protection Rules for Git Branching Model
# Run this script to configure branch protection on GitHub
echo "🚀 Setting up Branch Protection Rules for Git Branching Model"
echo "=============================================================="
echo ""
# Check if gh CLI is installed
if ! command -v gh &> /dev/null; then
echo "❌ GitHub CLI (gh) is not installed. Please install it first:"
echo " https://cli.github.com/"
exit 1
fi
# Check if authenticated
if ! gh auth status &> /dev/null; then
echo "❌ Not authenticated with GitHub. Please run: gh auth login"
exit 1
fi
REPO="KofTwentyTwo/Lukes-Rocket-Launcher"
echo "📋 Setting up branch protection for: $REPO"
echo ""
# Main branch protection
echo "🔒 Setting up MAIN branch protection..."
gh api repos/$REPO/branches/main/protection \
--method PUT \
--input - << EOF
{
"required_status_checks": {
"strict": true,
"contexts": ["Build and Test Arduino Firmware"]
},
"enforce_admins": true,
"required_pull_request_reviews": {
"required_approving_review_count": 1,
"dismiss_stale_reviews": true,
"require_code_owner_reviews": false
},
"restrictions": null
}
EOF
if [ $? -eq 0 ]; then
echo "✅ MAIN branch protection configured"
else
echo "❌ Failed to configure MAIN branch protection"
fi
echo ""
# Develop branch protection (optional, but recommended)
echo "🔒 Setting up DEVELOP branch protection..."
gh api repos/$REPO/branches/develop/protection \
--method PUT \
--input - << EOF
{
"required_status_checks": {
"strict": true,
"contexts": ["Build and Test Arduino Firmware"]
},
"enforce_admins": false,
"required_pull_request_reviews": {
"required_approving_review_count": 1,
"dismiss_stale_reviews": true,
"require_code_owner_reviews": false
},
"restrictions": null
}
EOF
if [ $? -eq 0 ]; then
echo "✅ DEVELOP branch protection configured"
else
echo "❌ Failed to configure DEVELOP branch protection"
fi
echo ""
echo "🎯 Branch Protection Setup Complete!"
echo ""
echo "📚 Next steps:"
echo " 1. Create feature branches from develop: git checkout -b feature/your-feature develop"
echo " 2. Create bugfix branches from main: git checkout -b bugfix/your-bug main"
echo " 3. Create release branches from develop: git checkout -b release/1.0.0 develop"
echo " 4. Create hotfix branches from main: git checkout -b hotfix/1.0.1 main"
echo ""
echo "🔄 Workflow:"
echo " Feature → Develop → Main (via PR)"
echo " Bugfix → Main (via PR)"
echo " Release → Develop → Main (via PR)"
echo " Hotfix → Main (via PR)"