{"id":32800158,"url":"https://github.com/systopia/civicrm-extension-template","last_synced_at":"2026-05-10T07:35:24.299Z","repository":{"id":322241731,"uuid":"1081776173","full_name":"systopia/civicrm-extension-template","owner":"systopia","description":"A template for CiviCRM extensions with configuration for QA tools, GitHub workflows, and Composer, as well as scripts for preparing releases.","archived":false,"fork":false,"pushed_at":"2025-11-03T10:43:46.000Z","size":94,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-11-03T12:19:11.652Z","etag":null,"topics":["civicrm","civicrm-extension","civicrm-extensions","github","github-workflow","github-workflows","phpcbf","phpcs","phpstan","phpunit"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/systopia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-23T09:16:59.000Z","updated_at":"2025-11-03T10:43:43.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/systopia/civicrm-extension-template","commit_stats":null,"previous_names":["systopia/civicrm-extension-template"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/systopia/civicrm-extension-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/systopia%2Fcivicrm-extension-template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/systopia%2Fcivicrm-extension-template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/systopia%2Fcivicrm-extension-template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/systopia%2Fcivicrm-extension-template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/systopia","download_url":"https://codeload.github.com/systopia/civicrm-extension-template/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/systopia%2Fcivicrm-extension-template/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":282941521,"owners_count":26752599,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-06T02:00:06.180Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["civicrm","civicrm-extension","civicrm-extensions","github","github-workflow","github-workflows","phpcbf","phpcs","phpstan","phpunit"],"created_at":"2025-11-06T04:00:20.471Z","updated_at":"2026-05-10T07:35:24.288Z","avatar_url":"https://github.com/systopia.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CiviCRM Extension Template\n\nThis directory contains some files that can be used for CiviCRM extensions.\n\nIt provides configurations for\n[PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer),\n[PHPStan](https://phpstan.org/),\nand [PHPUnit](https://phpunit.de/) (via\n[Symfony PHPUnit Bridge](https://symfony.com/doc/current/components/phpunit_bridge.html)).\nAdditionally there are workflows to run this tools in GitHub actions. The\nworkflows are configured to run on `git push` (might be changed).\n\n(Note: The tools are installed in individual directories to avoid potential\nconflicting requirements.)\n\nApart from that it contains the basic files to start a [documentation with MkDocs](#documentation-with-mkdocs).\n\nIn addition there are the following helper scripts:\n\n* [`tools/create-release.sh`](`tools/create-release.sh`) Helps to create a new release of the extension.\n* [`tools/update-pot.sh`](`tools/update-pot.sh`) Extracts translatable strings and updates the `.pot` file.\n\nSee the help of the scripts for more details.\n\nThis template allows to add `civicrm/civicrm-core` as requirement to the\n`composer.json` to specify the minimum required CiviCRM version as well as other\nCiviCRM extensions that are available as composer package. This makes it\npossible to install a CiviCRM extension via `composer` with all its\ndependencies. When running `composer update` in the extension directory itself,\nthe installation of `civicrm/civicrm-core` and CiviCRM extensions is\n[prevented](ComposerHelper.php.template).\n\n## Installation template\n\n### Install from scratch/update\n\nTo install/update the files from this template into an existing or newly\ncreated CiviCRM extension first make sure that the `info.xml` is up to date.\nThen run:\n\n```shell\n./install.sh \u003cextension directory\u003e [\u003cfile or directory in extension template\u003e ...]\n```\n\nThis will copy all non template files (excluding this file and `install.sh`\nitself) to the extension directory. In files with the extension `.template`\nthe placeholders will be replaced appropriately and the extension will be\ndropped. If files are specified only those will be installed.\n\nOnly the file `phpstan.neon.template` won't be renamed, but just copied.\n\nIf a file already exists you'll be asked how to proceed. So it is safe to run\nthis script in any case. You might also want to run this script after a file of\nthis template has been updated.\n\n(Hint: You might want to use the option `--update`. See script usage for\ndetails via `install.sh --help`.)\n\nThe script automatically executes\n[`tools/git/init-hooks.sh`](`tools/git/init-hooks.sh`) to initialize the git\nhooks.\n\nAfter running `install.sh`:\n\n* Change the vendor name *systopia* in `composer.json` if necessary.\n* Copy `phpstan.neon.template` to `phpstan.neon` and replace the placeholder\n  `{VENDOR_DIR}` with the vendor-path of the root composer project.\n  * If you installed CiviCRM without `composer` (e.g. as WordPress plugin), make \n    sure to use the alternative parameters section and replace `{CIVICRM_DIR}`\n    with the path to the CiviCRM installation directory.\n* Adapt `php-versions` in `.github/workflows/phpstan.yml`\n  * Recommendation: Earliest and latest supported minor version of each\n    supported major version.\n* If elements from `civicrm/civicrm-packages` are used in your extension,\n  `scanFiles` or `scanDirectories` in the phpstan configuration might need to be\n  adapted.\n* Adjust the directories to analyze in `phpstan.neon.dist` and `phpcs.xml.dist`.\n  * Remove directories if not existent, e.g. `api`.\n  * Add directories like `ang` if used. (Note: The directory `managed` usually\n    should be added only to `phpstan.neon.dist` because the code exported by\n    CiviCRM doesn't match all rules in `phpcs.xml.dist`.)\n* If you have (or plan to have) dependencies in the extension's `composer.json`\n  add the following code to `{EXT_SHORT_NAME}.php`:\n\n  ```php\n  function _{EXT_SHORT_NAME}_composer_autoload(): void {\n    if (file_exists(__DIR__ . '/vendor/autoload.php')) {\n      require_once __DIR__ . '/vendor/autoload.php';\n    }\n  }\n  ```\n\n  Call this function at the beginning of `{EXT_SHORT_NAME}_civicrm_config()` and\n  `{EXT_SHORT_NAME}_civicrm_container()` (if used).\n\nAdditionally, in some cases it makes sense to replace `README.md` with a symlink\nto `docs/index.md`. (Usually if both files would contain basically the same\ninformation.)\n\n### Installation tools\n\nNow install the different tools (might be run later for updates of the tools as\nwell):\n\n```shell\ncomposer composer-tools update\n```\n\n## Alternative: (Re)Activate after cloning repo\n\nIf this template already exists in a freshly cloned repository, you need to\ninitialize the git hooks and install the various tools.\n\nTo initialize the git hooks execute\n```shell\n./tools/git/init-hooks.sh\n```\n\nThe following steps are necessary in order to get the `composer-tools` running\nagain:\n\n* Copy `phpstan.neon.template` to `phpstan.neon` and replace the placeholder\n  `{VENDOR_DIR}` with the vendor-path of the root composer project.\n\nInstall all project dependencies, that are listed in the repos `composer.json`\nfile (if there are any) either in the extension directory or in the root\n`composer.json`. They are necessary for `phpstan` to resolve symbols of external\nlibrary code:\n\n```shell\ncomposer update # or add requirements to root composer.json\n```\n\nInstall `composer-tools` in order to locally run `phpcs`, `phpcbf`, `phpstan`\nand `phpunit`.\n\n```shell\ncomposer composer-tools update\n```\n\nMake sure that in your local CiviCRM instance, all those extensions have been\ninstalled, that the current project is depending on (if there are any).\nOtherwise, `phpstan` may complain about missing symbols.\n\n## Run tools\n\nRun the tests with `composer test` or each tool on its own:\n\n```shell\ncomposer phpcs\ncomposer phpstan\ncomposer phpunit\n```\n\nTo fix code style issues with `phpcbf` run `composer phpcbf`.\n\n## Recommendations for existing extensions\n\nIf you add this template to an existing extension it might lead to many\nerrors and  warnings that you cannot handle immediately. Here you can find\nsome recommendations for that case. Make sure the tests run successfully before\nenabling them in the CI system (GitHub Actions).\n\n### Initial handling of code style violations\n\nRecommended approach:\n\n1. Fix style violations automatically with `composer phpcbf`.\n1. Run `composer phpcs` and fix the remaining issues by hand.\n1. If there are still too many issues to handle immediately:\n   * [Ignore parts of\n    files](https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki/Advanced-Usage#ignoring-parts-of-a-file)\n    (be specific about ignored sniffs, if possible).\n    * Exclude files or directories from validation in `phpcs.xml.dist`.\n\nIn general it should be shortly mentioned when validation is disabled for that\nreason.\n\n### Initial handling of phpstan errors\n\nRecommended approach:\n\n1. Run phpstan with the lowest level (`composer run -- phpstan --level=0`) and\n  fix the reported errors.\n1. Gradually increase the level and fix the reported errors until the\n  important issues are fixed or the number of messages is overwhelming.\n1. Create a [baseline](https://phpstan.org/user-guide/baseline) for the\n   remaining errors:\n   * Run `composer run -- phpstan --generate-baseline`.\n   * Include `phpstan-baseline.neon` in `phpstan.neon.dist`:\n   ```\n   includes:\n  \t- phpstan-baseline.neon\n   ```\n\nConsider opening an issue saying that errors in the baseline should be checked.\n\n## Dealing with errors\n\nIf any tool reports an error or warning it has to be resolved!\n\nIn case it's a false positive or there exists no practicable way to resolve it,\nerrors can be ignored.\n\n### Dealing with code style errors\n\nErrors reported by phpcs can be ignored with `// phpcs:ignore \u003csniffs\u003e` above\nthe problematic line. See [the\nmanual](https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki/Advanced-Usage#ignoring-parts-of-a-file)\nfor more details. If you make use of `// phpcs:disable \u003csniffs\u003e` always enable\nthe sniffs again with `// phpcs:enable`. (Always state the ignored sniffs.)\n\n### Dealing with phpstan errors\n\nErrors reported by phpstan can be ignored with `// @phpstan-ignore \u003cerror\nidentifiers\u003e` above the problematic line. See [the\nmanual](https://phpstan.org/user-guide/ignoring-errors) for more details.\n(Always state the ignored error identifiers and don't use\n`// @phpstan-ignore-next-line`.)\n\nAdd an explanation if it's not obvious why an error is ignored.\n\nIn some cases it might make sense to ignore errors in the `phpstan.neon.dist`\n[configuration\nfile](https://phpstan.org/user-guide/ignoring-errors#ignoring-in-configuration-file).\nThough the ignored errors should always be as specific as possible, i.e. actual\nissues must never be covered that way.\n\n## Testing GitHub actions locally\n\nIt is possible to test GitHub actions for phpcs and phpstan locally using\n[`nektos/act`](https://github.com/nektos/act) and\n[`shivammathur/node`](https://github.com/shivammathur/node-docker) docker\nimages:\n\n```shell\nact -P ubuntu-latest=shivammathur/node:latest -j phpcs\nact -P ubuntu-latest=shivammathur/node:latest -j phpstan\n# Network \"bridge\" is necessary to start MySQL service in the container.\n# (act uses host network by default.)\nact -P ubuntu-latest=shivammathur/node:latest -j phpunit --network bridge\n```\nIt is possible to limit the matrix like this:\n\n```shell\nact workflow_dispatch -P ubuntu-latest=shivammathur/node:latest -j phpstan --matrix php-versions:8.4 --matrix prefer:prefer-stable\n```\n\nThe phpstan and phpunit workflows allow to specify a composer version\nconstraint for CiviCRM in the `workflow_dispatch` trigger:\n\n```shell\nact workflow_dispatch -P ubuntu-latest=shivammathur/node:latest -j phpunit --network bridge --input civicrm-version=\"6.7.8\"\n```\n\n`act` might be installed via [Homebrew](https://brew.sh/).\n\n## Usage in PhpStorm\n\nPhpStorm allows only one phpcs and phpstan configuration per project. If you\nhave a project with multiple CiviCRM extensions you might use the scripts in\nhttps://github.com/systopia/phpstorm-scripts\n\n## GitHub Actions: Depending on other CiviCRM extensions\n\nIf the CiviCRM extension depends on other extensions they have to be installed\nwhen running phpunit and also phpstan, if it directly uses code from another\nextension. The following describes how to handle that case. The placeholder\n`{OTHER}` represents the name of the other extension.\n\n### phpstan\n\nWhen running as GitHub action the code of the other extension has to be\navailable. In the best case they can be installed via composer. Otherwise, the\n`run` part of the *Install dependencies* step in `.github/workflows/phpstan.yml`\nhas to be modified accordingly. To use the base branch of an extension developed\non GitHub the additional line would look like this:\n```shell\ngit clone --depth=1 https://github.com/{ORGANIZATION}/{OTHER}.git ../{OTHER}\n```\n\nTo test with different versions when a dependent extension cannot be installed\nvia composer `${{ matrix.prefer }}` could be checked:\n```shell\nif [ \"${{ matrix.prefer }}\" = \"prefer-lowest\" ]; then\n  git clone ...\nelse\n  git clone ...\nfi\n```\n\n* Add `../{OTHER}` to `scanDirectories` in `phpstan.neon.dist`.\n\nNote: Depending on which code is used it might be enough to only scan a\nsubfolder of the other extension e.g. `Civi`.\n\n### phpunit\n\nIt has to be ensured that dependent extensions are available in the `ext` folder\nwhen running phpunit. In the best case extensions can be installed via\ncomposer. Otherwise, the `run` part of the *Set up CiviCRM* step in\n`.github/workflows/phpunit.yml` has to be modified accordingly. For example\nan extension can be fetched with `cv ext:download`:\n```shell\ncv ext:download \"{OTHER}@https://github.com/{ORGANIZATION}/{OTHER}/releases/download/${OTHER}_VERSION/{OTHER}-${OTHER}_VERSION.zip\"\n```\n\nIn this example the version is defined in the environment variable\n`{OTHER}_VERSION`.\n\nTo test with different versions when a dependent extension cannot be installed\nvia composer `${{ matrix.prefer }}` could be checked:\n```shell\nif [ \"${{ matrix.prefer }}\" = \"prefer-lowest\" ]; then\n  {OTHER}_VERSION=1.0.0\nelse\n  {OTHER}_VERSION=1.2.3\nfi\n```\n\n## Documentation with MkDocs\n\nBasic files for a documentation with [MkDocs](https://www.mkdocs.org/) are\ninstalled by this template.\n\nWhen editing the documentation you can use `mkdocs` to verify the changes.\nEnsure you have `mkdocs` installed (`apt install mkdocs`). Then you can run\n`mkdocs serve` in the extension directory and open the URL printed on the\nconsole. Changes will be applied immediately to the served website.\n\nHow to get the documentation up on [docs.civicrm.org](https://docs.civicrm.org/)\nis explained in the [developer\nguide](https://docs.civicrm.org/dev/en/latest/extensions/documentation/#submit).\n\n## Files to adapt if minimal PHP or CiviCRM version changes\n\n### Change of minimal PHP version\n\nThe following files have to be adapted accordingly if the minimal PHP version\nchanges:\n\n* `info.xml`:\n  [`\u003cphp_compatibility\u003e`](https://docs.civicrm.org/dev/en/latest/extensions/info-xml/#php_compatibility)\n  (if used)\n* `composer.json`: `composer require --no-update php:^{VERSION}`\n* `.github/workflows/phpstan.yml`\n* `.github/workflows/phpunit.yml`\n\n### Change of minimal CiviCRM version\n\nThe following files have to be adapted accordingly if the minimal CiviCRM\nversion changes:\n\n* `info.xml`: [`\u003ccompatiblity\u003e`](https://docs.civicrm.org/dev/en/latest/extensions/info-xml/#compatibility)\n* `composer.json`:\n  `composer require --no-update civicrm/civicrm-core:\u003e={VERSION} civicrm/civicrm-packages:\u003e={VERSION}`\n\n## Files to adapt if a new version of PHP is released\n\nTo test with the latest version of PHP the following files have to be adapted\naccordingly, if a new version of PHP (minor or major) is released:\n\n* `.github/workflows/phpstan.yml`\n* `.github/workflows/phpunit.yml`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsystopia%2Fcivicrm-extension-template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsystopia%2Fcivicrm-extension-template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsystopia%2Fcivicrm-extension-template/lists"}