diff --git a/.github/workflows/phpcs.yml b/.github/workflows/phpcs.yml
index a8c4a2d..a0290ee 100644
--- a/.github/workflows/phpcs.yml
+++ b/.github/workflows/phpcs.yml
@@ -6,6 +6,7 @@ on:
- '**.php'
- tools/phpcs/composer.json
- phpcs.xml.dist
+ - .github/workflows/phpcs.yml
jobs:
phpcs:
@@ -13,7 +14,7 @@ jobs:
name: PHP_CodeSniffer
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
@@ -29,7 +30,7 @@ jobs:
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache dependencies
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('tools/phpcs/composer.json') }}
diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml
index 1333c1d..efc0b1d 100644
--- a/.github/workflows/phpstan.yml
+++ b/.github/workflows/phpstan.yml
@@ -9,18 +9,19 @@ on:
- ci/composer.json
- phpstan.ci.neon
- phpstan.neon.dist
+ - .github/workflows/phpstan.yml
jobs:
phpstan:
runs-on: ubuntu-latest
strategy:
matrix:
- php-versions: ['7.4', '8.0', '8.3']
+ php-versions: ['8.1', '8.4']
prefer: ['prefer-stable', 'prefer-lowest']
name: PHPStan with PHP ${{ matrix.php-versions }} ${{ matrix.prefer }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
@@ -35,7 +36,7 @@ jobs:
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache dependencies
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ matrix.prefer }}-${{ hashFiles('**/composer.json') }}
diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml
index 6f91884..e06eaa1 100644
--- a/.github/workflows/phpunit.yml
+++ b/.github/workflows/phpunit.yml
@@ -8,6 +8,7 @@ on:
- tools/phpunit/composer.json
- phpunit.xml.dist
- tests/docker-prepare.sh
+ - .github/workflows/phpunit.yml
env:
# On github CI machine creating the "/vendor" volume fails otherwise with: read-only file system: unknown
@@ -18,13 +19,13 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- civicrm-image-tags: [ '5-drupal-php8.1', '5-drupal-php7.4', '5.45-drupal-php7.4' ]
+ civicrm-image-tags: [ 'drupal', '5.55-drupal-php8.1' ]
name: PHPUnit with Docker image michaelmcandrew/civicrm:${{ matrix.civicrm-image-tags }}
env:
CIVICRM_IMAGE_TAG: ${{ matrix.civicrm-image-tags }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Pull images
run: docker compose -f tests/docker-compose.yml pull --quiet
- name: Start containers
diff --git a/README.md b/README.md
index c641e7e..c4e7d80 100644
--- a/README.md
+++ b/README.md
@@ -20,8 +20,8 @@ The extension is licensed under [AGPL-3.0](LICENSE.txt).
## Requirements
-* PHP v7.4+
-* CiviCRM 5
+* PHP v8.1+
+* CiviCRM 5.55+
## Installation (Web UI)
diff --git a/ci/composer.json b/ci/composer.json
index 5f8cb19..0f93a1e 100644
--- a/ci/composer.json
+++ b/ci/composer.json
@@ -10,7 +10,7 @@
"sort-packages": true
},
"require": {
- "civicrm/civicrm-core": "^5.55",
- "civicrm/civicrm-packages": "^5.55"
+ "civicrm/civicrm-core": ">=5.55",
+ "civicrm/civicrm-packages": ">=5.55"
}
}
diff --git a/composer.json b/composer.json
index 37e2b09..6c755c3 100644
--- a/composer.json
+++ b/composer.json
@@ -1,5 +1,6 @@
{
"name": "systopia/activity-entity",
+ "description": "Connect activities with other entities",
"type": "civicrm-ext",
"license": "AGPL-3.0-or-later",
"authors": [
@@ -13,9 +14,12 @@
"prefer-stable": true,
"config": {
"prepend-autoloader": false,
- "sort-packages": true
+ "sort-packages": true,
+ "platform": {
+ }
},
"require": {
+ "php": "^8.1",
"webmozart/assert": "^1.11"
},
"scripts": {
@@ -40,7 +44,7 @@
"@php tools/phpcs/vendor/bin/phpcbf"
],
"phpstan": [
- "@php tools/phpstan/vendor/bin/phpstan"
+ "@php tools/phpstan/vendor/bin/phpstan -v"
],
"phpunit": [
"@php tools/phpunit/vendor/bin/simple-phpunit --coverage-text"
diff --git a/info.xml b/info.xml
index 2fe7c11..0aed19e 100644
--- a/info.xml
+++ b/info.xml
@@ -18,7 +18,7 @@
0.1-dev
dev
- 5.54
+ 5.55
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
index 3493c8a..fb94c2a 100644
--- a/phpstan.neon.dist
+++ b/phpstan.neon.dist
@@ -21,6 +21,9 @@ parameters:
- Civi\Core\Event\GenericHookEvent
- CRM_Core_Config
- CRM_Core_DAO
+ earlyTerminatingMethodCalls:
+ CRM_Queue_Runner:
+ - runAllViaWeb
checkTooWideReturnTypesInProtectedAndPublicMethods: true
checkUninitializedProperties: true
checkMissingCallableSignature: true
@@ -38,7 +41,6 @@ parameters:
# Tests
- # Accessing results of API requests
- message: "#^Offset '[^']+' does not exist on array[^\\|]+\\|null.$#"
+ identifier: offsetAccess.notFound
path: */tests/phpunit/Civi/**/*Test.php
- - '#^Method Civi\\Fixtures\\[^\s]+Fixture::[^\s]+\(\) should return array{[^}]+} but returns array\|null.$#'
tmpDir: .phpstan
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 1d1f4e8..a531297 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,6 +1,6 @@
./tests/phpunit
-
-
+
+
+
CRM
Civi
-
- CRM/*/DAO
-
-
-
+
+
+ CRM/ActivityEntity/DAO
+
+
+
diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml
index 16a4949..2a4dfc1 100644
--- a/tests/docker-compose.yml
+++ b/tests/docker-compose.yml
@@ -1,6 +1,6 @@
services:
civicrm:
- image: michaelmcandrew/civicrm:${CIVICRM_IMAGE_TAG:-5-drupal}
+ image: michaelmcandrew/civicrm:${CIVICRM_IMAGE_TAG:-drupal}
environment:
- PROJECT_NAME=test
- BASE_URL=http://localhost
@@ -9,6 +9,8 @@ services:
- CIVICRM_DB_PASS=secret
- CIVICRM_DB_HOST=mysql
- CIVICRM_DB_PORT=3306
+ - CIVICRM_CRED_KEYS=aes-cbc::test
+ - CIVICRM_SIGN_KEYS=jwt-hs256::test
- CIVICRM_SITE_KEY=TEST_KEY
- DRUPAL_DB_NAME=test
- DRUPAL_DB_USER=root
diff --git a/tests/docker-prepare.sh b/tests/docker-prepare.sh
index e95c9ce..e0a5119 100755
--- a/tests/docker-prepare.sh
+++ b/tests/docker-prepare.sh
@@ -4,6 +4,14 @@ set -eu -o pipefail
EXT_DIR=$(dirname "$(dirname "$(realpath "$0")")")
EXT_NAME=$(basename "$EXT_DIR")
+if ! type git >/dev/null 2>&1; then
+ apt -y update
+ apt -y install git
+fi
+
+# Prevent this git error: The repository does not have the correct ownership and git refuses to use it
+git config --global --add safe.directory "/var/www/html/sites/default/files/civicrm/ext/$EXT_NAME"
+
i=0
while ! mysql -h "$CIVICRM_DB_HOST" -P "$CIVICRM_DB_PORT" -u "$CIVICRM_DB_USER" --password="$CIVICRM_DB_PASS" -e 'SELECT 1;' >/dev/null 2>&1; do
i=$((i+1))
diff --git a/tests/phpunit/Civi/Fixtures/ActivityFixture.php b/tests/phpunit/Civi/Fixtures/ActivityFixture.php
index 6d50258..f8c66f4 100644
--- a/tests/phpunit/Civi/Fixtures/ActivityFixture.php
+++ b/tests/phpunit/Civi/Fixtures/ActivityFixture.php
@@ -31,10 +31,11 @@ final class ActivityFixture {
* @throws \CRM_Core_Exception
*/
public static function addFixture(array $values = []): array {
+ // @phpstan-ignore return.type
return Activity::create()->setValues([
'activity_type_id' => 1,
'source_contact_id' => 1,
- ] + $values)->execute()->first();
+ ] + $values)->execute()->single();
}
}
diff --git a/tests/phpunit/Civi/Fixtures/GroupFixture.php b/tests/phpunit/Civi/Fixtures/GroupFixture.php
index 55e3220..222bf22 100644
--- a/tests/phpunit/Civi/Fixtures/GroupFixture.php
+++ b/tests/phpunit/Civi/Fixtures/GroupFixture.php
@@ -31,9 +31,10 @@ final class GroupFixture {
* @throws \CRM_Core_Exception
*/
public static function addFixture(array $values = []): array {
+ // @phpstan-ignore return.type
return Group::create()->setValues([
'title' => 'Test',
- ] + $values)->execute()->first();
+ ] + $values)->execute()->single();
}
}
diff --git a/tools/create-release.sh b/tools/create-release.sh
new file mode 100755
index 0000000..29cbc35
--- /dev/null
+++ b/tools/create-release.sh
@@ -0,0 +1,399 @@
+#!/bin/bash
+set -euo pipefail
+
+# shellcheck disable=SC2155
+readonly PHP=$(which "${PHP:-php}")
+# shellcheck disable=SC2155
+readonly COMPOSER=$(which "${COMPOSER:-composer}")
+# shellcheck disable=SC2155
+readonly JQ=$(which "${JQ:-jq}")
+
+if [ -z "$PHP" ]; then
+ echo "php not found" >&2
+ exit 1
+fi
+
+if [ -z "$COMPOSER" ]; then
+ echo "composer not found" >&2
+ exit 1
+fi
+
+if [ -z "$JQ" ]; then
+ echo "jq not found" >&2
+ exit 1
+fi
+
+# shellcheck disable=SC2155
+readonly SCRIPT_NAME=$(basename "$0")
+
+usage() {
+ cat <.*' info.xml | sed 's#[[:space:]]*\(.*\)[[:space:]]*#\1#')
+ if [ -z "$version" ]; then
+ echo "Version not found in info.xml" >&2
+ exit 1
+ fi
+
+ if ! [[ "$version" =~ ^([0-9]+\.[0-9]+\.[0-9]+)-dev$ ]]; then
+ echo "The version number $version doesn't match the form a.b.c-dev" >&2
+ exit 1
+ fi
+
+ echo "${BASH_REMATCH[1]}"
+}
+
+detectDevelStage() {
+ local -r version=$1
+ if [[ "$version" = *alpha* ]]; then
+ echo alpha
+ elif [[ "$version" = *beta* ]]; then
+ echo beta
+ elif [[ "$version" = 0.* ]]; then
+ echo dev
+ else
+ echo stable
+ fi
+}
+
+detectNextVersion() {
+ local -r version=$1
+ [[ "$version" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+) ]]
+
+ local -r major=${BASH_REMATCH[1]}
+ local minor=${BASH_REMATCH[2]}
+ local patch=${BASH_REMATCH[3]}
+
+ if [[ "$version" = *-* ]]; then
+ # $version has pre-release
+ true
+ elif [ "$major" = 0 ]; then
+ patch=$((patch+1))
+ elif [ "$patch" = 0 ]; then
+ minor=$((minor+1))
+ else
+ patch=$((patch+1))
+ fi
+
+ echo "$major.$minor.$patch-dev"
+}
+
+validateVersion() {
+ local -r version=$1
+ if ! [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-(alpha|beta)[1-9][0-9]*)?$ ]]; then
+ echo "The version number $version is not a valid release version" >&2
+ exit 1
+ fi
+
+ if [ -n "$(git tag -l "$version")" ]; then
+ echo "git tag $version already exists" >&2
+ exit 1
+ fi
+}
+
+validateDevelopmentStage() {
+ local -r develStage=$1
+ if ! [[ "$develStage" =~ ^(stable|alpha|beta|dev)$ ]]; then
+ echo "$develStage is not a valid development stage" >&2
+ exit 1
+ fi
+}
+
+validateNextVersion() {
+ local -r nextVersion=$1
+ if ! [[ "$nextVersion" =~ ^[0-9]+\.[0-9]+\.[0-9]+-dev$ ]]; then
+ echo "The version number $nextVersion is not a valid next version" >&2
+ exit 1
+ fi
+}
+
+validateInfoXml() {
+ if [ ! -f info.xml ]; then
+ echo "info.xml not found in working directory" >&2
+ exit 1
+ fi
+
+ # Ensure info.xml contains elements so that they can be replaced in updateInfoXml.
+ if ! grep -q ".*" info.xml; then
+ echo "version element not found in info.xml" >&2
+ exit 1
+ fi
+
+ if ! grep -q ".*" info.xml; then
+ echo "develStage element not found in info.xml" >&2
+ exit 1
+ fi
+
+ if ! grep -q -e "" -e ".*" info.xml; then
+ echo "releaseDate element not found in info.xml" >&2
+ exit 1
+ fi
+}
+
+updateInfoXml() {
+ local -r version=$1
+ local -r develStage=$2
+ local -r releaseDate=${3:-}
+
+ if [ -z "$releaseDate" ]; then
+ local -r releaseDateXml=""
+ else
+ local -r releaseDateXml="$releaseDate"
+ fi
+
+ sed -i -e "s#.*#$version#g" \
+ -e "s#.*#$develStage#g" \
+ -e "s#.*#$releaseDateXml#g" \
+ -e "s##$releaseDateXml#g" \
+ info.xml
+}
+
+hasComposerRequires() {
+ if [ ! -f composer.json ]; then
+ return 1
+ fi
+
+ # All requires that are not "php" or "ext-*".
+ requires=$("$JQ" -r '.require|keys|.[]' composer.json 2>/dev/null | sed -e '/^php$/d' -e '/ext-.*/d')
+ [ "$requires" != "" ]
+}
+
+isVersionLesser() {
+ "$PHP" -r "if (version_compare('$1', '$2', '>=')) exit(1);"
+}
+
+getMinPhpVersion() {
+ local phpVersion=""
+ local -r phpConstraint=$("$JQ" --raw-output --monochrome-output .require.php composer.json)
+ if [ "$phpConstraint" = "null" ]; then
+ echo "PHP version constraint not found in composer.json. Please consider adding it" >&2
+ echo -n "Minimal supported PHP version: " >&2
+ read -r phpVersion
+ else
+ local -r oldIfs=$IFS
+ IFS=' |'
+ local constraint
+ for constraint in $phpConstraint; do
+ if [[ "$constraint" =~ ^(\^|~|>=)([0-9]+(\.[0-9]+(\.[0-9]+)?)?)$ ]]; then
+ if [ -z "$phpVersion" ] || isVersionLesser "${BASH_REMATCH[2]}" "$phpVersion"; then
+ phpVersion=${BASH_REMATCH[2]}
+ fi
+ fi
+ done
+ IFS=$oldIfs
+
+ if [ -n "$phpVersion" ]; then
+ echo -n "Minimal supported PHP version [$phpVersion]: " >&2
+ read -r input
+ if [ "$input" != "" ]; then
+ phpVersion=$input
+ fi
+ else
+ echo "Minimal supported PHP version could not be detected from composer version constraint. (Supported operators: ^, ~, >=, |)" >&2
+ echo -n "Minimal supported PHP version: " >&2
+ read -r phpVersion
+ fi
+ fi
+
+ echo "$phpVersion"
+}
+
+validateMinPhpVersion() {
+ if ! [[ "$1" =~ ^[0-9]+(\.[0-9]+(\.[0-9]+)?)?$ ]]; then
+ echo "$1 is not a supported minimal PHP version" >&2
+ exit 1
+ fi
+}
+
+updatePot() {
+ local -r potFiles=(l10n/*.pot)
+ if [ ${#potFiles[@]} -ge 1 ] && [ -e "${potFiles[0]}" ] && [ -x tools/update-pot.sh ]; then
+ echo "Update .pot file"
+ tools/update-pot.sh
+ if ! git diff --no-patch --exit-code "${potFiles[*]}"; then
+ echo ".pot file has changed. Please update the translation and push changes to the repository." >&2
+ exit 1
+ fi
+ fi
+}
+
+main() {
+ DRY_RUN=0
+ local noComposer=0
+ local noPotUpdate=0
+
+ while [ $# -gt 0 ]; do
+ case $1 in
+ -h|--help)
+ usage
+ exit 0
+ ;;
+
+ --dry-run)
+ DRY_RUN=1
+ shift
+ ;;
+
+ --no-composer)
+ noComposer=1
+ shift
+ ;;
+
+ --no-pot-update)
+ noPotUpdate=1
+ shift
+ ;;
+
+ *)
+ break
+ ;;
+ esac
+ done
+
+ if [ $# -gt 3 ]; then
+ usage >&2
+ exit 1
+ fi
+
+ validateInfoXml
+
+ local version
+ local nextVersion
+ local develStage
+
+ if [ $# -ge 1 ]; then
+ version=$1
+ else
+ version=$(detectVersion)
+ echo -n "Version [$version]: "
+ read -r input
+ if [ -n "$input" ]; then
+ version=$input
+ fi
+ fi
+ validateVersion "$version"
+
+ if [ $# -ge 2 ]; then
+ develStage=$2
+ else
+ develStage=$(detectDevelStage "$version")
+ echo -n "Development stage [$develStage]: "
+ read -r input
+ if [ -n "$input" ]; then
+ develStage=$input
+ fi
+ fi
+ validateDevelopmentStage "$develStage"
+
+ if [ $# -ge 3 ]; then
+ nextVersion=$3
+ else
+ nextVersion=$(detectNextVersion "$version")
+ echo -n "Next version [$nextVersion]: "
+ read -r input
+ if [ -n "$input" ]; then
+ nextVersion=$input
+ fi
+ fi
+ validateNextVersion "$nextVersion"
+
+ if [ $noComposer -eq 0 ] && ! hasComposerRequires; then
+ noComposer=1
+ fi
+
+ if [ $noComposer -eq 0 ]; then
+ local -r minPhpVersion=$(getMinPhpVersion)
+ validateMinPhpVersion "$minPhpVersion"
+ fi
+
+ if [ $noPotUpdate -eq 0 ]; then
+ updatePot
+ fi
+
+ local -r releaseDate=$(date +%Y-%m-%d)
+ run updateInfoXml "$version" "$develStage" "$releaseDate"
+ run git add info.xml
+
+ if [ $noComposer -eq 0 ]; then
+ local -r previousPlatformPhp=$(composer config platform.php 2>/dev/null ||:)
+ run composer config platform.php "$minPhpVersion"
+ run composer update --no-dev --optimize-autoloader
+ if [ -n "$previousPlatformPhp" ]; then
+ run composer config platform.php "$previousPlatformPhp"
+ else
+ run composer config --unset platform.php
+ fi
+ run git add -f composer.lock vendor
+ fi
+
+ run git commit -m "Set version to $version"
+ run git tag "$version"
+
+ run updateInfoXml "$nextVersion" "dev"
+ run git add info.xml
+
+ if [ $noComposer -eq 0 ]; then
+ run git rm -r composer.lock vendor
+ fi
+
+ if [ -f composer.json ]; then
+ local -r branch=$(git branch --show-current)
+ if [ "$branch" = "main" ] || [ "$branch" = "master" ]; then
+ [[ "$nextVersion" =~ ^([0-9]+\.[0-9]+)\.[0-9]+ ]]
+ local -r alias=${BASH_REMATCH[1]}.x-dev
+ run composer config "extra.branch-alias.dev-$branch" "$alias"
+ run git add composer.json
+ fi
+ fi
+
+ run git commit -m "Set version to $nextVersion"
+
+ echo ""
+ echo "Push changes with: git push && git push --tags"
+}
+
+main "$@"
diff --git a/tools/git/hooks-wrapper.sh b/tools/git/hooks-wrapper.sh
new file mode 100755
index 0000000..f32978c
--- /dev/null
+++ b/tools/git/hooks-wrapper.sh
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+# Inspired by https://github.com/sjungwirth/githooks/blob/949d55e84e92dfd84e9a73a8b7624bb2e4dbc872/bin/git/hooks-wrapper
+
+# This script needs to be symlinked to .git/hooks/.
+
+set -e
+
+HOOKNAME=$(basename "$0")
+NATIVE_HOOKS_DIR=$(dirname "$0")
+CUSTOM_HOOKS_DIR=$(dirname "$(realpath "$0")")/hooks
+
+# Runs all executables in $CUSTOM_HOOKS_DIR/hooks/$HOOKNAME.d and
+# $NATIVE_HOOKS_DIR/$HOOKNAME.local if existent.
+
+exitcode=
+for hook in "$CUSTOM_HOOKS_DIR/$HOOKNAME.d/"*; do
+ if [ -x "$hook" ]; then
+ "$hook" "$@" || exitcode=${exitcode:-$?}
+ fi
+done
+
+if [ -x "$NATIVE_HOOKS_DIR/$HOOKNAME.local" ]; then
+ "$NATIVE_HOOKS_DIR/$HOOKNAME.local" "$@" || exitcode=${exitcode:-$?}
+fi
+
+# shellcheck disable=SC2086
+exit $exitcode
diff --git a/tools/git/hooks/pre-commit.d/remember-update-pot.sh b/tools/git/hooks/pre-commit.d/remember-update-pot.sh
new file mode 100755
index 0000000..827242c
--- /dev/null
+++ b/tools/git/hooks/pre-commit.d/remember-update-pot.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+echo "Please remember to update the .pot file (tools/update-pot.sh) and the translation."
+
diff --git a/tools/git/hooks/pre-merge-commit.d/run-pre-commit-hook.sh b/tools/git/hooks/pre-merge-commit.d/run-pre-commit-hook.sh
new file mode 100755
index 0000000..b3c4912
--- /dev/null
+++ b/tools/git/hooks/pre-merge-commit.d/run-pre-commit-hook.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+. git-sh-setup
+if [ -x "$GIT_DIR/hooks/pre-commit" ]; then
+ exec "$GIT_DIR/hooks/pre-commit"
+fi
diff --git a/tools/git/init-hooks.sh b/tools/git/init-hooks.sh
new file mode 100755
index 0000000..b635da0
--- /dev/null
+++ b/tools/git/init-hooks.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+# Inspired by https://github.com/sjungwirth/githooks/blob/949d55e84e92dfd84e9a73a8b7624bb2e4dbc872/bin/git/init-hooks
+
+set -e
+
+SCRIPT_DIR=$(dirname "$0")
+NATIVE_HOOKS_DIR=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel)/.git/hooks
+CUSTOM_HOOKS_DIR=$SCRIPT_DIR/hooks
+HOOKS_WRAPPER=$(realpath -s --relative-to="$NATIVE_HOOKS_DIR" "$SCRIPT_DIR")/hooks-wrapper.sh
+
+cd "$CUSTOM_HOOKS_DIR"
+HOOK_DIRS=(*.d)
+
+for hook_dir in "${HOOK_DIRS[@]}"; do
+ hookname=${hook_dir:0:-2}
+ if [ ! -L "$NATIVE_HOOKS_DIR/$hookname" ]; then
+ if [ -f "$NATIVE_HOOKS_DIR/$hookname" ]; then
+ mv "$NATIVE_HOOKS_DIR/$hookname" "$NATIVE_HOOKS_DIR/$hookname.local"
+ fi
+ ln -s "$HOOKS_WRAPPER" "$NATIVE_HOOKS_DIR/$hookname"
+ fi
+done
diff --git a/tools/phpstan/composer.json b/tools/phpstan/composer.json
index 218fa72..ce2fee2 100644
--- a/tools/phpstan/composer.json
+++ b/tools/phpstan/composer.json
@@ -1,13 +1,14 @@
{
"require": {
- "phpstan/extension-installer": "^1.1",
- "phpstan/phpstan": "^1.7",
- "phpstan/phpstan-deprecation-rules": "^1.0",
- "phpstan/phpstan-phpunit": "^1.1",
- "phpstan/phpstan-strict-rules": "^1.2",
- "phpstan/phpstan-webmozart-assert": "^1.2",
- "thecodingmachine/phpstan-strict-rules": "^1.0",
- "voku/phpstan-rules": "^3.0"
+ "kcs/phpstan-strict-rules": "^2",
+ "phpstan/extension-installer": "^1.4",
+ "phpstan/phpstan": "^2",
+ "phpstan/phpstan-beberlei-assert": "^2",
+ "phpstan/phpstan-deprecation-rules": "^2",
+ "phpstan/phpstan-phpunit": "^2",
+ "phpstan/phpstan-strict-rules": "^2",
+ "phpstan/phpstan-webmozart-assert": "^2",
+ "voku/phpstan-rules": "^3.6"
},
"config": {
"allow-plugins": {
diff --git a/tools/update-pot.sh b/tools/update-pot.sh
new file mode 100755
index 0000000..d7e7c99
--- /dev/null
+++ b/tools/update-pot.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+set -euo pipefail
+
+readonly SCRIPT_PATH="$0"
+SCRIPT_NAME=$(basename "$SCRIPT_PATH")
+readonly SCRIPT_NAME
+SCRIPT_DIR=$(dirname "$SCRIPT_PATH")
+readonly SCRIPT_DIR
+
+usage() {
+ cat <