diff --git a/.wordlist.txt b/.wordlist.txt index c1c9880664..f1391ad9e0 100644 --- a/.wordlist.txt +++ b/.wordlist.txt @@ -521,6 +521,7 @@ IsNewCustomerRule ItemFacade ItemsFacade JSON +JUnit JVM JWT JavaScript diff --git a/products/tools/cli/validation.md b/products/tools/cli/validation.md index a2dea582d2..55d460d0f0 100644 --- a/products/tools/cli/validation.md +++ b/products/tools/cli/validation.md @@ -7,239 +7,303 @@ nav: ## Validation -Shopware CLI has built-in validation for extensions. This is useful in your CI/CD pipeline to validate the extension before you release it. +Shopware CLI has built-in validation for extensions. Run it during development and in CI/CD pipelines to find technical problems before uploading an extension version to the [Shopware Store](https://store.shopware.com/de/). -Some validation tools, especially when using `--full`, run PHP and Node.js tooling under the hood. The Docker examples are recommended because the image already contains the required runtime dependencies. If PHP and Node.js are available locally, you can run the `shopware-cli` commands directly instead. +Validation covers technical criteria that can be automated, such as metadata, packaging, static analysis, and linting. It is not a one-to-one replica of the complete Shopware Store review, which also includes functional testing, Store page content, and manual review. A successful validation run is a strong technical pre-upload signal, but it does not guarantee Store approval or mean that the CLI and Store review use an identical rule set. -## Validating an extension +Validation has two modes: + +- **Basic (default)**: Runs the built-in `sw-cli` checks, including metadata, icon, snippets, PHP linting, and packaging-related checks. It does not require a locally installed PHP or Node.js runtime. +- **Full (`--full`)**: Runs the basic checks plus validation tools such as PHPStan, ESLint, Stylelint, and the Administration and Storefront Twig linters. -To validate an extension, you can use the following command: +### Recommended setup: Docker - +Run Shopware CLI through the `ghcr.io/shopware/shopware-cli` Docker image for a consistent validation environment without managing the required runtimes on the host. The primary examples on this page use Docker. - +If you already run Shopware CLI directly in an existing development or CI environment, the same CLI commands continue to work. For full validation, the host environment must provide PHP 8.2 or newer, Node.js 20 or newer, Composer, and npm. -The command mounts your current directory to `/ext` inside the container and validates that mounted extension directory: +## Validating an extension + +From the extension directory, mount the current directory into the Shopware CLI container and validate it: ```shell docker run --rm -v "$(pwd)":/ext ghcr.io/shopware/shopware-cli extension validate /ext ``` - - - - -Use the local path to your extension when running the command without Docker: +If you run Shopware CLI directly on the host, use the corresponding local path: ```shell shopware-cli extension validate /path/to/your/extension ``` - +For direct CLI execution, relative paths are resolved from the current working directory. The command exits with a non-zero exit code if validation reports an error-level finding. Warnings are reported but do not fail the command. + +### Validating a directory or a zip file - +`extension validate` accepts both a source directory and a built zip file, and the two are not equivalent: -The path can be absolute or relative to the directory containing the extension or the zip file. The command exits with a non-zero exit code if the validation fails with an error-level message. +| Input | Behavior | +|-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Directory | Intended for feedback during development. `zip.disallowed_file` findings are automatically ignored for directory input. With `--full`, the files are copied to a temporary directory first unless `--no-copy` is used. | +| zip file | Validates the packaged artifact. Packaging-related validation is not automatically suppressed. | + +For day-to-day development, validate the source directory. Before uploading a release, validate the packaged zip so the checks run against the artifact you intend to submit. ## What is validated in basic mode? -- The `composer.json` has a `shopware/core` requirement and the constraint is parsable -- The extension metadata is filled with: - - `name` - - `label` (German and English) - - `description` (German and English) and longer than 150 characters and shorter than 185 characters -- PHP can be correctly linted with the minimum PHP version -- The `theme.json` can be parsed and included assets can be found -- All snippet files contain the same set of translation keys +Basic mode runs the `sw-cli` tool. It includes checks such as the following; this list is not exhaustive. The identifier in brackets is the value you can use in [validation ignores](#validation-ignores). -## Supported PHP versions for linting +Metadata and extension structure checks include: -The following PHP versions are supported for linting: +- The extension version and name are present and valid (`metadata.version`, `metadata.name`) +- The Shopware version constraint can be determined (`metadata.shopware_version`) +- Plugin Composer metadata such as type, description, authors, requirements, autoloading, labels, descriptions, manufacturer links, and support links is validated +- App metadata such as author, copyright, license, and development-only setup secrets is validated +- The extension icon exists and meets the supported size and dimension requirements (`metadata.icon`, `metadata.icon.size`) +- `theme.json` can be parsed and referenced assets can be found +- Administration and Storefront snippet files contain matching translation keys +- PHP source files are linted (`php.linter`) +- Deprecated `Resources/config/services.xml` and `Resources/config/routes.xml` are reported as warnings. `shopware-cli extension fix` can convert them to YAML +- Apps are checked for disallowed PHP and Twig files (`zip.disallowed_php_file`, `zip.disallowed_twig_file`) -- 7.3 -- 7.4 -- 8.1 -- 8.2 +Zip validation also runs packaging-related checks that are suppressed for directory input. -These versions don't need to be installed locally; they are downloaded on demand and executed using WebAssembly without any dependencies. +### Supported PHP versions for linting -## Running all validation tools +Shopware CLI uses an embedded Go-based PHP linter. It does not download or execute PHP runtimes for basic PHP linting. -By default, validate runs basic checks: extension metadata, some linting, and common mistakes. Use the `--full` option to run comprehensive checks with all available tools: phpstan for PHP static analysis, ESLint for JavaScript, and more. This will thoroughly validate your extension against the latest Shopware version. +The underlying linter supports PHP language profiles from PHP 7.2 through PHP 8.5, with PHP 8.6 available as a preview profile. Shopware CLI currently normalizes a derived PHP 7.2 profile to PHP 7.3 for linting. - +By default, Shopware CLI derives the PHP language profile from the extension's Shopware version constraint. To select a specific profile instead, set `validation.php_version` in `.shopware-extension.yml`: - +```yaml +validation: + php_version: '8.4' +``` -The command mounts your current directory to `/ext` inside the container and validates that mounted extension directory: +## Running full validation + +Use `--full` to add the additional validation tools to the built-in checks: ```shell docker run --rm -v "$(pwd)":/ext ghcr.io/shopware/shopware-cli extension validate --full /ext ``` - - - - -Use the local path to your extension when running the command without Docker: +If you run Shopware CLI directly on the host, the equivalent command is: ```shell shopware-cli extension validate --full /path/to/your/extension ``` - - - - -By default, it will check against the latest allowed Shopware version according to your constraints in `composer.json`. - -:::info -It's recommended to run the check against the lowest and highest allowed version, so you can be sure that your extension is compatible with all versions. This is critical because extensions often pass validation on the latest version but fail on the lowest supported version due to API changes and deprecations. -::: - -You can do this by using the `--check-against` option: - - +For direct execution, Shopware CLI prepares a cached tool directory for the CLI version. On first use it installs the PHP tool dependencies with Composer and the JavaScript tool dependencies with npm. If the validated extension has no `vendor` directory, full validation also resolves its Composer dependencies; packages listed under `suggest` are included so optional integrations can be analyzed. Private Composer packages require appropriate Composer authentication. - - -The command mounts your current directory to `/ext` inside the container and validates that mounted extension directory: +By default, Composer dependency resolution uses the highest versions allowed by the extension constraints. To test the other end of the supported range, use `--check-against lowest`: ```shell docker run --rm -v "$(pwd)":/ext ghcr.io/shopware/shopware-cli extension validate --full /ext --check-against lowest docker run --rm -v "$(pwd)":/ext ghcr.io/shopware/shopware-cli extension validate --full /ext --check-against highest ``` - - - - -Use the local path to your extension when running the command without Docker: +If you run Shopware CLI directly: ```shell shopware-cli extension validate --full /path/to/your/extension --check-against lowest shopware-cli extension validate --full /path/to/your/extension --check-against highest ``` - +With `--check-against lowest`, Composer adds `--prefer-lowest` when it resolves the extension dependencies. + +:::warning +Dependency resolution only runs when the validated copy does not already contain a `vendor` directory. If `vendor` is present, both `lowest` and `highest` reuse those installed dependencies instead of resolving a new dependency set. Use a clean validation input when you want the two commands to exercise both ends of the supported dependency range. +::: - +### Reporters -The check command has multiple reporting options, you can use `--reporter` to specify the output format. The following formats are supported: +Use `--reporter` to specify the output format: | Format | Description | |------------|-----------------------------------------| -| `summary` | default list of all errors and warnings | -| `json` | json output | -| `junit` | junit output | +| `summary` | List of errors and warnings | +| `json` | JSON output | +| `junit` | JUnit output | | `github` | GitHub Actions output | -| `markdown` | markdown output | - -## Running Specific Tools +| `gitlab` | GitLab Code Quality output | +| `markdown` | Markdown output | -Instead of running all tools, you can choose to run specific tools using the `--only` flag. The following tools are available: +If `--reporter` is not set, the format is detected automatically: `github` in GitHub Actions, `gitlab` in GitLab CI, and `summary` otherwise. -| Tool | Description | -|----------------|--------------------------------| -| `phpstan` | PHP static analysis | -| `sw-cli` | Shopware CLI validation checks | -| `stylelint` | CSS/SCSS linting | -| `admin-twig` | Admin Twig template checks | -| `php-cs-fixer` | PHP code style fixing | -| `prettier` | Code formatting | -| `eslint` | JavaScript/TypeScript linting | -| `rector` | PHP code refactoring | +## Running specific validation tools -You can run a single tool: +With `--full`, `extension validate` calls the validation check implemented by each registered tool. The tools that currently add validation findings are: - +| Tool | Validation performed | +|-------------------|----------------------------------------------------------------------------------| +| `sw-cli` | Built-in Shopware CLI checks for metadata, snippets, structure, and packaging | +| `phpstan` | PHP static analysis; skipped for apps because they do not have a `composer.json` | +| `eslint` | JavaScript and TypeScript linting | +| `stylelint` | CSS/SCSS linting | +| `admin-twig` | Administration Twig checks | +| `storefront-twig` | Storefront Twig checks | - +The shared tool registry also contains `rector`, `php-cs-fixer`, `prettier`, and `symfony-xml`. These tools currently do not add findings during `extension validate`; they are used by `extension fix` or `extension format` instead. -The command mounts your current directory to `/ext` inside the container and validates that mounted extension directory: +You can run only selected validation tools: ```shell docker run --rm -v "$(pwd)":/ext ghcr.io/shopware/shopware-cli extension validate --full /ext --only phpstan ``` -Or run multiple tools by separating them with commas: +Or run multiple validation tools by separating them with commas: ```shell docker run --rm -v "$(pwd)":/ext ghcr.io/shopware/shopware-cli extension validate --full /ext --only "phpstan,eslint,stylelint" ``` - +If you run Shopware CLI directly, use the same flags with the local extension path. - +The inverse is `--exclude`, which runs all registered tools except the listed names: -Use the local path to your extension when running the command without Docker: +```shell +docker run --rm -v "$(pwd)":/ext ghcr.io/shopware/shopware-cli extension validate --full /ext --exclude "eslint,stylelint" +``` + +Both flags accept a comma-separated list and fail with an error if a tool name does not exist. + +### Running without copying the sources + +With `--full`, a directory input is copied to a temporary directory before the tools run. Use `--no-copy` to run directly in the mounted source directory instead: ```shell -shopware-cli extension validate --full /path/to/your/extension --only phpstan +docker run --rm -v "$(pwd)":/ext ghcr.io/shopware/shopware-cli extension validate --full --no-copy /ext ``` -Or run multiple tools by separating them with commas: +For direct CLI execution, the equivalent option is: ```shell -shopware-cli extension validate --full /path/to/your/extension --only "phpstan,eslint,stylelint" +shopware-cli extension validate --full --no-copy /path/to/your/extension ``` - +This can be faster on large extensions and keeps generated dependency or cache files in the source directory, but it also means validation tools can modify or add files there. + +## Checking a release before uploading it to the Store - +For the strongest pre-upload signal available from Shopware CLI, validate the same package that you intend to upload with `--full`. This runs the full validator set used by the CLI against the packaged artifact. It does not guarantee that every Store-review criterion is represented in the CLI or that passing validation guarantees Store approval. -This is particularly useful when: +`extension package` is the current packaging command. `extension zip` remains available as a deprecated alias. -- You want to focus on specific aspects of your code -- You want to run only the relevant tools for the files you've changed -- You want to fix issues one tool at a time +From the extension root, create a release package with a predictable filename: + +```shell +mkdir -p dist +docker run --rm -v "$(pwd)":/ext -w /ext ghcr.io/shopware/shopware-cli extension package /ext --release --output-directory /ext/dist --filename extension.zip +``` + +Then validate the packaged artifact: + +```shell +docker run --rm -v "$(pwd)":/ext ghcr.io/shopware/shopware-cli extension validate --full /ext/dist/extension.zip +``` + +If you already run Shopware CLI directly, the corresponding workflow is: + +```shell +shopware-cli extension package /path/to/your/extension --release --output-directory dist --filename extension.zip +shopware-cli extension validate --full dist/extension.zip +``` + +See [Building Extensions and Creating Archives](./extension-commands/build.md) for packaging options and [Releasing an extension to the Shopware Store](./shopware-account-commands/releasing-extension-to-shopware-store.md) for the upload itself. + +In GitHub Actions, you can use the same Docker image rather than maintaining PHP and Node.js setup steps on the runner: + +```yaml +name: Validate extension +on: [push, pull_request] + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Validate extension + run: docker run --rm -v "$GITHUB_WORKSPACE":/ext ghcr.io/shopware/shopware-cli extension validate --full /ext + + validate-release: + if: startsWith(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Create release package + run: | + mkdir -p dist + docker run --rm -v "$GITHUB_WORKSPACE":/ext -w /ext ghcr.io/shopware/shopware-cli extension package /ext --release --output-directory /ext/dist --filename extension.zip + - name: Validate release package + run: docker run --rm -v "$GITHUB_WORKSPACE":/ext ghcr.io/shopware/shopware-cli extension validate --full /ext/dist/extension.zip +``` + +The `github` reporter is selected automatically in GitHub Actions, so findings are emitted as GitHub annotations. + +:::warning +Local or CI validation cannot replace the Store review completely. It does not install and functionally test the extension in a shop, verify all Store listing content, or cover manual review criteria. A version that passes validation can still be rejected for those reasons. +::: ## Validation ignores -If you want to ignore errors or warnings, you can create a `.shopware-extension.yaml` file in your extension root with the following content: +To ignore selected errors or warnings for an extension, create a `.shopware-extension.yml` file in the extension root: ```yaml validation: ignore: - # Ignore all errors by identifier + # Ignore all findings with this identifier - identifier: 'Shopware.XXXXXX' - # Ignore all errors by identifier and path + # Ignore findings with this identifier and path - identifier: 'Shopware.XXXXXX' path: 'path/to/file.php' - # Ignore all errors by message and path + # Ignore findings containing this message and matching this path - message: 'Some error message' path: 'path/to/file.php' - # Ignore all errors by message + # Ignore findings containing this message - message: 'Some error message' ``` -::: info -Validation ignores are only applied to the `validate` command. The `fix` command does not respect the `validation.ignore` rules and will still apply fixes even when an ignore would match (by identifier or message). This is a known limitation. -::: +The identifier of a finding is shown in the validation output. + +Validation ignores are applied by the `validate` command. `extension fix` does not use `validation.ignore` to decide which fixes to apply. + +When a directory rather than a zip file is validated, `zip.disallowed_file` findings are automatically ignored independently of your configuration. ## Scanning a project -It's possible to scan an entire project instead of just a single extension. This is useful if you want to check all extensions in your project at once. You can do this by passing the path to the project root instead of the extension path. +Use the dedicated project validation command to scan a Shopware project instead of a single extension: -All config files like `phpstan.neon` and `.php-cs-fixer.dist.php` should be placed in the project root for proper configuration or to override the default settings. The Verifier will automatically detect the config files and use them for the checks. +```shell +docker run --rm -v "$(pwd)":/project -w /project ghcr.io/shopware/shopware-cli project validate /project +``` -Ignoring errors works similarly to extensions; in that case, you can create a `.shopware-project.yaml` file in your project root with the same syntax. +If you run Shopware CLI directly: -## Common issues +```shell +shopware-cli project validate /path/to/your/project +``` -### Fixer does nothing for Shopware 6.7 +`project validate` discovers project extensions and configured bundles and runs the registered validation tools against the project. Project-level validation settings are read from `.shopware-project.yml` under `validation`. -The fixers are enabled by the supported Shopware Version in the plugins `composer.json` file. For 6.7, you should change the composer constraint to this: +For example, project validation ignores using the same identifier, path, and message fields: -```json -{ - "minimum-stability": "dev", - "require": { - "shopware/core": "~6.7.0" - } -} +```yaml +validation: + ignore: + - identifier: 'phpstan/some.identifier' + path: 'custom/plugins/MyPlugin/src/Example.php' ``` +You can also exclude extensions from project validation with `validation.ignore_extensions` in `.shopware-project.yml`. + +## Common issues + ### Missing classes in a Storefront/Elasticsearch bundle -Your plugin typically requires only `shopware/core`, but when you use classes from Storefront or the Elasticsearch Bundle, and they are required, you have to add `shopware/storefront` or `shopware/elasticsearch` also to the `require` in the composer.json. If those features are optional with `class_exists` checks, you want to add them into `require-dev`, so the dependencies are installed only for development and PHPStan can recognize the files. +Your plugin typically requires only `shopware/core`, but when you use classes from Storefront or the Elasticsearch Bundle and they are required, add `shopware/storefront` or `shopware/elasticsearch` to `require` in `composer.json`. If those integrations are optional and guarded by checks such as `class_exists`, add the packages to `require-dev` so PHPStan can resolve the classes during development. + +### PHPStan uses my own configuration instead of the Shopware one + +If `phpstan.neon`, `phpstan.neon.dist`, or `phpstan.dist.neon` exists in the validated root directory, PHPStan uses it instead of the default configuration shipped with Shopware CLI. Remove or rename the file if you want to validate with the default rule set.