diff --git a/.git-hooks-matomo/pre-push b/.git-hooks-matomo/pre-push index c0e95b94..eb24a2ca 100755 --- a/.git-hooks-matomo/pre-push +++ b/.git-hooks-matomo/pre-push @@ -17,13 +17,13 @@ ### Check we're running in the context of a plugin and get helpful dir variables ### REPO_DIR="$(git rev-parse --show-toplevel)" -echo "Running pre-commit hook in repo: $REPO_DIR" +echo "Running pre-push hook in repo: $REPO_DIR" if [[ "$REPO_DIR" =~ /plugins/(.*) ]]; then PLUGIN_PATH="plugins/${BASH_REMATCH[1]}/" else - echo "Not a plugin, not running any further checks" - exit 1 + echo "Not inside a Matomo checkout's plugins/ directory, skipping PHPStan checks" + exit 0 fi MATOMO_DIR=$(echo "$REPO_DIR" | sed -E 's|/plugins/.*$||') @@ -56,39 +56,59 @@ fi # Basic setup cd "$REPO_DIR" STATUS=0 +MAIN_BRANCH='5.x-dev' +ZERO_OID='0000000000000000000000000000000000000000' +PHPSTAN_CREATED_CONFIG=phpstan/phpstan.created.neon +PHPSTAN_MODIFIED_CONFIG=phpstan/phpstan.modified.neon +### Run PHPStan on the files a pushed commit adds or changes. ### -### Run PHPStan on newly created files. ### +# $1 -- the pushed commit +# $2 -- git diff filter (A for created files, CM for modified files) +# $3 -- the phpstan config to use +# $4 -- log label for the file kind +check_pushed_commit() { + local commit="$1" filter="$2" config="$3" label="$4" -PHPSTAN_CREATED_CONFIG=phpstan/phpstan.created.neon -MAIN_BRANCH='5.x-dev' -if [[ -f "$PHPSTAN_CREATED_CONFIG" ]]; then - CHANGED_FILES=$(git diff --name-only ${MAIN_BRANCH} --diff-filter=A | grep '\.php$' || true) - if [ -z "$CHANGED_FILES" ]; then - echo "No created PHP files" - else - echo "Running PHPstan at a very high level on new files" - CHANGED_FILES=`echo "$CHANGED_FILES" | sed -e 's/^\(.*\)$/"\1"/' | xargs -I{} echo "${PLUGIN_PATH}{}"` - echo "$CHANGED_FILES" | xargs $COMMAND analyse -c ${PLUGIN_PATH}${PHPSTAN_CREATED_CONFIG} || STATUS=1 + if [[ ! -f "$config" ]]; then + return 0 fi -fi + # Use the merge base with the remote main branch: the local branch can be stale + # or missing, which silently widens the diff to files the push doesn't touch. + local diff_base + diff_base=$(git merge-base "$commit" "origin/${MAIN_BRANCH}" 2>/dev/null) + if [[ -z "$diff_base" ]]; then + echo "Could not resolve the merge base between ${commit} and origin/${MAIN_BRANCH}." + echo "Run 'git fetch origin ${MAIN_BRANCH}' and push again." + return 1 + fi + local changed_files + changed_files=$(git diff --name-only "$diff_base" "$commit" --diff-filter="$filter" | grep '\.php$' || true) + if [ -z "$changed_files" ]; then + echo "No ${label} PHP files" + return 0 + fi -### Run PHPStan on modified files. ### -PHPSTAN_MODIFIED_CONFIG=phpstan/phpstan.modified.neon -if [[ -f "$PHPSTAN_MODIFIED_CONFIG" ]]; then - CHANGED_FILES=$(git diff --name-only ${MAIN_BRANCH} --diff-filter=CM | grep '\.php$' || true) - if [ -z "$CHANGED_FILES" ]; then - echo "No changed PHP files" - else - echo "Running PHPstan on modified files" - CHANGED_FILES=`echo "$CHANGED_FILES" | sed -e 's/^\(.*\)$/"\1"/' | xargs -I{} echo "${PLUGIN_PATH}{}"` - echo "$CHANGED_FILES" | xargs $COMMAND analyse -c ${PLUGIN_PATH}${PHPSTAN_MODIFIED_CONFIG} || STATUS=1 + echo "Running PHPstan on ${label} files" + changed_files=`echo "$changed_files" | sed -e 's/^\(.*\)$/"\1"/' | xargs -I{} echo "${PLUGIN_PATH}{}"` + echo "$changed_files" | xargs $COMMAND analyse -c ${PLUGIN_PATH}${config} || return 1 +} + +# Check the commits actually being pushed, as supplied on stdin: HEAD is wrong +# when pushing another local branch or several refs at once. The inner commands +# read /dev/null so they cannot consume the remaining stdin lines. +while read -r local_ref local_oid remote_ref remote_oid; do + if [[ "$local_oid" == "$ZERO_OID" ]]; then + continue # deleting the remote ref, nothing is pushed fi -fi + echo "Checking ${local_ref} (${local_oid})" + check_pushed_commit "$local_oid" A "$PHPSTAN_CREATED_CONFIG" "created" < /dev/null || STATUS=1 + check_pushed_commit "$local_oid" CM "$PHPSTAN_MODIFIED_CONFIG" "modified" < /dev/null || STATUS=1 +done # Don't bother running the full check, as we check changes files already, and # can assume that the unchanged files don't need rechecking. diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 9762ff92..73b0b2f9 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -3,87 +3,13 @@ name: PHPStan check on: pull_request permissions: - actions: read - checks: read contents: read - deployments: none - issues: read - packages: none - pull-requests: read - repository-projects: none - security-events: none - statuses: read - -env: - PLUGIN_NAME: GoogleAnalyticsImporter - DEPENDENT_PLUGINS: matomo-org/plugin-MarketingCampaignsReporting innocraft/plugin-Funnels innocraft/plugin-ConnectAccounts jobs: phpstan: - name: PHPStan - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - lfs: false - persist-credentials: false - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - # 8.2 rather than 7.2: the ConnectAccounts dependency uses PHP 8 - # attribute syntax, which fatals when loaded on 7.2. The analysis - # level stays PHP 7.2 via phpVersion in phpstan.neon. - php-version: '8.2' - - - name: Check out github-action-tests repository - uses: actions/checkout@v4 - with: - repository: matomo-org/github-action-tests - ref: main - path: github-action-tests - persist-credentials: false - - - name: checkout matomo for plugin builds - shell: bash - run: ${{ github.workspace }}/github-action-tests/scripts/bash/checkout_matomo.sh - env: - PLUGIN_NAME: ${{ env.PLUGIN_NAME }} - WORKSPACE: ${{ github.workspace }} - ACTION_PATH: ${{ github.workspace }}/github-action-tests - MATOMO_TEST_TARGET: maximum_supported_matomo - - - name: prepare setup - shell: bash - run: | - cd ${{ github.workspace }}/matomo - echo -e "composer install" - composer install --ignore-platform-reqs - - - name: checkout additional plugins - if: ${{ env.DEPENDENT_PLUGINS != '' }} - shell: bash - working-directory: ${{ github.workspace }}/matomo - run: ${{ github.workspace }}/github-action-tests/scripts/bash/checkout_dependent_plugins.sh - - env: - DEPENDENT_PLUGINS: ${{ env.DEPENDENT_PLUGINS }} - GITHUB_USER_TOKEN: ${{ secrets.TESTS_ACCESS_TOKEN || secrets.GITHUB_TOKEN }} - - - name: "Restore result cache" - uses: actions/cache/restore@v4 - with: - path: /tmp/phpstan # same as in phpstan.neon - key: "phpstan-result-cache-${{ github.run_id }}" - restore-keys: | - phpstan-result-cache- - - - name: PHPStan whole repo - id: phpstan-all - run: cd ${{ github.workspace }}/matomo && composer run phpstan -- -vvv -c plugins/${{ env.PLUGIN_NAME }}/phpstan.neon - - - name: "Save result cache" - uses: actions/cache/save@v4 - if: ${{ !cancelled() }} - with: - path: /tmp/phpstan # same as in phpstan.neon - key: "phpstan-result-cache-${{ github.run_id }}" + uses: matomo-org/plugin-ci-workflows/.github/workflows/plugin-phpstan.yml@main + with: + plugin-name: GoogleAnalyticsImporter + dependent-plugins: 'matomo-org/plugin-MarketingCampaignsReporting innocraft/plugin-Funnels innocraft/plugin-ConnectAccounts' + secrets: + TESTS_ACCESS_TOKEN: ${{ secrets.TESTS_ACCESS_TOKEN }} diff --git a/phpstan.neon b/phpstan.neon index d7cae372..cdc521dc 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -12,7 +12,7 @@ parameters: # still scanned so the prefixed Google classes are discovered even # when the Matomo bootstrap cannot load plugin autoloaders (e.g. CI) analyse: - - vendor/ + - vendor/ (?) ignoreErrors: # WpMatomo and get_option exist only when running inside Matomo for WordPress - '#^Instantiated class WpMatomo\\Site not found\.$#' diff --git a/phpstan/phpstan.created.neon b/phpstan/phpstan.created.neon index e9a7cc9b..1ddc9b14 100644 --- a/phpstan/phpstan.created.neon +++ b/phpstan/phpstan.created.neon @@ -1,5 +1,6 @@ includes: - ../phpstan.neon parameters: - level: 5 + # new files carry no pre-existing debt, so hold them to the strictest level + level: 9 tmpDir: /tmp/phpstan/GoogleAnalyticsImporter/created diff --git a/phpstan/phpstan.modified.neon b/phpstan/phpstan.modified.neon index fc9d1a45..c6524a25 100644 --- a/phpstan/phpstan.modified.neon +++ b/phpstan/phpstan.modified.neon @@ -1,5 +1,7 @@ includes: - ../phpstan.neon parameters: + # touched files are ratcheted up to the plugin-wide goal of level 5, + # even where the CI base level is lower level: 5 tmpDir: /tmp/phpstan/GoogleAnalyticsImporter/modified