Initial commit
This commit is contained in:
186
scripts/create-commit-message.py
Executable file
186
scripts/create-commit-message.py
Executable file
@@ -0,0 +1,186 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
TYPO3 Core Contribution Commit Message Generator
|
||||
Creates properly formatted commit messages following TYPO3 standards
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import re
|
||||
from typing import Optional
|
||||
|
||||
|
||||
COMMIT_TYPES = {
|
||||
'BUGFIX': 'Bug fixes',
|
||||
'FEATURE': 'New features (main branch only)',
|
||||
'TASK': 'Refactoring, cleanup, miscellaneous',
|
||||
'DOCS': 'Documentation changes',
|
||||
'SECURITY': 'Security vulnerability fixes'
|
||||
}
|
||||
|
||||
BREAKING_CHANGE_PREFIX = '[!!!]'
|
||||
|
||||
|
||||
def validate_subject(subject: str, has_breaking: bool) -> tuple[bool, Optional[str]]:
|
||||
"""Validate subject line against TYPO3 rules"""
|
||||
max_length = 52 if not has_breaking else 47 # Account for [!!!] prefix
|
||||
|
||||
if len(subject) > 72:
|
||||
return False, "Subject line exceeds 72 characters (absolute limit)"
|
||||
|
||||
if len(subject) > max_length:
|
||||
return False, f"Subject line exceeds {max_length} characters (recommended limit)"
|
||||
|
||||
if not subject[0].isupper():
|
||||
return False, "Subject must start with uppercase letter"
|
||||
|
||||
if subject.endswith('.'):
|
||||
return False, "Subject should not end with a period"
|
||||
|
||||
# Check for imperative mood (simple heuristic)
|
||||
past_tense_endings = ['ed', 'ing']
|
||||
first_word = subject.split()[0].lower()
|
||||
if any(first_word.endswith(end) for end in past_tense_endings):
|
||||
return False, f"Use imperative mood ('{first_word}' appears to be past/present continuous tense)"
|
||||
|
||||
return True, None
|
||||
|
||||
|
||||
def wrap_text(text: str, width: int = 72) -> str:
|
||||
"""Wrap text at specified width"""
|
||||
words = text.split()
|
||||
lines = []
|
||||
current_line = []
|
||||
current_length = 0
|
||||
|
||||
for word in words:
|
||||
word_length = len(word)
|
||||
if current_length + word_length + len(current_line) > width:
|
||||
if current_line:
|
||||
lines.append(' '.join(current_line))
|
||||
current_line = [word]
|
||||
current_length = word_length
|
||||
else:
|
||||
current_line.append(word)
|
||||
current_length += word_length
|
||||
|
||||
if current_line:
|
||||
lines.append(' '.join(current_line))
|
||||
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
def parse_releases(releases_str: str) -> list[str]:
|
||||
"""Parse comma-separated release versions"""
|
||||
releases = [r.strip() for r in releases_str.split(',')]
|
||||
# Validate format
|
||||
valid_releases = []
|
||||
for release in releases:
|
||||
if release == 'main' or re.match(r'^\d+\.\d+$', release):
|
||||
valid_releases.append(release)
|
||||
else:
|
||||
print(f"Warning: Invalid release format '{release}', skipping")
|
||||
return valid_releases
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate TYPO3-compliant commit messages',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog='''
|
||||
Examples:
|
||||
%(prog)s --issue 105737 --type BUGFIX
|
||||
%(prog)s --issue 105737 --type FEATURE --breaking
|
||||
%(prog)s --type TASK --related 12345,12346
|
||||
'''
|
||||
)
|
||||
|
||||
parser.add_argument('--issue', type=int, help='Forge issue number')
|
||||
parser.add_argument('--related', help='Related issue numbers (comma-separated)')
|
||||
parser.add_argument('--type', choices=COMMIT_TYPES.keys(), required=True,
|
||||
help='Commit type')
|
||||
parser.add_argument('--breaking', action='store_true',
|
||||
help='Mark as breaking change (adds [!!!] prefix)')
|
||||
parser.add_argument('--releases', default='main',
|
||||
help='Target releases (comma-separated, e.g., "main, 13.4, 12.4")')
|
||||
parser.add_argument('--output', help='Output file (default: print to stdout)')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Interactive mode
|
||||
print("=== TYPO3 Commit Message Generator ===\n")
|
||||
|
||||
# Get subject line
|
||||
print(f"Commit Type: [{args.type}]")
|
||||
if args.breaking:
|
||||
print(f"Breaking Change: Yes (will add {BREAKING_CHANGE_PREFIX} prefix)")
|
||||
print()
|
||||
|
||||
subject = input("Enter subject line (max 52 chars, imperative mood): ").strip()
|
||||
|
||||
# Validate subject
|
||||
valid, error = validate_subject(subject, args.breaking)
|
||||
if not valid:
|
||||
print(f"\n❌ Error: {error}")
|
||||
sys.exit(1)
|
||||
|
||||
# Get description
|
||||
print("\nEnter description (explain how and why, not what).")
|
||||
print("Press Ctrl+D (Linux/Mac) or Ctrl+Z (Windows) when done:")
|
||||
description_lines = []
|
||||
try:
|
||||
while True:
|
||||
line = input()
|
||||
description_lines.append(line)
|
||||
except EOFError:
|
||||
pass
|
||||
|
||||
description = '\n'.join(description_lines).strip()
|
||||
if description:
|
||||
description = wrap_text(description)
|
||||
|
||||
# Build commit message
|
||||
type_prefix = f"{BREAKING_CHANGE_PREFIX}{args.type}" if args.breaking else args.type
|
||||
message = f"[{type_prefix}] {subject}\n\n"
|
||||
|
||||
if description:
|
||||
message += f"{description}\n\n"
|
||||
|
||||
# Add footer
|
||||
if args.issue:
|
||||
message += f"Resolves: #{args.issue}\n"
|
||||
|
||||
if args.related:
|
||||
related_issues = [f"#{num.strip()}" for num in args.related.split(',')]
|
||||
for issue in related_issues:
|
||||
message += f"Related: {issue}\n"
|
||||
|
||||
releases = parse_releases(args.releases)
|
||||
if releases:
|
||||
message += f"Releases: {', '.join(releases)}\n"
|
||||
|
||||
# Output
|
||||
print("\n" + "="*60)
|
||||
print("Generated Commit Message:")
|
||||
print("="*60)
|
||||
print(message)
|
||||
print("="*60)
|
||||
print("\nNote: Change-Id will be added automatically by git hook")
|
||||
print("="*60)
|
||||
|
||||
if args.output:
|
||||
with open(args.output, 'w') as f:
|
||||
f.write(message)
|
||||
print(f"\n✓ Commit message saved to: {args.output}")
|
||||
print(f" Use: git commit -F {args.output}")
|
||||
else:
|
||||
print("\nTo use this message:")
|
||||
print(" 1. Copy the message above")
|
||||
print(" 2. Run: git commit")
|
||||
print(" 3. Paste into your editor")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
192
scripts/create-forge-issue.sh
Executable file
192
scripts/create-forge-issue.sh
Executable file
@@ -0,0 +1,192 @@
|
||||
#!/bin/bash
|
||||
# Create TYPO3 Forge issue via Redmine REST API
|
||||
#
|
||||
# Usage:
|
||||
# 1. Get your API key from https://forge.typo3.org/my/account
|
||||
# 2. Set environment variable: export FORGE_API_KEY="your-key-here"
|
||||
# 3. Run: ./scripts/create-forge-issue.sh
|
||||
|
||||
set -e
|
||||
|
||||
# Color output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Check for API key
|
||||
if [ -z "$FORGE_API_KEY" ]; then
|
||||
echo -e "${RED}Error: FORGE_API_KEY environment variable not set${NC}"
|
||||
echo ""
|
||||
echo "Get your API key from: https://forge.typo3.org/my/account"
|
||||
echo "Then set it: export FORGE_API_KEY=\"your-key-here\""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for required tools
|
||||
for tool in curl jq; do
|
||||
if ! command -v $tool &> /dev/null; then
|
||||
echo -e "${RED}Error: $tool is required but not installed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "${GREEN}TYPO3 Forge Issue Creator${NC}"
|
||||
echo ""
|
||||
|
||||
# Interactive prompts
|
||||
read -p "Issue subject (title): " SUBJECT
|
||||
if [ -z "$SUBJECT" ]; then
|
||||
echo -e "${RED}Error: Subject is required${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Issue description (multi-line, press Ctrl+D when done):"
|
||||
DESCRIPTION=$(cat)
|
||||
if [ -z "$DESCRIPTION" ]; then
|
||||
echo -e "${RED}Error: Description is required${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Select tracker type:"
|
||||
echo " 1) Bug"
|
||||
echo " 2) Feature"
|
||||
echo " 3) Task"
|
||||
read -p "Choice [1]: " TRACKER_CHOICE
|
||||
TRACKER_CHOICE=${TRACKER_CHOICE:-1}
|
||||
|
||||
case $TRACKER_CHOICE in
|
||||
1) TRACKER_ID=1; TRACKER_NAME="Bug" ;;
|
||||
2) TRACKER_ID=2; TRACKER_NAME="Feature" ;;
|
||||
3) TRACKER_ID=4; TRACKER_NAME="Task" ;;
|
||||
*) echo -e "${RED}Invalid choice${NC}"; exit 1 ;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
echo "Select priority:"
|
||||
echo " 1) Must have"
|
||||
echo " 2) Should have (recommended)"
|
||||
echo " 3) Could have"
|
||||
read -p "Choice [2]: " PRIORITY_CHOICE
|
||||
PRIORITY_CHOICE=${PRIORITY_CHOICE:-2}
|
||||
|
||||
case $PRIORITY_CHOICE in
|
||||
1) PRIORITY_ID=3; PRIORITY_NAME="Must have" ;;
|
||||
2) PRIORITY_ID=4; PRIORITY_NAME="Should have" ;;
|
||||
3) PRIORITY_ID=5; PRIORITY_NAME="Could have" ;;
|
||||
*) echo -e "${RED}Invalid choice${NC}"; exit 1 ;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
read -p "TYPO3 version affected (e.g., 13, 14) [13]: " TYPO3_VERSION
|
||||
TYPO3_VERSION=${TYPO3_VERSION:-13}
|
||||
|
||||
echo ""
|
||||
echo "Select category (common ones, or enter ID manually):"
|
||||
echo " 1) Miscellaneous (975)"
|
||||
echo " 2) Backend API (971)"
|
||||
echo " 3) Backend User Interface (972)"
|
||||
echo " 4) Frontend (977)"
|
||||
echo " 5) Database API (974)"
|
||||
echo " 6) Indexed Search (1000)"
|
||||
echo " 7) Extension Manager (976)"
|
||||
echo " 8) Documentation (1004)"
|
||||
echo " 9) Enter category ID manually"
|
||||
read -p "Choice [1]: " CATEGORY_CHOICE
|
||||
CATEGORY_CHOICE=${CATEGORY_CHOICE:-1}
|
||||
|
||||
case $CATEGORY_CHOICE in
|
||||
1) CATEGORY_ID=975; CATEGORY_NAME="Miscellaneous" ;;
|
||||
2) CATEGORY_ID=971; CATEGORY_NAME="Backend API" ;;
|
||||
3) CATEGORY_ID=972; CATEGORY_NAME="Backend User Interface" ;;
|
||||
4) CATEGORY_ID=977; CATEGORY_NAME="Frontend" ;;
|
||||
5) CATEGORY_ID=974; CATEGORY_NAME="Database API" ;;
|
||||
6) CATEGORY_ID=1000; CATEGORY_NAME="Indexed Search" ;;
|
||||
7) CATEGORY_ID=976; CATEGORY_NAME="Extension Manager" ;;
|
||||
8) CATEGORY_ID=1004; CATEGORY_NAME="Documentation" ;;
|
||||
9)
|
||||
read -p "Enter category ID: " CATEGORY_ID
|
||||
CATEGORY_NAME="Custom ($CATEGORY_ID)"
|
||||
;;
|
||||
*) echo -e "${RED}Invalid choice${NC}"; exit 1 ;;
|
||||
esac
|
||||
|
||||
# Optional tags
|
||||
echo ""
|
||||
read -p "Tags (comma-separated, optional): " TAGS
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo -e "${YELLOW}Summary:${NC}"
|
||||
echo " Tracker: $TRACKER_NAME"
|
||||
echo " Subject: $SUBJECT"
|
||||
echo " Priority: $PRIORITY_NAME"
|
||||
echo " Category: $CATEGORY_NAME"
|
||||
echo " TYPO3 Version: $TYPO3_VERSION"
|
||||
[ -n "$TAGS" ] && echo " Tags: $TAGS"
|
||||
echo ""
|
||||
|
||||
read -p "Create this issue? [Y/n]: " CONFIRM
|
||||
CONFIRM=${CONFIRM:-Y}
|
||||
|
||||
if [[ ! $CONFIRM =~ ^[Yy]$ ]]; then
|
||||
echo "Cancelled."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Build JSON payload
|
||||
JSON_PAYLOAD=$(jq -n \
|
||||
--arg subject "$SUBJECT" \
|
||||
--arg description "$DESCRIPTION" \
|
||||
--argjson tracker "$TRACKER_ID" \
|
||||
--argjson category "$CATEGORY_ID" \
|
||||
--argjson priority "$PRIORITY_ID" \
|
||||
--arg typo3_version "$TYPO3_VERSION" \
|
||||
--arg tags "$TAGS" \
|
||||
'{
|
||||
issue: {
|
||||
project_id: "typo3cms-core",
|
||||
subject: $subject,
|
||||
description: $description,
|
||||
tracker_id: $tracker,
|
||||
category_id: $category,
|
||||
priority_id: $priority,
|
||||
custom_fields: [
|
||||
{id: 4, value: $typo3_version}
|
||||
] + (if $tags != "" then [{id: 3, value: $tags}] else [] end)
|
||||
}
|
||||
}')
|
||||
|
||||
# Create issue
|
||||
echo ""
|
||||
echo -e "${YELLOW}Creating issue...${NC}"
|
||||
|
||||
RESPONSE=$(curl -s -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-Redmine-API-Key: $FORGE_API_KEY" \
|
||||
-d "$JSON_PAYLOAD" \
|
||||
https://forge.typo3.org/issues.json)
|
||||
|
||||
# Check for errors
|
||||
if echo "$RESPONSE" | jq -e '.errors' > /dev/null 2>&1; then
|
||||
echo -e "${RED}Error creating issue:${NC}"
|
||||
echo "$RESPONSE" | jq -r '.errors[]'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract issue details
|
||||
ISSUE_ID=$(echo "$RESPONSE" | jq -r '.issue.id')
|
||||
ISSUE_URL="https://forge.typo3.org/issues/${ISSUE_ID}"
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}Success! Issue created:${NC}"
|
||||
echo ""
|
||||
echo " Issue #: $ISSUE_ID"
|
||||
echo " URL: $ISSUE_URL"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Next steps:${NC}"
|
||||
echo " 1. Use in commit message: ${GREEN}Resolves: #${ISSUE_ID}${NC}"
|
||||
echo " 2. Create feature branch: ${GREEN}git checkout -b feature/${ISSUE_ID}-description${NC}"
|
||||
echo ""
|
||||
102
scripts/query-forge-metadata.sh
Executable file
102
scripts/query-forge-metadata.sh
Executable file
@@ -0,0 +1,102 @@
|
||||
#!/bin/bash
|
||||
# Query TYPO3 Forge project metadata via Redmine REST API
|
||||
#
|
||||
# Usage:
|
||||
# 1. Get your API key from https://forge.typo3.org/my/account
|
||||
# 2. Set environment variable: export FORGE_API_KEY="your-key-here"
|
||||
# 3. Run: ./scripts/query-forge-metadata.sh [categories|trackers|all]
|
||||
|
||||
set -e
|
||||
|
||||
# Color output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Check for API key
|
||||
if [ -z "$FORGE_API_KEY" ]; then
|
||||
echo -e "${RED}Error: FORGE_API_KEY environment variable not set${NC}"
|
||||
echo ""
|
||||
echo "Get your API key from: https://forge.typo3.org/my/account"
|
||||
echo "Then set it: export FORGE_API_KEY=\"your-key-here\""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for required tools
|
||||
for tool in curl jq; do
|
||||
if ! command -v $tool &> /dev/null; then
|
||||
echo -e "${RED}Error: $tool is required but not installed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Parse arguments
|
||||
QUERY_TYPE=${1:-all}
|
||||
|
||||
# Fetch project metadata
|
||||
echo -e "${YELLOW}Fetching TYPO3 Core project metadata...${NC}"
|
||||
echo ""
|
||||
|
||||
RESPONSE=$(curl -s \
|
||||
-H "X-Redmine-API-Key: $FORGE_API_KEY" \
|
||||
https://forge.typo3.org/projects/typo3cms-core.json)
|
||||
|
||||
# Check for errors
|
||||
if echo "$RESPONSE" | jq -e '.errors' > /dev/null 2>&1; then
|
||||
echo -e "${RED}Error querying Forge:${NC}"
|
||||
echo "$RESPONSE" | jq -r '.errors[]'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Display trackers
|
||||
if [[ "$QUERY_TYPE" == "trackers" || "$QUERY_TYPE" == "all" ]]; then
|
||||
echo -e "${GREEN}=== Trackers ===${NC}"
|
||||
echo ""
|
||||
echo "$RESPONSE" | jq -r '.project.trackers[] | "\(.id)\t\(.name)"' | \
|
||||
awk -F'\t' '{printf " %-4s %s\n", $1, $2}'
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Display categories
|
||||
if [[ "$QUERY_TYPE" == "categories" || "$QUERY_TYPE" == "all" ]]; then
|
||||
echo -e "${GREEN}=== Issue Categories ===${NC}"
|
||||
echo ""
|
||||
echo "$RESPONSE" | jq -r '.project.issue_categories[] | "\(.id)\t\(.name)"' | \
|
||||
awk -F'\t' '{printf " %-6s %s\n", $1, $2}'
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Display usage examples
|
||||
if [[ "$QUERY_TYPE" == "all" ]]; then
|
||||
echo -e "${BLUE}=== Usage Examples ===${NC}"
|
||||
echo ""
|
||||
echo "Create bug in Backend API category:"
|
||||
echo ' curl -X POST \'
|
||||
echo ' -H "Content-Type: application/json" \'
|
||||
echo ' -H "X-Redmine-API-Key: $FORGE_API_KEY" \'
|
||||
echo ' -d '"'"'{'
|
||||
echo ' "issue": {'
|
||||
echo ' "project_id": "typo3cms-core",'
|
||||
echo ' "subject": "Issue title",'
|
||||
echo ' "description": "Description",'
|
||||
echo ' "tracker_id": 1,'
|
||||
echo ' "category_id": 971,'
|
||||
echo ' "priority_id": 4,'
|
||||
echo ' "custom_fields": [{"id": 4, "value": "13"}]'
|
||||
echo ' }'
|
||||
echo ' }'"'"' \'
|
||||
echo ' https://forge.typo3.org/issues.json'
|
||||
echo ""
|
||||
echo "Or use the interactive script:"
|
||||
echo " ./scripts/create-forge-issue.sh"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Save to file if requested
|
||||
if [[ "$2" == "--save" ]]; then
|
||||
OUTPUT_FILE="forge-metadata-$(date +%Y%m%d).json"
|
||||
echo "$RESPONSE" > "$OUTPUT_FILE"
|
||||
echo -e "${GREEN}Metadata saved to: $OUTPUT_FILE${NC}"
|
||||
fi
|
||||
433
scripts/setup-typo3-coredev.sh
Executable file
433
scripts/setup-typo3-coredev.sh
Executable file
@@ -0,0 +1,433 @@
|
||||
#!/bin/bash
|
||||
# TYPO3 Core Development Environment Setup Script
|
||||
# Based on proven production workflow
|
||||
# Creates complete DDEV-based TYPO3 Core development environment
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Print functions
|
||||
print_header() {
|
||||
echo -e "\n${BLUE}===================================================${NC}"
|
||||
echo -e "${BLUE}$1${NC}"
|
||||
echo -e "${BLUE}===================================================${NC}\n"
|
||||
}
|
||||
|
||||
print_step() {
|
||||
echo -e "${GREEN}➜${NC} $1"
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e "${YELLOW}ℹ${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}✗${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}✓${NC} $1"
|
||||
}
|
||||
|
||||
# Check prerequisites
|
||||
check_prerequisites() {
|
||||
print_header "Checking Prerequisites"
|
||||
|
||||
local missing_prereqs=false
|
||||
|
||||
# Check Git
|
||||
if command -v git &> /dev/null; then
|
||||
print_success "Git: $(git --version)"
|
||||
else
|
||||
print_error "Git not found. Please install Git first."
|
||||
missing_prereqs=true
|
||||
fi
|
||||
|
||||
# Check DDEV
|
||||
if command -v ddev &> /dev/null; then
|
||||
print_success "DDEV: $(ddev version | head -n1)"
|
||||
else
|
||||
print_error "DDEV not found. Please install DDEV first: https://ddev.readthedocs.io/"
|
||||
missing_prereqs=true
|
||||
fi
|
||||
|
||||
# Check Docker
|
||||
if command -v docker &> /dev/null; then
|
||||
if docker ps &> /dev/null; then
|
||||
print_success "Docker: Running"
|
||||
else
|
||||
print_error "Docker not running. Please start Docker."
|
||||
missing_prereqs=true
|
||||
fi
|
||||
else
|
||||
print_error "Docker not found. DDEV requires Docker."
|
||||
missing_prereqs=true
|
||||
fi
|
||||
|
||||
if [ "$missing_prereqs" = true ]; then
|
||||
print_error "Missing prerequisites. Please install required tools and try again."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Gather user input
|
||||
gather_input() {
|
||||
print_header "Configuration"
|
||||
|
||||
# Project name
|
||||
read -p "Project name (e.g., t3coredev-14-php8-4): " PROJECT_NAME
|
||||
if [ -z "$PROJECT_NAME" ]; then
|
||||
PROJECT_NAME="t3coredev-14-php8-4"
|
||||
print_info "Using default: $PROJECT_NAME"
|
||||
fi
|
||||
|
||||
# Git user name
|
||||
read -p "Your name for Git commits: " GIT_NAME
|
||||
while [ -z "$GIT_NAME" ]; do
|
||||
print_error "Name is required"
|
||||
read -p "Your name for Git commits: " GIT_NAME
|
||||
done
|
||||
|
||||
# Git email
|
||||
read -p "Your email for Git commits: " GIT_EMAIL
|
||||
while [ -z "$GIT_EMAIL" ]; do
|
||||
print_error "Email is required"
|
||||
read -p "Your email for Git commits: " GIT_EMAIL
|
||||
done
|
||||
|
||||
# Gerrit username
|
||||
read -p "Your Gerrit username (review.typo3.org): " GERRIT_USER
|
||||
while [ -z "$GERRIT_USER" ]; do
|
||||
print_error "Gerrit username is required"
|
||||
read -p "Your Gerrit username: " GERRIT_USER
|
||||
done
|
||||
|
||||
# PHP version
|
||||
read -p "PHP version (8.2, 8.3, 8.4) [default: 8.4]: " PHP_VERSION
|
||||
if [ -z "$PHP_VERSION" ]; then
|
||||
PHP_VERSION="8.4"
|
||||
fi
|
||||
|
||||
# Timezone
|
||||
read -p "Timezone [default: Europe/Vienna]: " TIMEZONE
|
||||
if [ -z "$TIMEZONE" ]; then
|
||||
TIMEZONE="Europe/Vienna"
|
||||
fi
|
||||
|
||||
# Admin password
|
||||
read -sp "TYPO3 admin password: " ADMIN_PASSWORD
|
||||
echo
|
||||
while [ -z "$ADMIN_PASSWORD" ]; do
|
||||
print_error "Admin password is required"
|
||||
read -sp "TYPO3 admin password: " ADMIN_PASSWORD
|
||||
echo
|
||||
done
|
||||
|
||||
# Confirm
|
||||
echo -e "\n${YELLOW}Configuration Summary:${NC}"
|
||||
echo " Project: $PROJECT_NAME"
|
||||
echo " Git Name: $GIT_NAME"
|
||||
echo " Git Email: $GIT_EMAIL"
|
||||
echo " Gerrit User: $GERRIT_USER"
|
||||
echo " PHP Version: $PHP_VERSION"
|
||||
echo " Timezone: $TIMEZONE"
|
||||
echo
|
||||
|
||||
read -p "Proceed with setup? (y/n): " CONFIRM
|
||||
if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then
|
||||
print_info "Setup cancelled."
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
# Create project directory
|
||||
create_project_dir() {
|
||||
print_header "Creating Project Directory"
|
||||
|
||||
if [ -d "$PROJECT_NAME" ]; then
|
||||
print_error "Directory $PROJECT_NAME already exists!"
|
||||
read -p "Delete and recreate? (y/n): " DELETE_CONFIRM
|
||||
if [[ "$DELETE_CONFIRM" =~ ^[Yy]$ ]]; then
|
||||
rm -rf "$PROJECT_NAME"
|
||||
print_success "Deleted existing directory"
|
||||
else
|
||||
print_error "Cannot proceed with existing directory"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir -p "$PROJECT_NAME"
|
||||
cd "$PROJECT_NAME"
|
||||
print_success "Created and entered directory: $PROJECT_NAME"
|
||||
}
|
||||
|
||||
# Clone TYPO3 repository
|
||||
clone_repository() {
|
||||
print_header "Cloning TYPO3 Repository"
|
||||
|
||||
print_step "Cloning from GitHub..."
|
||||
if git clone git@github.com:typo3/typo3 . 2>&1 | grep -q "Permission denied\|Could not"; then
|
||||
print_error "Failed to clone via SSH. Trying HTTPS..."
|
||||
rm -rf .git
|
||||
if ! git clone https://github.com/typo3/typo3.git . ; then
|
||||
print_error "Failed to clone repository"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
print_success "Repository cloned successfully"
|
||||
}
|
||||
|
||||
# Configure Git
|
||||
configure_git() {
|
||||
print_header "Configuring Git"
|
||||
|
||||
print_step "Setting user identity..."
|
||||
git config user.name "$GIT_NAME"
|
||||
git config user.email "$GIT_EMAIL"
|
||||
print_success "User identity configured"
|
||||
|
||||
print_step "Enabling automatic rebase..."
|
||||
git config branch.autosetuprebase remote
|
||||
print_success "Automatic rebase enabled"
|
||||
|
||||
print_step "Installing git hooks..."
|
||||
if [ -f "Build/git-hooks/commit-msg" ]; then
|
||||
cp Build/git-hooks/commit-msg .git/hooks/commit-msg
|
||||
chmod +x .git/hooks/commit-msg
|
||||
print_success "Commit-msg hook installed"
|
||||
else
|
||||
print_error "Commit-msg hook not found in Build/git-hooks/"
|
||||
fi
|
||||
|
||||
print_step "Configuring Gerrit remote..."
|
||||
git config remote.origin.pushurl "ssh://${GERRIT_USER}@review.typo3.org:29418/Packages/TYPO3.CMS.git"
|
||||
git config remote.origin.push "+refs/heads/main:refs/for/main"
|
||||
print_success "Gerrit remote configured"
|
||||
|
||||
# Test Gerrit connection
|
||||
print_step "Testing Gerrit SSH connection..."
|
||||
if timeout 5 ssh -p 29418 -o StrictHostKeyChecking=no -o BatchMode=yes "${GERRIT_USER}@review.typo3.org" gerrit version &>/dev/null; then
|
||||
print_success "Gerrit connection successful"
|
||||
else
|
||||
print_error "Cannot connect to Gerrit. Please verify your SSH keys are configured."
|
||||
print_info "Continue anyway? SSH key might need configuration."
|
||||
read -p "Continue? (y/n): " CONTINUE
|
||||
if [[ ! "$CONTINUE" =~ ^[Yy]$ ]]; then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Configure DDEV
|
||||
configure_ddev() {
|
||||
print_header "Configuring DDEV"
|
||||
|
||||
print_step "Setting project type..."
|
||||
ddev config --project-type typo3 -y
|
||||
|
||||
print_step "Configuring timezone..."
|
||||
ddev config --timezone "$TIMEZONE"
|
||||
|
||||
print_step "Setting PHP version..."
|
||||
ddev config --php-version="$PHP_VERSION"
|
||||
|
||||
print_step "Configuring webserver..."
|
||||
ddev config --webserver-type=apache-fpm
|
||||
|
||||
print_step "Setting database version..."
|
||||
ddev config --database=mariadb:10.6
|
||||
|
||||
print_step "Adding environment variables..."
|
||||
ddev config --web-environment-add="TYPO3_CONTEXT=Development/Ddev"
|
||||
ddev config --web-environment-add="COMPOSER_ROOT_VERSION=14.0.x-dev"
|
||||
|
||||
print_success "DDEV configured successfully"
|
||||
}
|
||||
|
||||
# Start DDEV
|
||||
start_ddev() {
|
||||
print_header "Starting DDEV"
|
||||
|
||||
print_step "Starting containers..."
|
||||
if ddev start; then
|
||||
print_success "DDEV started successfully"
|
||||
ddev describe
|
||||
else
|
||||
print_error "Failed to start DDEV"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Install dependencies
|
||||
install_dependencies() {
|
||||
print_header "Installing Dependencies"
|
||||
|
||||
print_step "Running Composer install via runTests.sh..."
|
||||
if ./Build/Scripts/runTests.sh -s composerInstall; then
|
||||
print_success "Dependencies installed"
|
||||
else
|
||||
print_error "Failed to install dependencies"
|
||||
print_info "Trying alternative method..."
|
||||
if ddev composer install; then
|
||||
print_success "Dependencies installed via ddev composer"
|
||||
else
|
||||
print_error "Failed to install dependencies"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Setup TYPO3
|
||||
setup_typo3() {
|
||||
print_header "Setting Up TYPO3"
|
||||
|
||||
print_step "Creating installation trigger files..."
|
||||
ddev exec 'touch /var/www/html/FIRST_INSTALL'
|
||||
ddev exec 'touch /var/www/html/typo3conf/ENABLE_INSTALL_TOOL'
|
||||
ddev exec 'echo "KEEP_FILE" > /var/www/html/typo3conf/ENABLE_INSTALL_TOOL'
|
||||
print_success "Trigger files created"
|
||||
|
||||
print_step "Running TYPO3 setup..."
|
||||
if ddev typo3 setup \
|
||||
--driver=mysqli \
|
||||
--host=db \
|
||||
--port=3306 \
|
||||
--dbname=db \
|
||||
--username=db \
|
||||
--password=db \
|
||||
--admin-username=backenduser \
|
||||
--admin-user-password="$ADMIN_PASSWORD" \
|
||||
--admin-email="$GIT_EMAIL" \
|
||||
--project-name="TYPO3 Core Dev v14 PHP ${PHP_VERSION}" \
|
||||
--no-interaction \
|
||||
--server-type=apache \
|
||||
--force; then
|
||||
print_success "TYPO3 setup completed"
|
||||
else
|
||||
print_error "TYPO3 setup failed"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Activate extensions
|
||||
activate_extensions() {
|
||||
print_header "Activating Extensions"
|
||||
|
||||
print_step "Setting up extensions..."
|
||||
ddev typo3 extension:setup
|
||||
|
||||
print_step "Activating indexed_search..."
|
||||
ddev typo3 extension:activate indexed_search
|
||||
|
||||
print_step "Activating styleguide..."
|
||||
ddev typo3 extension:activate styleguide
|
||||
|
||||
print_step "Activating scheduler..."
|
||||
ddev typo3 extension:activate scheduler
|
||||
|
||||
print_success "Extensions activated"
|
||||
}
|
||||
|
||||
# Setup backend groups
|
||||
setup_backend_groups() {
|
||||
print_header "Setting Up Backend User Groups"
|
||||
|
||||
print_step "Creating default backend groups..."
|
||||
if ddev typo3 setup:begroups:default --groups=Both; then
|
||||
print_success "Backend groups configured"
|
||||
else
|
||||
print_error "Failed to setup backend groups"
|
||||
fi
|
||||
}
|
||||
|
||||
# Generate test data
|
||||
generate_test_data() {
|
||||
print_header "Generating Test Data"
|
||||
|
||||
read -p "Generate styleguide test data? (y/n): " GENERATE_DATA
|
||||
if [[ "$GENERATE_DATA" =~ ^[Yy]$ ]]; then
|
||||
print_step "Generating TCA examples..."
|
||||
ddev typo3 styleguide:generate --create -- tca
|
||||
|
||||
print_step "Generating frontend system template..."
|
||||
ddev typo3 styleguide:generate --create -- frontend-systemplate
|
||||
|
||||
print_success "Test data generated"
|
||||
else
|
||||
print_info "Skipping test data generation"
|
||||
fi
|
||||
}
|
||||
|
||||
# Final steps
|
||||
finalize() {
|
||||
print_header "Setup Complete!"
|
||||
|
||||
print_success "TYPO3 Core development environment is ready!"
|
||||
echo
|
||||
echo -e "${GREEN}Project Details:${NC}"
|
||||
echo " Name: $PROJECT_NAME"
|
||||
echo " URL: https://${PROJECT_NAME}.ddev.site"
|
||||
echo " Backend: https://${PROJECT_NAME}.ddev.site/typo3"
|
||||
echo " Admin User: backenduser"
|
||||
echo " Admin Password: [the password you entered]"
|
||||
echo
|
||||
echo -e "${GREEN}Next Steps:${NC}"
|
||||
echo " 1. Open backend: ddev launch /typo3"
|
||||
echo " 2. Run tests: ./Build/Scripts/runTests.sh -s unit"
|
||||
echo " 3. Create branch: git checkout -b feature/your-feature"
|
||||
echo " 4. Make changes and commit with proper message"
|
||||
echo " 5. Push to Gerrit: git push origin HEAD:refs/for/main"
|
||||
echo
|
||||
echo -e "${GREEN}Useful Commands:${NC}"
|
||||
echo " ddev start - Start project"
|
||||
echo " ddev stop - Stop project"
|
||||
echo " ddev restart - Restart project"
|
||||
echo " ddev ssh - SSH into container"
|
||||
echo " ddev typo3 cache:flush - Clear TYPO3 caches"
|
||||
echo " ddev logs -f - Follow logs"
|
||||
echo
|
||||
|
||||
read -p "Open TYPO3 backend now? (y/n): " OPEN_BACKEND
|
||||
if [[ "$OPEN_BACKEND" =~ ^[Yy]$ ]]; then
|
||||
ddev launch /typo3
|
||||
fi
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
clear
|
||||
print_header "TYPO3 Core Development Setup"
|
||||
|
||||
echo "This script will set up a complete TYPO3 Core development environment."
|
||||
echo "It will:"
|
||||
echo " - Clone TYPO3 Core repository"
|
||||
echo " - Configure Git for Gerrit submissions"
|
||||
echo " - Set up DDEV with optimal settings"
|
||||
echo " - Install TYPO3 with test data"
|
||||
echo " - Activate development extensions"
|
||||
echo
|
||||
|
||||
check_prerequisites
|
||||
gather_input
|
||||
create_project_dir
|
||||
clone_repository
|
||||
configure_git
|
||||
configure_ddev
|
||||
start_ddev
|
||||
install_dependencies
|
||||
setup_typo3
|
||||
activate_extensions
|
||||
setup_backend_groups
|
||||
generate_test_data
|
||||
finalize
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main
|
||||
256
scripts/validate-commit-message.py
Executable file
256
scripts/validate-commit-message.py
Executable file
@@ -0,0 +1,256 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
TYPO3 Commit Message Validator
|
||||
Validates commit messages against TYPO3 contribution standards
|
||||
"""
|
||||
|
||||
import sys
|
||||
import re
|
||||
import argparse
|
||||
from typing import List, Tuple
|
||||
|
||||
|
||||
VALID_TYPES = ['BUGFIX', 'FEATURE', 'TASK', 'DOCS', 'SECURITY']
|
||||
BREAKING_PREFIX = '[!!!]'
|
||||
|
||||
|
||||
class CommitMessageValidator:
|
||||
def __init__(self, message: str):
|
||||
self.message = message
|
||||
self.lines = message.split('\n')
|
||||
self.errors = []
|
||||
self.warnings = []
|
||||
|
||||
def validate(self) -> Tuple[bool, List[str], List[str]]:
|
||||
"""Run all validation checks"""
|
||||
self.check_subject_line()
|
||||
self.check_blank_line()
|
||||
self.check_footer()
|
||||
self.check_change_id()
|
||||
|
||||
return len(self.errors) == 0, self.errors, self.warnings
|
||||
|
||||
def check_subject_line(self):
|
||||
"""Validate the subject line"""
|
||||
if not self.lines:
|
||||
self.errors.append("Commit message is empty")
|
||||
return
|
||||
|
||||
subject = self.lines[0]
|
||||
|
||||
# Check for commit type
|
||||
type_pattern = r'^\[(?:\[!!!\])?(BUGFIX|FEATURE|TASK|DOCS|SECURITY)\]'
|
||||
match = re.match(type_pattern, subject)
|
||||
|
||||
if not match:
|
||||
self.errors.append(
|
||||
f"Subject must start with commit type: {', '.join(f'[{t}]' for t in VALID_TYPES)}"
|
||||
)
|
||||
return
|
||||
|
||||
commit_type = match.group(1)
|
||||
|
||||
# Check for breaking change prefix
|
||||
if subject.startswith('[!!!]'):
|
||||
if commit_type == 'BUGFIX':
|
||||
self.warnings.append(
|
||||
"Breaking changes are unusual for BUGFIX. Consider using FEATURE or TASK"
|
||||
)
|
||||
|
||||
# Extract subject without type prefix
|
||||
subject_without_type = re.sub(type_pattern, '', subject).strip()
|
||||
|
||||
# Check length
|
||||
if len(subject) > 72:
|
||||
self.errors.append(
|
||||
f"Subject line is {len(subject)} characters (max 72). Current: {len(subject)}"
|
||||
)
|
||||
elif len(subject) > 52:
|
||||
self.warnings.append(
|
||||
f"Subject line is {len(subject)} characters (recommended max 52)"
|
||||
)
|
||||
|
||||
# Check capitalization
|
||||
if subject_without_type and not subject_without_type[0].isupper():
|
||||
self.errors.append("Subject description must start with uppercase letter")
|
||||
|
||||
# Check for period at end
|
||||
if subject.endswith('.'):
|
||||
self.errors.append("Subject line should not end with a period")
|
||||
|
||||
# Check for imperative mood (heuristic)
|
||||
if subject_without_type:
|
||||
first_word = subject_without_type.split()[0].lower()
|
||||
if first_word.endswith('ed') or first_word.endswith('ing'):
|
||||
self.warnings.append(
|
||||
f"Use imperative mood: '{first_word}' may not be imperative. "
|
||||
"Use 'Fix' not 'Fixed' or 'Fixing'"
|
||||
)
|
||||
|
||||
def check_blank_line(self):
|
||||
"""Check for blank line after subject"""
|
||||
if len(self.lines) < 2:
|
||||
return # Only subject line, no body
|
||||
|
||||
if len(self.lines) >= 2 and self.lines[1] != '':
|
||||
self.errors.append("Second line must be blank (separate subject from body)")
|
||||
|
||||
def check_footer(self):
|
||||
"""Check footer tags"""
|
||||
footer_pattern = r'^(Resolves|Related|Releases|Depends|Reverts):\s*'
|
||||
|
||||
has_resolves = False
|
||||
has_releases = False
|
||||
has_change_id = False
|
||||
|
||||
for i, line in enumerate(self.lines):
|
||||
if re.match(footer_pattern, line):
|
||||
# Check format: should have colon followed by space
|
||||
if not re.match(r'^[A-Z][a-z]+:\s+', line):
|
||||
self.errors.append(
|
||||
f"Line {i+1}: Footer tag must have colon followed by space: '{line}'"
|
||||
)
|
||||
|
||||
# Check specific tags
|
||||
if line.startswith('Resolves:'):
|
||||
has_resolves = True
|
||||
# Validate issue number format
|
||||
if not re.match(r'^Resolves:\s+#\d+', line):
|
||||
self.errors.append(
|
||||
f"Line {i+1}: Resolves must reference issue number: 'Resolves: #12345'"
|
||||
)
|
||||
|
||||
elif line.startswith('Related:'):
|
||||
if not re.match(r'^Related:\s+#\d+', line):
|
||||
self.errors.append(
|
||||
f"Line {i+1}: Related must reference issue number: 'Related: #12345'"
|
||||
)
|
||||
|
||||
elif line.startswith('Releases:'):
|
||||
has_releases = True
|
||||
# Validate releases format
|
||||
releases_value = line.split(':', 1)[1].strip()
|
||||
releases = [r.strip() for r in releases_value.split(',')]
|
||||
for release in releases:
|
||||
if release != 'main' and not re.match(r'^\d+\.\d+$', release):
|
||||
self.errors.append(
|
||||
f"Line {i+1}: Invalid release format '{release}'. "
|
||||
"Use 'main' or version like '13.4'"
|
||||
)
|
||||
|
||||
elif line.startswith('Change-Id:'):
|
||||
has_change_id = True
|
||||
|
||||
# Warnings for missing tags
|
||||
if not has_resolves:
|
||||
self.warnings.append(
|
||||
"No 'Resolves: #<issue>' tag found. Required for features and tasks."
|
||||
)
|
||||
|
||||
if not has_releases:
|
||||
self.warnings.append(
|
||||
"No 'Releases:' tag found. Required to specify target versions."
|
||||
)
|
||||
|
||||
def check_change_id(self):
|
||||
"""Check for Change-Id"""
|
||||
change_id_pattern = r'^Change-Id:\s+I[a-f0-9]{40}$'
|
||||
has_change_id = any(re.match(change_id_pattern, line) for line in self.lines)
|
||||
|
||||
if not has_change_id:
|
||||
self.warnings.append(
|
||||
"No Change-Id found. It will be added automatically by git commit-msg hook."
|
||||
)
|
||||
|
||||
def check_line_length(self):
|
||||
"""Check body line lengths"""
|
||||
for i, line in enumerate(self.lines[2:], start=3): # Skip subject and blank line
|
||||
if line.startswith(('Resolves:', 'Related:', 'Releases:', 'Change-Id:', 'Depends:', 'Reverts:')):
|
||||
continue # Skip footer
|
||||
|
||||
if len(line) > 72:
|
||||
# Allow URLs to be longer
|
||||
if not re.search(r'https?://', line):
|
||||
self.warnings.append(
|
||||
f"Line {i}: Length {len(line)} exceeds 72 characters"
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Validate TYPO3 commit messages',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter
|
||||
)
|
||||
|
||||
parser.add_argument('--file', '-f', help='File containing commit message')
|
||||
parser.add_argument('--message', '-m', help='Commit message string')
|
||||
parser.add_argument('--strict', action='store_true',
|
||||
help='Treat warnings as errors')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Get message
|
||||
if args.file:
|
||||
try:
|
||||
with open(args.file, 'r') as f:
|
||||
message = f.read()
|
||||
except FileNotFoundError:
|
||||
print(f"Error: File not found: {args.file}")
|
||||
return 1
|
||||
elif args.message:
|
||||
message = args.message
|
||||
else:
|
||||
# Read from last commit
|
||||
try:
|
||||
import subprocess
|
||||
result = subprocess.run(
|
||||
['git', 'log', '-1', '--pretty=%B'],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True
|
||||
)
|
||||
message = result.stdout
|
||||
except subprocess.CalledProcessError:
|
||||
print("Error: Could not read last commit message")
|
||||
print("Usage: Provide --file or --message, or run in a git repository")
|
||||
return 1
|
||||
|
||||
# Validate
|
||||
validator = CommitMessageValidator(message)
|
||||
is_valid, errors, warnings = validator.validate()
|
||||
|
||||
# Print results
|
||||
print("=" * 60)
|
||||
print("TYPO3 Commit Message Validation")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
if errors:
|
||||
print("❌ ERRORS:")
|
||||
for error in errors:
|
||||
print(f" • {error}")
|
||||
print()
|
||||
|
||||
if warnings:
|
||||
print("⚠️ WARNINGS:")
|
||||
for warning in warnings:
|
||||
print(f" • {warning}")
|
||||
print()
|
||||
|
||||
if not errors and not warnings:
|
||||
print("✅ Commit message is valid!")
|
||||
elif not errors:
|
||||
print("✅ No errors found (warnings can be ignored)")
|
||||
else:
|
||||
print("❌ Validation failed. Please fix errors above.")
|
||||
|
||||
print("=" * 60)
|
||||
|
||||
# Exit code
|
||||
if errors or (args.strict and warnings):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
181
scripts/verify-prerequisites.sh
Executable file
181
scripts/verify-prerequisites.sh
Executable file
@@ -0,0 +1,181 @@
|
||||
#!/bin/bash
|
||||
# TYPO3 Core Contribution Prerequisites Checker
|
||||
# Verifies accounts, git configuration, and development environment setup
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo "================================================"
|
||||
echo "TYPO3 Core Contribution Prerequisites Check"
|
||||
echo "================================================"
|
||||
echo
|
||||
|
||||
# Track overall status
|
||||
ALL_CHECKS_PASSED=true
|
||||
|
||||
# Function to print status
|
||||
print_status() {
|
||||
if [ "$1" = "pass" ]; then
|
||||
echo -e "${GREEN}✓${NC} $2"
|
||||
elif [ "$1" = "fail" ]; then
|
||||
echo -e "${RED}✗${NC} $2"
|
||||
ALL_CHECKS_PASSED=false
|
||||
elif [ "$1" = "warn" ]; then
|
||||
echo -e "${YELLOW}⚠${NC} $2"
|
||||
fi
|
||||
}
|
||||
|
||||
# 1. Check Git installation
|
||||
echo "1. Checking Git installation..."
|
||||
if command -v git &> /dev/null; then
|
||||
GIT_VERSION=$(git --version)
|
||||
print_status "pass" "Git installed: $GIT_VERSION"
|
||||
else
|
||||
print_status "fail" "Git not installed"
|
||||
fi
|
||||
echo
|
||||
|
||||
# 2. Check Git user configuration
|
||||
echo "2. Checking Git user configuration..."
|
||||
GIT_USER_NAME=$(git config --global user.name 2>/dev/null || echo "")
|
||||
GIT_USER_EMAIL=$(git config --global user.email 2>/dev/null || echo "")
|
||||
|
||||
if [ -n "$GIT_USER_NAME" ] && [ -n "$GIT_USER_EMAIL" ]; then
|
||||
print_status "pass" "Git user configured: $GIT_USER_NAME <$GIT_USER_EMAIL>"
|
||||
else
|
||||
print_status "fail" "Git user not configured. Run:"
|
||||
echo " git config --global user.name \"Your Name\""
|
||||
echo " git config --global user.email \"your-email@example.org\""
|
||||
fi
|
||||
echo
|
||||
|
||||
# 2a. Verify Git email matches Gerrit account
|
||||
echo "2a. Verifying Git email against Gerrit..."
|
||||
if [ -n "$GIT_USER_EMAIL" ]; then
|
||||
echo " Your Git email: $GIT_USER_EMAIL"
|
||||
print_status "warn" " IMPORTANT: Verify this email is registered at:"
|
||||
echo " https://review.typo3.org/settings#EmailAddresses"
|
||||
echo " Gerrit will reject pushes if email doesn't match!"
|
||||
fi
|
||||
echo
|
||||
|
||||
# 3. Check if in TYPO3 repository
|
||||
echo "3. Checking TYPO3 repository..."
|
||||
if [ -d ".git" ]; then
|
||||
REPO_URL=$(git config --get remote.origin.url 2>/dev/null || echo "")
|
||||
if [[ "$REPO_URL" == *"typo3"* ]]; then
|
||||
print_status "pass" "In TYPO3 repository"
|
||||
|
||||
# Check TYPO3-specific git config
|
||||
echo " Checking TYPO3-specific configuration..."
|
||||
|
||||
# Check auto-rebase
|
||||
AUTO_REBASE=$(git config --get branch.autosetuprebase 2>/dev/null || echo "")
|
||||
if [ "$AUTO_REBASE" = "remote" ]; then
|
||||
print_status "pass" " Auto-rebase configured"
|
||||
else
|
||||
print_status "fail" " Auto-rebase not configured. Run: git config branch.autosetuprebase remote"
|
||||
fi
|
||||
|
||||
# Check Gerrit push URL
|
||||
PUSH_URL=$(git config --get remote.origin.pushurl 2>/dev/null || echo "")
|
||||
if [[ "$PUSH_URL" == *"review.typo3.org"* ]]; then
|
||||
print_status "pass" " Gerrit push URL configured"
|
||||
else
|
||||
print_status "fail" " Gerrit push URL not configured. Run:"
|
||||
echo " git config remote.origin.pushurl ssh://<USERNAME>@review.typo3.org:29418/Packages/TYPO3.CMS.git"
|
||||
fi
|
||||
|
||||
# Check push refspec
|
||||
PUSH_REFSPEC=$(git config --get remote.origin.push 2>/dev/null || echo "")
|
||||
if [[ "$PUSH_REFSPEC" == *"refs/for/main"* ]]; then
|
||||
print_status "pass" " Push refspec configured for Gerrit"
|
||||
else
|
||||
print_status "fail" " Push refspec not configured. Run:"
|
||||
echo " git config remote.origin.push +refs/heads/main:refs/for/main"
|
||||
fi
|
||||
|
||||
else
|
||||
print_status "warn" "In git repository but not TYPO3. URL: $REPO_URL"
|
||||
fi
|
||||
else
|
||||
print_status "warn" "Not in a git repository (run from TYPO3 repo root)"
|
||||
fi
|
||||
echo
|
||||
|
||||
# 4. Check Git hooks
|
||||
echo "4. Checking Git hooks..."
|
||||
if [ -f ".git/hooks/commit-msg" ]; then
|
||||
print_status "pass" "commit-msg hook installed"
|
||||
else
|
||||
print_status "fail" "commit-msg hook not installed. Run: composer gerrit:setup"
|
||||
fi
|
||||
|
||||
if [ -f ".git/hooks/pre-commit" ]; then
|
||||
print_status "pass" "pre-commit hook installed"
|
||||
else
|
||||
print_status "warn" "pre-commit hook not installed (optional but recommended)"
|
||||
fi
|
||||
echo
|
||||
|
||||
# 5. Check SSH connection to Gerrit
|
||||
echo "5. Checking Gerrit SSH connection..."
|
||||
if timeout 5 ssh -p 29418 -o StrictHostKeyChecking=no -o BatchMode=yes review.typo3.org gerrit version &>/dev/null; then
|
||||
print_status "pass" "Gerrit SSH connection successful"
|
||||
else
|
||||
print_status "fail" "Cannot connect to Gerrit via SSH. Check your SSH keys and Gerrit setup"
|
||||
fi
|
||||
echo
|
||||
|
||||
# 6. Check Composer
|
||||
echo "6. Checking Composer installation..."
|
||||
if command -v composer &> /dev/null; then
|
||||
COMPOSER_VERSION=$(composer --version 2>/dev/null | head -n1)
|
||||
print_status "pass" "Composer installed: $COMPOSER_VERSION"
|
||||
else
|
||||
print_status "warn" "Composer not found (needed for running tests and gerrit:setup)"
|
||||
fi
|
||||
echo
|
||||
|
||||
# 7. Check PHP
|
||||
echo "7. Checking PHP installation..."
|
||||
if command -v php &> /dev/null; then
|
||||
PHP_VERSION=$(php -v | head -n1)
|
||||
print_status "pass" "PHP installed: $PHP_VERSION"
|
||||
else
|
||||
print_status "warn" "PHP not found (needed for development and testing)"
|
||||
fi
|
||||
echo
|
||||
|
||||
# 8. Check DDEV (optional)
|
||||
echo "8. Checking DDEV installation (optional)..."
|
||||
if command -v ddev &> /dev/null; then
|
||||
DDEV_VERSION=$(ddev version | head -n1)
|
||||
print_status "pass" "DDEV installed: $DDEV_VERSION"
|
||||
else
|
||||
print_status "warn" "DDEV not found (recommended for development environment)"
|
||||
fi
|
||||
echo
|
||||
|
||||
# Final Summary
|
||||
echo "================================================"
|
||||
if [ "$ALL_CHECKS_PASSED" = true ]; then
|
||||
echo -e "${GREEN}✓ All critical checks passed!${NC}"
|
||||
echo "You're ready to contribute to TYPO3 Core."
|
||||
else
|
||||
echo -e "${RED}✗ Some checks failed.${NC}"
|
||||
echo "Please address the issues above before contributing."
|
||||
fi
|
||||
echo "================================================"
|
||||
|
||||
# Exit with appropriate code
|
||||
if [ "$ALL_CHECKS_PASSED" = true ]; then
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user