# yaml-language-server: $schema=https://json.schemastore.org/github-action.json # permissions: # - contents: read # Required for checking out repository --- name: Run Composer Install description: 'Runs Composer install on a repository with advanced caching and configuration.' author: 'Ismo Vuorinen' branding: icon: server color: gray-dark inputs: php: description: 'PHP Version to use.' required: true default: '8.4' extensions: description: 'Comma-separated list of PHP extensions to install' required: false default: 'mbstring, xml, zip, curl, json' tools: description: 'Comma-separated list of Composer tools to install' required: false default: 'composer:v2' args: description: 'Arguments to pass to Composer.' required: false default: '--no-progress --prefer-dist --optimize-autoloader' composer-version: description: 'Composer version to use (1 or 2)' required: false default: '2' stability: description: 'Minimum stability (stable, RC, beta, alpha, dev)' required: false default: 'stable' cache-directories: description: 'Additional directories to cache (comma-separated)' required: false default: '' token: description: 'GitHub token for private repository access' required: false default: '' max-retries: description: 'Maximum number of retry attempts for Composer commands' required: false default: '3' outputs: lock: description: 'composer.lock or composer.json file hash' value: ${{ steps.hash.outputs.lock }} php-version: description: 'Installed PHP version' value: ${{ steps.php.outputs.version }} composer-version: description: 'Installed Composer version' value: ${{ steps.composer.outputs.version }} cache-hit: description: 'Indicates if there was a cache hit' value: ${{ steps.composer-cache.outputs.cache-hit }} runs: using: composite steps: - name: Mask Secrets shell: bash env: GITHUB_TOKEN: ${{ inputs.token || github.token }} run: | echo "::add-mask::$GITHUB_TOKEN" - name: Checkout Repository uses: actions/checkout@71cf2267d89c5cb81562390fa70a37fa40b1305e # v6-beta with: token: ${{ inputs.token || github.token }} - name: Validate Inputs id: validate uses: ivuorinen/actions/validate-inputs@0fa9a68f07a1260b321f814202658a6089a43d42 with: action-type: php-composer - name: Setup PHP id: php uses: shivammathur/setup-php@bf6b4fbd49ca58e4608c9c89fba0b8d90bd2a39f # 2.35.5 with: php-version: ${{ inputs.php }} extensions: ${{ inputs.extensions }} tools: ${{ inputs.tools }} coverage: none ini-values: memory_limit=1G, max_execution_time=600 fail-fast: true - name: Get Dependency Hashes id: hash shell: bash env: CACHE_DIRECTORIES: ${{ inputs.cache-directories }} COMPOSER_LOCK_HASH: ${{ hashFiles('**/composer.lock') }} COMPOSER_JSON_HASH: ${{ hashFiles('**/composer.json') }} run: | set -euo pipefail # Function to calculate directory hash calculate_dir_hash() { local dir=$1 if [ -d "$dir" ]; then find "$dir" -type f -exec sha256sum {} \; | sort | sha256sum | cut -d' ' -f1 fi } # Get composer.lock hash or composer.json hash if [ -f composer.lock ]; then echo "lock=$COMPOSER_LOCK_HASH" >> $GITHUB_OUTPUT else echo "lock=$COMPOSER_JSON_HASH" >> $GITHUB_OUTPUT fi # Calculate additional directory hashes if [ -n "$CACHE_DIRECTORIES" ]; then IFS=',' read -ra DIRS <<< "$CACHE_DIRECTORIES" for dir in "${DIRS[@]}"; do dir_hash=$(calculate_dir_hash "$dir") if [ -n "$dir_hash" ]; then echo "${dir}_hash=$dir_hash" >> $GITHUB_OUTPUT fi done fi - name: Configure Composer id: composer shell: bash env: GITHUB_TOKEN: ${{ inputs.token || github.token }} STABILITY: ${{ inputs.stability }} COMPOSER_VERSION: ${{ inputs.composer-version }} run: | set -euo pipefail # Configure Composer environment composer config --global process-timeout 600 composer config --global allow-plugins true composer config --global github-oauth.github.com "$GITHUB_TOKEN" if [ "$STABILITY" != "stable" ]; then composer config minimum-stability "$STABILITY" fi # Verify Composer installation composer_full_version=$(composer --version | grep -oP 'Composer version \K[0-9]+\.[0-9]+\.[0-9]+') if [ -z "$composer_full_version" ]; then echo "::error::Failed to detect Composer version" exit 1 fi # Extract major version for comparison composer_major_version=${composer_full_version%%.*} expected_version="$COMPOSER_VERSION" echo "Detected Composer version: $composer_full_version (major: $composer_major_version)" if [ "$composer_major_version" != "$expected_version" ]; then echo "::error::Composer major version mismatch. Expected $expected_version.x, got $composer_full_version" exit 1 fi # Store full version for output echo "version=$composer_full_version" >> "$GITHUB_OUTPUT" # Log Composer configuration echo "Composer Configuration:" composer config --list - name: Cache Composer packages id: composer-cache uses: ivuorinen/actions/common-cache@0fa9a68f07a1260b321f814202658a6089a43d42 with: type: 'composer' paths: vendor,~/.composer/cache${{ inputs.cache-directories != "" && format(",{0}", inputs.cache-directories) || "" }} key-prefix: 'php-${{ inputs.php }}-composer-${{ inputs.composer-version }}' key-files: 'composer.lock,composer.json' restore-keys: | ${{ runner.os }}-php-${{ inputs.php }}-composer-${{ inputs.composer-version }}- ${{ runner.os }}-php-${{ inputs.php }}-composer- ${{ runner.os }}-php-${{ inputs.php }}- - name: Clear Composer Cache Before Final Attempt if: steps.composer-cache.outputs.cache-hit != 'true' shell: bash run: | set -euo pipefail echo "Clearing Composer cache to ensure clean installation..." composer clear-cache - name: Install Dependencies uses: step-security/retry@e1d59ce1f574b32f0915e3a8df055cfe9f99be5d # v3 with: timeout_minutes: 10 max_attempts: ${{ inputs.max-retries }} retry_wait_seconds: 30 command: composer install ${{ inputs.args }} - name: Verify Installation shell: bash run: | set -euo pipefail # Verify vendor directory if [ ! -d "vendor" ]; then echo "::error::vendor directory not found" exit 1 fi # Verify autoloader if [ ! -f "vendor/autoload.php" ]; then echo "::error::autoload.php not found" exit 1 fi - name: Generate Optimized Autoloader if: success() shell: bash run: |- set -euo pipefail composer dump-autoload --optimize --classmap-authoritative