Last active
September 10, 2025 10:37
-
-
Save jayenne/c381a46513868db0bcfeab89928b7ed3 to your computer and use it in GitHub Desktop.
Build environmanet specific composer.json files by .env APP_ENV or by --env=local
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # filepath: ./composer-by-env.sh | |
| ############################################################################### | |
| # Composer Environment Configuration Script | |
| # | |
| # Author: Jayenne Montana | |
| # License: MIT License | |
| # | |
| # Copyright (c) 2025 Jayenne Montana | |
| # | |
| # Permission is hereby granted, free of charge, to any person obtaining a copy | |
| # of this software and associated documentation files (the "Software"), to deal | |
| # in the Software without restriction, including without limitation the rights | |
| # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| # copies of the Software, and to permit persons to whom the Software is | |
| # furnished to do so, subject to the following conditions: | |
| # | |
| # The above copyright notice and this permission notice shall be included in all | |
| # copies or substantial portions of the Software. | |
| # | |
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| # SOFTWARE. | |
| # | |
| ############################################################################### | |
| # | |
| # This script intelligently merges environment-specific composer configuration | |
| # files with the base composer.json file based on the APP_ENV value from .env | |
| # or command line override. The script supports deep merging with key removal | |
| # and tilde-prefixed object deletion. | |
| # | |
| # USAGE: | |
| # ./composer-by-env.sh [--env=environment] | |
| # | |
| # EXAMPLES: | |
| # # Use APP_ENV from .env file (default behavior) | |
| # ./composer-by-env.sh | |
| # | |
| # # Override environment to 'local' | |
| # ./composer-by-env.sh --env=local | |
| # ./composer-by-env.sh --env local | |
| # | |
| # # Override environment to 'staging' | |
| # ./composer-by-env.sh --env=staging | |
| # | |
| # # Override environment to 'production' | |
| # ./composer-by-env.sh --env=production | |
| # | |
| # REQUIRED FILES: | |
| # - composer.json (base configuration) | |
| # - composer.{APP_ENV}.json (environment-specific configuration) | |
| # - .env (for APP_ENV value, if not overridden) | |
| # | |
| # MERGING BEHAVIOR: | |
| # 1. Deep merges nested objects (autoload, config, etc.) | |
| # 2. Environment file keys take precedence over base composer.json | |
| # 3. Keys present in base but missing in environment file are REMOVED | |
| # 4. Keys prefixed with "~" (tilde) are completely removed from output | |
| # 5. Maintains proper JSON formatting and comma handling | |
| # | |
| # EXAMPLE FILES: | |
| # composer.local.json (adds local development repositories): | |
| # { | |
| # "repositories": [ | |
| # { | |
| # "type": "path", | |
| # "url": "../../Contributions/multicaret/laravel-acquaintances", | |
| # "options": { "symlink": true } | |
| # } | |
| # ], | |
| # "minimum-stability": "dev", | |
| # "prefer-stable": false | |
| # } | |
| # | |
| # composer.staging.json (removes repositories entirely using tilde prefix): | |
| # { | |
| # "autoload": { | |
| # "psr-4": { | |
| # "App\\": "app/", | |
| # "Database\\Factories\\": "database/factories/" | |
| # } | |
| # }, | |
| # "~repositories": [], | |
| # "minimum-stability": "stable" | |
| # } | |
| # | |
| # composer.production.json (removes specific autoload entries): | |
| # { | |
| # "autoload": { | |
| # "psr-4": { | |
| # "App\\": "app/", | |
| # "Database\\Factories\\": "database/factories/", | |
| # "Database\\Seeders\\": "database/seeders/" | |
| # } | |
| # }, | |
| # "repositories": [ | |
| # { | |
| # "type": "vcs", | |
| # "url": "https://github.com/multicaret/laravel-acquaintances" | |
| # } | |
| # ], | |
| # "minimum-stability": "stable", | |
| # "prefer-stable": true | |
| # } | |
| # | |
| # KEY REMOVAL EXAMPLES: | |
| # Base composer.json has: | |
| # "autoload": { | |
| # "psr-4": { | |
| # "App\\": "app/", | |
| # "Database\\Factories\\": "database/factories/", | |
| # "Database\\Seeders\\": "database/seeders/", | |
| # "Multicaret\\Acquaintances\\": "src/" | |
| # } | |
| # } | |
| # | |
| # Environment file has: | |
| # "autoload": { | |
| # "psr-4": { | |
| # "App\\": "app/", | |
| # "Database\\Factories\\": "database/factories/" | |
| # } | |
| # } | |
| # | |
| # Result: "Database\\Seeders\\" and "Multicaret\\Acquaintances\\" are removed | |
| # | |
| # TILDE REMOVAL EXAMPLES: | |
| # Environment file has: | |
| # { | |
| # "~repositories": [...], // Entire repositories section removed | |
| # "~require-dev": {...}, // Entire require-dev section removed | |
| # "autoload": {...} // Normal key, will be merged | |
| # } | |
| # | |
| # Result: repositories and require-dev sections completely removed from output | |
| # | |
| # BEHAVIOR: | |
| # 1. Reads APP_ENV from .env file or uses command line override | |
| # 2. Looks for composer.{APP_ENV}.json file | |
| # 3. Exits with error if environment file not found | |
| # 4. Backs up original composer.json to composer.json.backup | |
| # 5. Performs intelligent deep merge with key removal | |
| # 6. Removes any keys prefixed with "~" (tilde) | |
| # 7. Uses 'jq' for advanced merging, falls back to replacement without jq | |
| # | |
| # DEPENDENCIES: | |
| # - jq (recommended, for intelligent merging - will fallback to file replacement) | |
| # - sed (for basic tilde removal without jq) | |
| # | |
| # MAKEFILE INTEGRATION: | |
| # compose-local: | |
| # ./composer-by-env.sh --env=local | |
| # | |
| # compose-production: | |
| # ./composer-by-env.sh --env=production | |
| # | |
| ############################################################################### | |
| # Parse command line arguments | |
| while [[ $# -gt 0 ]]; do | |
| case $1 in | |
| --env=*) | |
| OVERRIDE_ENV="${1#*=}" | |
| shift | |
| ;; | |
| --env) | |
| OVERRIDE_ENV="$2" | |
| shift | |
| shift | |
| ;; | |
| *) | |
| echo "Unknown option $1" | |
| echo "Usage: $0 [--env=environment]" | |
| echo "Example: $0 --env=local" | |
| exit 1 | |
| ;; | |
| esac | |
| done | |
| # Load environment variables from .env file | |
| if [ -f .env ]; then | |
| export $(grep -v '^#' .env | xargs) | |
| fi | |
| # Use override if provided, otherwise use APP_ENV from .env, otherwise default to production | |
| if [ -n "$OVERRIDE_ENV" ]; then | |
| APP_ENV="$OVERRIDE_ENV" | |
| echo "Using command line override: APP_ENV=$APP_ENV" | |
| else | |
| APP_ENV=${APP_ENV:-production} | |
| echo "APP_ENV is set to: $APP_ENV" | |
| fi | |
| # Define the environment-specific composer file | |
| COMPOSER_ENV_FILE="composer.${APP_ENV}.json" | |
| # Check if environment-specific composer file exists FIRST | |
| if [ ! -f "$COMPOSER_ENV_FILE" ]; then | |
| # Exit with error if environment-specific composer file is not found | |
| echo "Error: $COMPOSER_ENV_FILE not found for environment '$APP_ENV'" | |
| echo "" | |
| echo "Please create $COMPOSER_ENV_FILE with environment-specific composer configuration." | |
| echo "Example structure:" | |
| echo "{" | |
| echo " \"repositories\": [...]," | |
| echo " \"require-dev\": {...}," | |
| echo " \"minimum-stability\": \"dev\"" | |
| echo "}" | |
| echo "" | |
| exit 1 | |
| fi | |
| # Function to intelligently merge composer files using jq | |
| merge_composer_files() { | |
| local env_file=$1 | |
| if command -v jq >/dev/null 2>&1; then | |
| echo "Using jq to intelligently merge $env_file into composer.json..." | |
| # Create a temporary jq script for complex merging | |
| cat > merge_script.jq << 'EOF' | |
| # Remove keys starting with tilde from overlay before merging | |
| def remove_tilde_keys: | |
| if type == "object" then | |
| with_entries( | |
| select(.key | startswith("~") | not) | | |
| .value |= remove_tilde_keys | |
| ) | |
| elif type == "array" then | |
| map(remove_tilde_keys) | |
| else | |
| . | |
| end; | |
| # Deep merge that preserves base keys and adds/overwrites with overlay | |
| def deep_merge: | |
| .[0] as $base | .[1] as $overlay | | |
| $base * ($overlay | remove_tilde_keys); | |
| # Main processing - simple merge with tilde removal | |
| deep_merge | |
| EOF | |
| # Apply the merge | |
| jq -s -f merge_script.jq composer.json "$env_file" > composer.temp.json | |
| if [ $? -eq 0 ]; then | |
| mv composer.temp.json composer.json | |
| rm merge_script.jq | |
| echo "Successfully merged $env_file into composer.json" | |
| else | |
| echo "Error: Failed to merge $env_file" | |
| rm -f composer.temp.json merge_script.jq | |
| exit 1 | |
| fi | |
| else | |
| echo "jq not available - replacing composer.json with $env_file" | |
| cp "$env_file" composer.json | |
| echo "Replaced composer.json with $env_file" | |
| fi | |
| } | |
| # File exists, proceed with merging | |
| echo "Found $COMPOSER_ENV_FILE - merging with composer.json..." | |
| # Backup original composer.json if not already backed up | |
| if [ ! -f "composer.json.backup" ]; then | |
| cp composer.json composer.json.backup | |
| echo "Backed up composer.json to composer.json.backup" | |
| fi | |
| # Merge environment-specific configuration | |
| merge_composer_files "$COMPOSER_ENV_FILE" | |
| echo "Configuration updated for $APP_ENV environment" | |
| echo "Composer configuration ready for $APP_ENV environment" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment