Skip to content

Instantly share code, notes, and snippets.

@jayenne
Last active September 10, 2025 10:37
Show Gist options
  • Select an option

  • Save jayenne/c381a46513868db0bcfeab89928b7ed3 to your computer and use it in GitHub Desktop.

Select an option

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