diff --git a/.github/actions/phpstan-adjust-scandirs/action.yml b/.github/actions/phpstan-adjust-scandirs/action.yml index 3f9db78..38d07df 100644 --- a/.github/actions/phpstan-adjust-scandirs/action.yml +++ b/.github/actions/phpstan-adjust-scandirs/action.yml @@ -59,6 +59,19 @@ runs: printf "${NC}\n" } + set_env() { + local key="$1" + local value="$2" + echo "${key}=${value}" + echo "${key}=${value}" >> $GITHUB_ENV + } + + if [ ! -f phpstan.neon ]; then + # Mark phpstan.neon config as generated (missing level and scan paths) + # so that the `php-sa` workflow supplies these parameters through CLI arguments. + set_env PHPSTAN_CONFIG_GENERATED 1 + fi + log_cmd phpstan-adjust-scandirs.php \ --config=phpstan.neon --scan="${PHPSTAN_SCANDIRS}" --exclude="${PHPSTAN_EXCLUDE}" --inplace ./phpstan-adjust-scandirs/phpstan-adjust-scandirs/phpstan-adjust-scandirs.php \ diff --git a/.github/workflows/php-sa.yml b/.github/workflows/php-sa.yml index f3228f4..54422f1 100644 --- a/.github/workflows/php-sa.yml +++ b/.github/workflows/php-sa.yml @@ -50,7 +50,7 @@ jobs: php-tools: phpstan - name: Adjust PHPStan scanDirectories - if: ${{ hashFiles(format('{0}/{1}', inputs.working-directory, 'phpstan.neon')) != '' && env.PHPSTAN_SCANDIRS != ''}} + if: ${{ env.PHPSTAN_SCANDIRS != ''}} uses: Icinga/github-actions/.github/actions/phpstan-adjust-scandirs@main with: working-directory: ${{ inputs.working-directory }} @@ -71,16 +71,27 @@ jobs: printf "${NC}\n" } + args=() + + configuration="" if [ -f "${{ inputs.working-directory }}/phpstan.neon" ]; then - args=(--configuration "${{ inputs.working-directory }}/phpstan.neon") - else - # Find all direct child directories, excluding vendor and hidden dirs - dirs=($(find "${{ inputs.working-directory }}" -mindepth 1 -maxdepth 1 \ + configuration="${{ inputs.working-directory }}/phpstan.neon" + fi; + + # Supply level and scan paths as CLI arguments if PHPStan config is missing or generated. + if [ -z "$configuration" ] || [ -n "$PHPSTAN_CONFIG_GENERATED" ]; then + # Find all direct child directories containing PHP files; exclude vendor dirs. + php_dirs=() + while IFS= read -r -d '' dir; do + if [[ -n $(find "$dir" -type f -name "*.php" -print -quit) ]]; then + php_dirs+=("$dir") + fi + done < <(find "${{ inputs.working-directory }}" -mindepth 1 -maxdepth 1 \ -type d \ - -not -path "${{ inputs.working-directory }}/vendor" \ - -not -path "${{ inputs.working-directory }}/.*")) + -not -name "vendor" \ + -print0) - # Find all direct child PHP files + # Find all direct child PHP files. php_files=($(find "${{ inputs.working-directory }}" -mindepth 1 -maxdepth 1 \ -type f -name '*.php')) @@ -92,11 +103,22 @@ jobs: composer_vendor="$(composer -d ${{ inputs.working-directory }} config vendor-dir)" autoload="${{ inputs.working-directory }}/${composer_vendor}/autoload.php" fi + args=(--level=6) if [ -n "$autoload" ]; then args+=(-a "$autoload") fi - args+=("${dirs[@]}" "${php_files[@]}") + args+=("${php_dirs[@]}" "${php_files[@]}") + fi + + if [ -n "$configuration" ]; then + array_unshift() { + local -n arr="$1" + shift + arr=("$@" "${arr[@]}") + } + + array_unshift args --configuration "$configuration" fi log_cmd phpstan analyse "${args[@]}" diff --git a/phpstan-adjust-scandirs/phpstan-adjust-scandirs.php b/phpstan-adjust-scandirs/phpstan-adjust-scandirs.php index d731da6..7302d6c 100755 --- a/phpstan-adjust-scandirs/phpstan-adjust-scandirs.php +++ b/phpstan-adjust-scandirs/phpstan-adjust-scandirs.php @@ -29,7 +29,7 @@ function usage(): void --exclude Directories to exclude from analysis, separated by PATH_SEPARATOR (optional) --inplace Update phpstan.neon configuration file in-place (default: output to stdout) -This script merges provided scan and exclude directories with the existing config, +This script merges provided scan and exclude directories with any existing config, validates directory readability, removes nested directories to keep only the highest-level, and outputs the updated NEON config or writes it in-place. @@ -66,14 +66,15 @@ function usage(): void $diff[] = "+ $dir"; } -try { - $config = Neon::decodeFile($configFile); - if ($config === null) { - throw new Exception("Config is empty"); +if (is_file($configFile)) { + try { + $config = Neon::decodeFile($configFile) ?? []; + } catch (Throwable $e) { + fwrite(STDERR, "Error: Failed to decode NEON config: " . $e->getMessage() . "\n"); + exit(1); } -} catch (Throwable $e) { - fwrite(STDERR, "Error: Failed to decode NEON config: " . $e->getMessage() . "\n"); - exit(1); +} else { + $config = []; } foreach ((array) ($config['parameters']['scanDirectories'] ?? []) as $existing) {