Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/smoke-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
run: shopware-cli extension package plugin --disable-git --release

- name: Validate Plugin
run: shopware-cli extension validate FroshTools.zip
run: shopware-cli extension validate FroshTools.zip --only sw-cli
Comment thread
MalteJanz marked this conversation as resolved.

- name: Get Changelog
run: shopware-cli extension get-changelog FroshTools.zip
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,4 @@ The verifier registers tools through the name-only `Tool` interface. `CheckTool`
- **Fixers**: Rector, ESLint, Stylelint, Symfony XML conversion
- **Formatters**: PHP-CS-Fixer, Prettier

`extension validate` defaults to `sw-cli`; `--full` selects all checkers, and an explicit `--only` overrides that default. The extension commands report whether each tool was invoked or skipped; invocation does not guarantee that files were analyzed or changed.
`extension validate` runs all checkers by default. The deprecated `--full` flag remains accepted but has no effect; use `--only` or `--exclude` to select checkers. The extension commands report whether each tool was invoked or skipped; invocation does not guarantee that files were analyzed or changed.
2 changes: 1 addition & 1 deletion architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ A new command is just a new file: drop `cmd/<group>/<group><sub>.go` (`cmd/root.

Each verifier tool registers a name and implements only the capabilities it supports: checking, fixing, formatting, or a combination. Commands select tools by capability, then apply `--only` and (where available) `--exclude`. An unsupported `--only` name is an error that lists the tools available to that command.

`extension validate` runs only the built-in `sw-cli` checker by default; `--full` selects all checkers, while an explicit `--only` takes precedence over `--full`. The format commands support `--dry-run`; the fix commands do not.
`extension validate` runs all checkers by default. The deprecated `--full` flag remains accepted but has no effect; use `--only` or `--exclude` to select checkers. The format commands support `--dry-run`; the fix commands do not.

```go
type Tool interface {
Expand Down
24 changes: 7 additions & 17 deletions cmd/extension/extension_validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ var extensionValidateCmd = &cobra.Command{
Short: "Validate extension metadata, assets, and code quality",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
isFull, _ := cmd.Flags().GetBool("full")
storeCompliance, _ := cmd.Flags().GetBool("store-compliance")
reportingFormat, err := extensionValidationFormat(cmd)
if err != nil {
Expand All @@ -34,7 +33,7 @@ var extensionValidateCmd = &cobra.Command{
exclude, _ := cmd.Flags().GetString("exclude")
noCopy, _ := cmd.Flags().GetBool("no-copy")

tools, statuses, err := selectExtensionValidationTools(isFull, only, exclude)
tools, statuses, err := selectExtensionValidationTools(only, exclude)
if err != nil {
return err
}
Expand Down Expand Up @@ -133,14 +132,10 @@ var extensionValidateCmd = &cobra.Command{
},
}

func selectExtensionValidationTools(full bool, only, exclude string) (verifier.ToolList[verifier.CheckTool], []validation.ToolInvocationStatus, error) {
func selectExtensionValidationTools(only, exclude string) (verifier.ToolList[verifier.CheckTool], []validation.ToolInvocationStatus, error) {
validationTools := verifier.GetToolsOf[verifier.CheckTool]()

requested := only
if requested == "" && !full {
requested = "sw-cli"
}
requestedTools, err := validationTools.Only(requested)
requestedTools, err := validationTools.Only(only)
if err != nil {
return nil, nil, err
}
Expand All @@ -152,13 +147,7 @@ func selectExtensionValidationTools(full bool, only, exclude string) (verifier.T
return nil, nil, errors.New("no validation checks selected after applying --exclude")
}

statuses := extensionToolInvocationStatuses(validationTools, requestedTools, selected)
for i := range statuses {
if only == "" && statuses[i].Reason == "not selected by --only" {
statuses[i].Reason = "not selected; use --full or --only"
}
}
return selected, statuses, nil
return selected, extensionToolInvocationStatuses(validationTools, requestedTools, selected), nil
}

func requiresToolSetup(tool verifier.CheckTool) bool {
Expand All @@ -185,15 +174,16 @@ func extensionValidationFormat(cmd *cobra.Command) (string, error) {

func init() {
extensionRootCmd.AddCommand(extensionValidateCmd)
extensionValidateCmd.PersistentFlags().Bool("full", false, "Run all validation checks by default (minus --exclude selections)")
extensionValidateCmd.PersistentFlags().Bool("full", false, "Run all validation checks")
Comment thread
MalteJanz marked this conversation as resolved.
extensionValidateCmd.PersistentFlags().Bool("store-compliance", false, "Run the Extension Store compliance checks")
extensionValidateCmd.PersistentFlags().String("format", "", "Reporting format (summary, json, github, gitlab, junit, markdown)")
extensionValidateCmd.PersistentFlags().String("reporter", "", "Reporting format (summary, json, github, gitlab, junit, markdown)")
extensionValidateCmd.PersistentFlags().String("check-against", "highest", "Check against Shopware Version (highest, lowest)")
extensionValidateCmd.PersistentFlags().String("only", "", "Run only these validation checks, regardless of --full (comma-separated, e.g. phpstan,eslint)")
extensionValidateCmd.PersistentFlags().String("only", "", "Run only these validation checks (comma-separated, e.g. phpstan,eslint)")
extensionValidateCmd.PersistentFlags().String("exclude", "", "Exclude specific tools by name (comma-separated, e.g. phpstan,eslint)")
extensionValidateCmd.PersistentFlags().Bool("no-copy", false, "Do not copy extension files to temporary directory")
extensionValidateCmd.MarkFlagsMutuallyExclusive("format", "reporter")
_ = extensionValidateCmd.PersistentFlags().MarkDeprecated("full", "all validation checks now run by default; omit --full; to restore old behaviour use --only sw-cli")
_ = extensionValidateCmd.PersistentFlags().MarkDeprecated("reporter", "use --format instead")
_ = extensionValidateCmd.PersistentFlags().MarkHidden("reporter")
extensionValidateCmd.PreRunE = func(cmd *cobra.Command, args []string) error {
Expand Down
52 changes: 22 additions & 30 deletions cmd/extension/extension_validate_selection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,17 @@ func toolStatusByName(t *testing.T, statuses []validation.ToolInvocationStatus,
}

func TestExtensionValidationSelection(t *testing.T) {
t.Run("default runs only sw-cli", func(t *testing.T) {
tools, statuses, err := selectExtensionValidationTools(false, "", "")
t.Run("default runs all checkers", func(t *testing.T) {
tools, statuses, err := selectExtensionValidationTools("", "")
require.NoError(t, err)
assert.Equal(t, []string{"sw-cli"}, toolNamesForValidation(tools))
assert.False(t, slices.ContainsFunc(tools, requiresToolSetup))
assert.Equal(t, "not selected; use --full or --only", toolStatusByName(t, statuses, "phpstan").Reason)
assert.Len(t, tools, 5)
assert.Len(t, statuses, len(tools))
assert.True(t, slices.ContainsFunc(tools, requiresToolSetup))
assert.Equal(t, "invoked", toolStatusByName(t, statuses, "phpstan").Status)
})

t.Run("only phpstan works without full", func(t *testing.T) {
tools, statuses, err := selectExtensionValidationTools(false, "phpstan", "")
t.Run("only phpstan", func(t *testing.T) {
tools, statuses, err := selectExtensionValidationTools("phpstan", "")
require.NoError(t, err)
assert.Equal(t, []string{"phpstan"}, toolNamesForValidation(tools))
assert.True(t, slices.ContainsFunc(tools, requiresToolSetup))
Expand All @@ -41,68 +42,59 @@ func TestExtensionValidationSelection(t *testing.T) {
})

t.Run("Twig validation needs no external tools", func(t *testing.T) {
tools, _, err := selectExtensionValidationTools(false, "storefront-twig", "")
tools, _, err := selectExtensionValidationTools("storefront-twig", "")
require.NoError(t, err)
assert.Equal(t, []string{"storefront-twig"}, toolNamesForValidation(tools))
assert.False(t, slices.ContainsFunc(tools, requiresToolSetup))
})

t.Run("full selects all validation checks", func(t *testing.T) {
tools, statuses, err := selectExtensionValidationTools(true, "", "")
require.NoError(t, err)
assert.Len(t, tools, 5)
assert.Len(t, statuses, len(tools))
assert.True(t, slices.ContainsFunc(tools, requiresToolSetup))
})

t.Run("only overrides full", func(t *testing.T) {
tools, statuses, err := selectExtensionValidationTools(true, "phpstan", "")
require.NoError(t, err)
assert.Equal(t, []string{"phpstan"}, toolNamesForValidation(tools))
assert.Equal(t, "not selected by --only", toolStatusByName(t, statuses, "sw-cli").Reason)
})

t.Run("full exclusion is reported", func(t *testing.T) {
tools, statuses, err := selectExtensionValidationTools(true, "", "phpstan")
t.Run("default exclusion is reported", func(t *testing.T) {
tools, statuses, err := selectExtensionValidationTools("", "phpstan")
require.NoError(t, err)
assert.NotContains(t, toolNamesForValidation(tools), "phpstan")
assert.Equal(t, "excluded by --exclude", toolStatusByName(t, statuses, "phpstan").Reason)
})

t.Run("exclude applies after only", func(t *testing.T) {
tools, statuses, err := selectExtensionValidationTools(false, "phpstan,sw-cli", "sw-cli")
tools, statuses, err := selectExtensionValidationTools("phpstan,sw-cli", "sw-cli")
require.NoError(t, err)
assert.Equal(t, []string{"phpstan"}, toolNamesForValidation(tools))
assert.Equal(t, "excluded by --exclude", toolStatusByName(t, statuses, "sw-cli").Reason)
})

t.Run("duplicate only values run once", func(t *testing.T) {
tools, _, err := selectExtensionValidationTools(false, "sw-cli,sw-cli", "")
tools, _, err := selectExtensionValidationTools("sw-cli,sw-cli", "")
require.NoError(t, err)
assert.Equal(t, []string{"sw-cli"}, toolNamesForValidation(tools))
})

t.Run("unsupported operation lists checkers", func(t *testing.T) {
_, _, err := selectExtensionValidationTools(false, "prettier", "")
_, _, err := selectExtensionValidationTools("prettier", "")
require.ErrorContains(t, err, `tool with name "prettier" not found, possible tools:`)
assert.NotContains(t, err.Error(), "prettier,")
assert.Contains(t, err.Error(), "phpstan")
})

t.Run("typo lists only checkers", func(t *testing.T) {
_, _, err := selectExtensionValidationTools(false, "phpsta", "")
_, _, err := selectExtensionValidationTools("phpsta", "")
require.ErrorContains(t, err, `tool with name "phpsta" not found, possible tools:`)
assert.Contains(t, err.Error(), "phpstan")
assert.NotContains(t, err.Error(), "prettier")
assert.NotContains(t, err.Error(), "rector")
})

t.Run("empty selection fails", func(t *testing.T) {
_, _, err := selectExtensionValidationTools(false, "sw-cli", "sw-cli")
_, _, err := selectExtensionValidationTools("sw-cli", "sw-cli")
require.EqualError(t, err, "no validation checks selected after applying --exclude")
})
}

func TestExtensionValidateFullFlagDeprecated(t *testing.T) {
flag := extensionValidateCmd.PersistentFlags().Lookup("full")
require.NotNil(t, flag)
assert.NotEmpty(t, flag.Deprecated)
}

func toolNamesForValidation(tools verifier.ToolList[verifier.CheckTool]) []string {
names := make([]string, 0, len(tools))
for _, tool := range tools {
Expand Down
10 changes: 5 additions & 5 deletions skills/shopware-cli-extension-store/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ Run both validations from the extension root and capture the full output **and**

```bash
shopware-cli --version
shopware-cli extension validate . --format markdown
shopware-cli extension validate . --store-compliance --format markdown
shopware-cli extension validate . --only sw-cli --format markdown
shopware-cli extension validate . --only sw-cli --store-compliance --format markdown
```

- The **exit code** is the pass/fail signal (`0` = pass, non-zero = findings). The report goes to stdout; a usage block or error goes to stderr — do not read a validation failure as a usage error.
- `--format markdown` gives a stable, quotable form. `--reporter` is a deprecated alias that prints a warning — use `--format`.
- Treat the store-compliance run as a delta over the normal run: report only the lines it adds.
- With neither `--full` nor `--only`, `extension validate` runs only the built-in `sw-cli` checker — **not** PHPStan/ESLint/Stylelint. (`sw-cli` is that checker's name, not shorthand for the binary.) An explicit `--only` selects its named checkers even without `--full`. For the two commands above, report "the `sw-cli` checks passed", not "full validation passed". Source: `cmd/extension/extension_validate.go`, `selectExtensionValidationTools`.
- `extension validate` now runs all checkers by default. The two commands above explicitly select only the built-in `sw-cli` checker, not PHPStan/ESLint/Stylelint. (`sw-cli` is that checker's name, not shorthand for the binary.) Report "the `sw-cli` checks passed", not "full validation passed". Source: `cmd/extension/extension_validate.go`, `selectExtensionValidationTools`.
- The Markdown report includes a checker table. `invoked` means the checker was called; it does not prove that files were analyzed or that a check passed. `skipped` means it was not selected or was excluded. Classify only finding lines, not checker-status lines.
- Use one `shopware-cli` binary throughout, and state its version. Never mix binaries mid-answer.
- Each error line ends with its result identifier — that identifier is the row's Source, and `L0` catches any line the table does not name explicitly. The CLI currently prints a missing icon twice; count a repeated line once.
Expand Down Expand Up @@ -123,7 +123,7 @@ Eight invariants. Check each finding against all eight before writing it.
| [`store-review-errors`](https://developer.shopware.com/docs/guides/development/testing/store/store-review-errors.html) | Common reasons reviewers reject a submission | user asks why a submission failed, or wants rejection risks |
| [`not-allowed-store-behaviors`](https://developer.shopware.com/docs/guides/development/testing/store/not-allowed-store-behaviors.html) | Prohibited patterns | extension touches core internals, filesystem, or DB directly |
| [`functionality-integration`](https://developer.shopware.com/docs/guides/development/testing/store/functionality-integration.html) | Correct integration with core, persistence, public APIs | extension has subscribers, entities, or API endpoints |
| [`code-quality`](https://developer.shopware.com/docs/guides/development/testing/store/code-quality.html) | Code standards reviewers apply | user asks about code quality, or `--full` validation was run |
| [`code-quality`](https://developer.shopware.com/docs/guides/development/testing/store/code-quality.html) | Code standards reviewers apply | user asks about code quality, or code-quality checkers were invoked |
| [`installation-and-cleanup`](https://developer.shopware.com/docs/guides/development/testing/store/installation-and-cleanup.html) | Install/update/uninstall and data removal | extension implements lifecycle methods or creates tables |
| [`cookies-and-privacy`](https://developer.shopware.com/docs/guides/development/testing/store/cookies-and-privacy.html) | Cookie registration, GDPR, subprocessors | extension sets cookies, tracks, or sends data to third parties |
| [`seo-and-structured-data`](https://developer.shopware.com/docs/guides/development/testing/store/seo-and-structured-data.html) | SEO output and structured data | extension changes storefront markup, URLs, or meta tags |
Expand All @@ -139,7 +139,7 @@ Preconditions here work like §2's: a page you had no trigger to read produces n

- CLI binary and version
- inspection timestamp
- sw-cli checks: pass/fail + exit code (state whether `--full` was run)
- sw-cli checks: pass/fail + exit code (state that `--only sw-cli` was used)
- store-compliance checks: pass/fail + exit code
- remote Store listing: inspected / not inspected
- files modified: no
Expand Down
4 changes: 2 additions & 2 deletions skills/shopware-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,9 @@ Normal extension validation runs the built-in checks implemented by the current

Common flags include:

- `--only <tools>` — run only the named checkers, independently of `--full` (comma-separated).
- `--only <tools>` — run only the named checkers (comma-separated).
- `--exclude <tools>` — remove checkers from the selected set.
- `--full` — select all checkers by default, including PHPStan, ESLint, and Stylelint; explicit `--only` takes precedence.
- All checkers run by default, including PHPStan, ESLint, and Stylelint. `--full` is deprecated and has no effect. To do a quick validation use `--only sw-cli`
- `--check-against <mode>` — `highest` (default) or `lowest`: which supported Shopware version to check against.
- `--store-compliance` — enable Store-compliance mode while the current CLI supports the flag. Prefer `validation.store_compliance: true` in `.shopware-extension.yml` for persistent Store intent.
- `--format <format>` — choose an output format supported by the current CLI (`--reporter` is a deprecated alias).
Expand Down
Loading