Initial commit
This commit is contained in:
621
references/directory-structure.md
Normal file
621
references/directory-structure.md
Normal file
@@ -0,0 +1,621 @@
|
||||
# Directory Structure Standards
|
||||
|
||||
**Purpose:** Validate proper separation of committed configuration vs generated/temporary files in TYPO3 extensions
|
||||
|
||||
## Why Directory Structure Matters
|
||||
|
||||
Proper directory organization prevents common issues:
|
||||
|
||||
- ✅ **Version Control Hygiene** - Only configuration committed, not generated files
|
||||
- ✅ **Build Reproducibility** - Clean installs work without artifacts
|
||||
- ✅ **CI/CD Clarity** - Clear separation of what's tracked vs generated
|
||||
- ✅ **Onboarding Efficiency** - New contributors understand structure instantly
|
||||
- ✅ **Maintenance Simplicity** - Updates don't conflict with local artifacts
|
||||
|
||||
Without proper structure:
|
||||
- ❌ Bloated repositories with vendor/, cache files committed
|
||||
- ❌ Git conflicts in generated files
|
||||
- ❌ CI failures from missing .gitignore patterns
|
||||
- ❌ Confusion about what files are source vs generated
|
||||
|
||||
## TYPO3 Standard Directory Pattern
|
||||
|
||||
### Build/ - Committed Configuration
|
||||
|
||||
**Purpose:** Project-specific configuration files that define how to build, test, and validate code
|
||||
|
||||
**Characteristics:**
|
||||
- ✅ Committed to git
|
||||
- ✅ Shared across all developers
|
||||
- ✅ Version controlled
|
||||
- ✅ Defines project standards
|
||||
|
||||
**Standard Contents:**
|
||||
|
||||
```
|
||||
Build/
|
||||
├── phpstan.neon # Static analysis config
|
||||
├── phpstan-baseline.neon # Known issues baseline (optional)
|
||||
├── php-cs-fixer.php # Code style config
|
||||
├── rector.php # Refactoring rules (optional, can be in Build/rector/)
|
||||
├── phpunit/
|
||||
│ ├── UnitTests.xml # Unit test configuration
|
||||
│ ├── FunctionalTests.xml # Functional test configuration
|
||||
│ └── bootstrap.php # Test bootstrap (if needed)
|
||||
└── Scripts/
|
||||
└── runTests.sh # Test orchestration script
|
||||
```
|
||||
|
||||
**Alternative Rector Location:**
|
||||
```
|
||||
Build/
|
||||
└── rector/
|
||||
└── rector.php # Rector config in subdirectory
|
||||
```
|
||||
|
||||
**Git Status:** All files tracked (not in .gitignore)
|
||||
|
||||
### .Build/ - Generated/Temporary Files
|
||||
|
||||
**Purpose:** Composer-generated files, caches, runtime artifacts, test outputs
|
||||
|
||||
**Characteristics:**
|
||||
- ❌ NOT committed to git (gitignored)
|
||||
- ✅ Generated by composer/build tools
|
||||
- ✅ Recreatable from configuration
|
||||
- ✅ Developer-specific caches allowed
|
||||
|
||||
**Standard Contents:**
|
||||
|
||||
```
|
||||
.Build/
|
||||
├── bin/ # Composer bin-dir (phpunit, phpstan, etc.)
|
||||
├── public/ # Web root (TYPO3 web-dir)
|
||||
│ ├── index.php
|
||||
│ ├── typo3/
|
||||
│ └── typo3conf/
|
||||
├── vendor/ # Composer dependencies
|
||||
├── .php-cs-fixer.cache # php-cs-fixer cache file
|
||||
├── .phpunit.result.cache # PHPUnit cache
|
||||
└── var/ # Runtime cache/logs (optional)
|
||||
```
|
||||
|
||||
**Git Status:** Entire directory in .gitignore
|
||||
|
||||
**Standard .gitignore Entry:**
|
||||
|
||||
```gitignore
|
||||
# Composer generated files
|
||||
.Build/
|
||||
```
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
### 1. Build/ Directory Structure
|
||||
|
||||
**Check for committed configuration files:**
|
||||
|
||||
```bash
|
||||
ls -la Build/
|
||||
```
|
||||
|
||||
**Required files for comprehensive quality:**
|
||||
- `Build/phpstan.neon` - Static analysis configuration
|
||||
- `Build/php-cs-fixer.php` - Code style configuration
|
||||
- `Build/phpunit/UnitTests.xml` - Unit test configuration
|
||||
- `Build/Scripts/runTests.sh` - Test orchestration
|
||||
|
||||
**Optional but recommended:**
|
||||
- `Build/phpunit/FunctionalTests.xml` - Functional test configuration
|
||||
- `Build/phpstan-baseline.neon` - Known issues baseline
|
||||
- `Build/rector.php` or `Build/rector/rector.php` - Refactoring rules
|
||||
|
||||
**Severity if missing:** 🟡 **Medium** - Quality tools not standardized
|
||||
|
||||
### 2. .Build/ Directory Gitignore
|
||||
|
||||
**Check .gitignore for .Build/ exclusion:**
|
||||
|
||||
```bash
|
||||
grep -E '^\\.Build/' .gitignore
|
||||
```
|
||||
|
||||
**Expected pattern:**
|
||||
|
||||
```gitignore
|
||||
.Build/
|
||||
```
|
||||
|
||||
**Alternative patterns (also valid):**
|
||||
|
||||
```gitignore
|
||||
/.Build/
|
||||
.Build/*
|
||||
```
|
||||
|
||||
**Check for accidental commits:**
|
||||
|
||||
```bash
|
||||
git ls-files .Build/
|
||||
# Should return empty - no files from .Build/ should be tracked
|
||||
```
|
||||
|
||||
**Severity if misconfigured:** 🔴 **High** - Can lead to repository bloat
|
||||
|
||||
### 3. Composer Configuration Alignment
|
||||
|
||||
**Validate composer.json paths match directory structure:**
|
||||
|
||||
```bash
|
||||
# Extract composer paths
|
||||
cat composer.json | jq -r '.config."bin-dir"' # Should be .Build/bin
|
||||
cat composer.json | jq -r '.config."vendor-dir"' # Should be .Build/vendor
|
||||
cat composer.json | jq -r '.extra.typo3.cms."web-dir"' # Should be .Build/public
|
||||
```
|
||||
|
||||
**Expected composer.json:**
|
||||
|
||||
```json
|
||||
{
|
||||
"config": {
|
||||
"vendor-dir": ".Build/vendor",
|
||||
"bin-dir": ".Build/bin"
|
||||
},
|
||||
"extra": {
|
||||
"typo3/cms": {
|
||||
"web-dir": ".Build/public"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Severity if mismatched:** 🔴 **High** - Build will fail or create wrong structure
|
||||
|
||||
### 4. Quality Tool Configuration Paths
|
||||
|
||||
**Validate tool configs reference correct cache locations:**
|
||||
|
||||
```bash
|
||||
# Check PHPUnit cache location
|
||||
grep -r "\.phpunit\.result\.cache" Build/phpunit/*.xml
|
||||
# Should reference .Build/.phpunit.result.cache or no cache directive
|
||||
|
||||
# Check php-cs-fixer cache
|
||||
grep -r "cache-file" composer.json Build/php-cs-fixer.php
|
||||
# Should reference .Build/.php-cs-fixer.cache if specified
|
||||
|
||||
# Check PHPStan cache (usually auto-managed)
|
||||
grep -r "tmpDir" Build/phpstan.neon
|
||||
# Should reference .Build/phpstan if specified, or auto temp
|
||||
```
|
||||
|
||||
**Example CORRECT patterns:**
|
||||
|
||||
```json
|
||||
// composer.json
|
||||
{
|
||||
"scripts": {
|
||||
"ci:cgl": [
|
||||
"php-cs-fixer fix --cache-file .Build/.php-cs-fixer.cache --dry-run --diff"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```php
|
||||
// Build/php-cs-fixer.php
|
||||
return (new PhpCsFixer\Config())
|
||||
->setCacheFile('.Build/.php-cs-fixer.cache')
|
||||
->setRules([...]);
|
||||
```
|
||||
|
||||
```neon
|
||||
# Build/phpstan.neon
|
||||
parameters:
|
||||
tmpDir: .Build/phpstan
|
||||
```
|
||||
|
||||
**Severity if wrong:** 🟡 **Medium** - Works but creates clutter in Build/
|
||||
|
||||
### 5. Tea Extension Reference
|
||||
|
||||
**Source:** https://github.com/TYPO3BestPractices/tea
|
||||
|
||||
**Tea Directory Structure:**
|
||||
|
||||
```
|
||||
tea/
|
||||
├── .gitignore # Excludes .Build/
|
||||
├── Build/
|
||||
│ ├── phpstan.neon
|
||||
│ ├── phpstan-baseline.neon
|
||||
│ ├── php-cs-fixer.php
|
||||
│ ├── phpunit/
|
||||
│ │ ├── FunctionalTests.xml
|
||||
│ │ └── UnitTests.xml
|
||||
│ └── Scripts/
|
||||
│ └── runTests.sh
|
||||
├── .Build/ # Gitignored, generated by composer
|
||||
│ ├── bin/
|
||||
│ ├── public/
|
||||
│ └── vendor/
|
||||
└── composer.json # Defines .Build/ paths
|
||||
```
|
||||
|
||||
**Tea .gitignore (relevant excerpt):**
|
||||
|
||||
```gitignore
|
||||
# Composer-generated files
|
||||
.Build/
|
||||
composer.lock
|
||||
```
|
||||
|
||||
**Tea composer.json (relevant excerpt):**
|
||||
|
||||
```json
|
||||
{
|
||||
"config": {
|
||||
"vendor-dir": ".Build/vendor",
|
||||
"bin-dir": ".Build/bin"
|
||||
},
|
||||
"extra": {
|
||||
"typo3/cms": {
|
||||
"web-dir": ".Build/public"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Common Issues and Fixes
|
||||
|
||||
### Issue 1: Cache Files in Build/ Directory
|
||||
|
||||
**Diagnosis:**
|
||||
|
||||
```bash
|
||||
ls -la Build/.php-cs-fixer.cache Build/.phpunit.result.cache
|
||||
# If these files exist, they're in the WRONG location
|
||||
```
|
||||
|
||||
**Problem:** Cache files committed or in wrong directory
|
||||
|
||||
**Fix:**
|
||||
|
||||
```bash
|
||||
# Remove from wrong location
|
||||
rm Build/.php-cs-fixer.cache Build/.phpunit.result.cache
|
||||
|
||||
# Update configuration to use .Build/
|
||||
```
|
||||
|
||||
**Update php-cs-fixer config:**
|
||||
|
||||
```php
|
||||
// Build/php-cs-fixer.php
|
||||
return (new PhpCsFixer\Config())
|
||||
->setCacheFile('.Build/.php-cs-fixer.cache') // ✅ Correct location
|
||||
// ...
|
||||
```
|
||||
|
||||
**Update composer scripts:**
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"ci:cgl": [
|
||||
"php-cs-fixer fix --cache-file .Build/.php-cs-fixer.cache --dry-run --diff"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Issue 2: .Build/ Files Committed to Git
|
||||
|
||||
**Diagnosis:**
|
||||
|
||||
```bash
|
||||
git ls-files .Build/
|
||||
# Should be empty
|
||||
```
|
||||
|
||||
**Problem:** Generated files tracked in git (vendor/, bin/, etc.)
|
||||
|
||||
**Fix:**
|
||||
|
||||
```bash
|
||||
# Remove from git tracking
|
||||
git rm -r --cached .Build/
|
||||
|
||||
# Ensure .gitignore has entry
|
||||
echo ".Build/" >> .gitignore
|
||||
|
||||
# Commit the cleanup
|
||||
git add .gitignore
|
||||
git commit -m "fix: remove .Build/ from git tracking, add to .gitignore"
|
||||
```
|
||||
|
||||
### Issue 3: Missing Build/ Directory
|
||||
|
||||
**Diagnosis:**
|
||||
|
||||
```bash
|
||||
ls -la Build/
|
||||
# Directory doesn't exist
|
||||
```
|
||||
|
||||
**Problem:** No standardized quality tool configuration
|
||||
|
||||
**Fix:**
|
||||
|
||||
```bash
|
||||
# Create Build/ directory structure
|
||||
mkdir -p Build/{phpunit,Scripts}
|
||||
|
||||
# Add quality tool configs (see templates below)
|
||||
# Then commit
|
||||
git add Build/
|
||||
git commit -m "feat: add Build/ directory with quality tool configurations"
|
||||
```
|
||||
|
||||
### Issue 4: Rector in Wrong Location
|
||||
|
||||
**Diagnosis:**
|
||||
|
||||
```bash
|
||||
# Check for rector.php in project root
|
||||
ls -la rector.php
|
||||
|
||||
# Should be in Build/ or Build/rector/ instead
|
||||
```
|
||||
|
||||
**Problem:** Configuration file in project root instead of Build/
|
||||
|
||||
**Fix:**
|
||||
|
||||
```bash
|
||||
# Option 1: Move to Build/
|
||||
mv rector.php Build/rector.php
|
||||
|
||||
# Option 2: Move to Build/rector/ (preferred for complex configs)
|
||||
mkdir -p Build/rector
|
||||
mv rector.php Build/rector/rector.php
|
||||
|
||||
# Update paths in rector.php
|
||||
# Then commit
|
||||
git add Build/
|
||||
git rm rector.php
|
||||
git commit -m "refactor: move rector config to Build/ directory"
|
||||
```
|
||||
|
||||
## Conformance Report Integration
|
||||
|
||||
### When Evaluating Directory Structure:
|
||||
|
||||
**In "Best Practices" Section:**
|
||||
|
||||
```markdown
|
||||
### Directory Structure
|
||||
|
||||
**Analysis:**
|
||||
|
||||
- ✅ Build/ directory present with committed configurations
|
||||
- ✅ Build/phpstan.neon, Build/php-cs-fixer.php present
|
||||
- ✅ Build/phpunit/ directory with test configs
|
||||
- ✅ Build/Scripts/runTests.sh present
|
||||
- ✅ .Build/ properly gitignored (entire directory)
|
||||
- ✅ Composer paths correctly reference .Build/
|
||||
- ✅ Cache files located in .Build/, not Build/
|
||||
- ✅ No .Build/ files committed to git
|
||||
|
||||
**Or with issues:**
|
||||
|
||||
- ❌ Cache files in wrong location
|
||||
- Files: Build/.php-cs-fixer.cache, Build/.phpunit.result.cache
|
||||
- Expected: .Build/.php-cs-fixer.cache, .Build/.phpunit.result.cache
|
||||
- Severity: Medium
|
||||
- Fix: Move cache files to .Build/ and update configs
|
||||
|
||||
- ❌ .Build/ files committed to git
|
||||
- Files: .Build/vendor/, .Build/bin/
|
||||
- Command: `git ls-files .Build/` shows tracked files
|
||||
- Severity: High
|
||||
- Fix: `git rm -r --cached .Build/` and ensure .gitignore has `.Build/`
|
||||
|
||||
- ⚠️ Missing Build/ directory
|
||||
- Impact: No standardized quality tool configuration
|
||||
- Severity: Medium
|
||||
- Recommendation: Create Build/ with phpstan.neon, php-cs-fixer.php, phpunit configs
|
||||
```
|
||||
|
||||
## Scoring Impact
|
||||
|
||||
**Best Practices Score Deductions:**
|
||||
|
||||
| Issue | Severity | Score Impact |
|
||||
|-------|----------|--------------|
|
||||
| .Build/ files committed | High | -4 points |
|
||||
| Cache files in Build/ | Medium | -2 points |
|
||||
| Missing .gitignore for .Build/ | High | -3 points |
|
||||
| Composer paths don't match structure | High | -3 points |
|
||||
| Missing Build/ directory | Medium | -2 points |
|
||||
| Rector/configs in project root | Low | -1 point |
|
||||
|
||||
**Maximum deduction for directory issues:** -6 points (out of 20 for Best Practices)
|
||||
|
||||
## Automated Validation Script
|
||||
|
||||
Create `scripts/validate-directory-structure.sh`:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
echo "🔍 Validating directory structure..."
|
||||
|
||||
ISSUES=0
|
||||
|
||||
# Check 1: .Build/ should be gitignored
|
||||
if ! grep -qE '^\\.Build/' .gitignore; then
|
||||
echo "❌ .gitignore missing '.Build/' entry"
|
||||
ISSUES=$((ISSUES + 1))
|
||||
else
|
||||
echo "✅ .Build/ properly gitignored"
|
||||
fi
|
||||
|
||||
# Check 2: No .Build/ files should be tracked
|
||||
TRACKED_BUILD=$(git ls-files .Build/ 2>/dev/null | wc -l)
|
||||
if [ "${TRACKED_BUILD}" -gt 0 ]; then
|
||||
echo "❌ .Build/ files are committed to git:"
|
||||
git ls-files .Build/
|
||||
ISSUES=$((ISSUES + 1))
|
||||
else
|
||||
echo "✅ No .Build/ files tracked in git"
|
||||
fi
|
||||
|
||||
# Check 3: Build/ directory should exist
|
||||
if [ ! -d "Build" ]; then
|
||||
echo "⚠️ Build/ directory missing"
|
||||
ISSUES=$((ISSUES + 1))
|
||||
else
|
||||
echo "✅ Build/ directory exists"
|
||||
|
||||
# Check for standard files
|
||||
[ -f "Build/phpstan.neon" ] && echo " ✅ phpstan.neon" || echo " ⚠️ phpstan.neon missing"
|
||||
[ -f "Build/php-cs-fixer.php" ] && echo " ✅ php-cs-fixer.php" || echo " ⚠️ php-cs-fixer.php missing"
|
||||
[ -d "Build/phpunit" ] && echo " ✅ phpunit/" || echo " ⚠️ phpunit/ missing"
|
||||
[ -f "Build/Scripts/runTests.sh" ] && echo " ✅ runTests.sh" || echo " ⚠️ runTests.sh missing"
|
||||
fi
|
||||
|
||||
# Check 4: Cache files should NOT be in Build/
|
||||
if [ -f "Build/.php-cs-fixer.cache" ] || [ -f "Build/.phpunit.result.cache" ]; then
|
||||
echo "❌ Cache files in wrong location (Build/ instead of .Build/):"
|
||||
ls -la Build/.*.cache 2>/dev/null || true
|
||||
ISSUES=$((ISSUES + 1))
|
||||
else
|
||||
echo "✅ No cache files in Build/"
|
||||
fi
|
||||
|
||||
# Check 5: Composer paths should reference .Build/
|
||||
BIN_DIR=$(jq -r '.config."bin-dir" // ".Build/bin"' composer.json)
|
||||
VENDOR_DIR=$(jq -r '.config."vendor-dir" // ".Build/vendor"' composer.json)
|
||||
WEB_DIR=$(jq -r '.extra.typo3.cms."web-dir" // ".Build/public"' composer.json)
|
||||
|
||||
if [ "${BIN_DIR}" = ".Build/bin" ]; then
|
||||
echo "✅ Composer bin-dir: ${BIN_DIR}"
|
||||
else
|
||||
echo "⚠️ Composer bin-dir: ${BIN_DIR} (expected .Build/bin)"
|
||||
ISSUES=$((ISSUES + 1))
|
||||
fi
|
||||
|
||||
if [ "${VENDOR_DIR}" = ".Build/vendor" ]; then
|
||||
echo "✅ Composer vendor-dir: ${VENDOR_DIR}"
|
||||
else
|
||||
echo "⚠️ Composer vendor-dir: ${VENDOR_DIR} (expected .Build/vendor)"
|
||||
ISSUES=$((ISSUES + 1))
|
||||
fi
|
||||
|
||||
if [ "${WEB_DIR}" = ".Build/public" ]; then
|
||||
echo "✅ TYPO3 web-dir: ${WEB_DIR}"
|
||||
else
|
||||
echo "⚠️ TYPO3 web-dir: ${WEB_DIR} (expected .Build/public)"
|
||||
ISSUES=$((ISSUES + 1))
|
||||
fi
|
||||
|
||||
echo ""
|
||||
if [ ${ISSUES} -eq 0 ]; then
|
||||
echo "✅ Directory structure validation complete - no issues found"
|
||||
exit 0
|
||||
else
|
||||
echo "❌ Directory structure validation found ${ISSUES} issue(s)"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
## Quick Reference Checklist
|
||||
|
||||
**When evaluating directory structure:**
|
||||
|
||||
```
|
||||
□ .gitignore contains .Build/ entry
|
||||
□ Build/ directory exists and contains configs
|
||||
□ Build/phpstan.neon exists
|
||||
□ Build/php-cs-fixer.php exists
|
||||
□ Build/phpunit/ directory exists with XML configs
|
||||
□ Build/Scripts/runTests.sh exists
|
||||
□ .Build/ is NOT tracked in git (git ls-files .Build/ is empty)
|
||||
□ Cache files are in .Build/, not Build/
|
||||
□ Composer bin-dir = .Build/bin
|
||||
□ Composer vendor-dir = .Build/vendor
|
||||
□ TYPO3 web-dir = .Build/public
|
||||
□ No configuration files in project root (rector.php, phpstan.neon, etc.)
|
||||
```
|
||||
|
||||
## Configuration File Templates
|
||||
|
||||
### Build/phpstan.neon
|
||||
|
||||
```neon
|
||||
includes:
|
||||
- vendor/phpstan/phpstan-strict-rules/rules.neon
|
||||
- vendor/saschaegerer/phpstan-typo3/extension.neon
|
||||
|
||||
parameters:
|
||||
level: max
|
||||
paths:
|
||||
- Classes
|
||||
- Tests
|
||||
tmpDir: .Build/phpstan
|
||||
reportUnmatchedIgnoredErrors: true
|
||||
```
|
||||
|
||||
### Build/php-cs-fixer.php
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$finder = (new PhpCsFixer\Finder())
|
||||
->in(__DIR__ . '/../Classes')
|
||||
->in(__DIR__ . '/../Tests');
|
||||
|
||||
return (new PhpCsFixer\Config())
|
||||
->setRules([
|
||||
'@PSR12' => true,
|
||||
'@PhpCsFixer' => true,
|
||||
'declare_strict_types' => true,
|
||||
])
|
||||
->setCacheFile('.Build/.php-cs-fixer.cache')
|
||||
->setRiskyAllowed(true)
|
||||
->setFinder($finder);
|
||||
```
|
||||
|
||||
### Build/rector/rector.php
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Config\RectorConfig;
|
||||
use Rector\ValueObject\PhpVersion;
|
||||
use Ssch\TYPO3Rector\Set\Typo3LevelSetList;
|
||||
|
||||
return RectorConfig::configure()
|
||||
->withPaths([
|
||||
__DIR__ . '/../../Classes/',
|
||||
__DIR__ . '/../../Tests/',
|
||||
])
|
||||
->withPhpVersion(PhpVersion::PHP_82)
|
||||
->withPhpSets(true)
|
||||
->withSets([
|
||||
Typo3LevelSetList::UP_TO_TYPO3_13,
|
||||
]);
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- **Tea Extension Structure:** https://github.com/TYPO3BestPractices/tea
|
||||
- **Composer Documentation:** https://getcomposer.org/doc/06-config.md
|
||||
- **TYPO3 Extension Structure:** https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ExtensionArchitecture/FileStructure/
|
||||
Reference in New Issue
Block a user