# 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 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 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/