`product-export:generate` should warn when it skips generation instead of exiting silently
- Dominant language
- PHP
- Stars
- 3.4k
- Forks
- 1.2k
- Avg merge
- 3d 55m
- Merged PRs (30d)
- 436
Description
### Shopware Version
trunk
### Affected area / extension
Platform(Default)
### Actual behaviour
## Summary
`bin/console product-export:generate` exits with success (code `0`) and prints nothing whether or not it actually regenerated a file. If generation is skipped — because the cache interval hasn't expired yet, or because the export is scheduler-managed (`generate_by_cronjob = 1`) and `--force` wasn't passed — the operator has no way to tell "nothing needed doing" apart from "the command silently did nothing when I expected it to." That ambiguity is what made the `--force`-is-ignored bug (see companion report) so hard to notice in the first place: the command gives identical output (none) whether it worked or not.
## Environment
- Shopware version: 6.7.0.0
- Affected component: `Shopware\Core\Content\ProductExport`
## Root Cause / Where This Lives
`ProductExporter::createFile()` is the only place that knows *why* it's returning early — it currently just checks a boolean and bails:
```php
// Content/ProductExport/Service/ProductExporter.php
private function createFile(...): void
{
$filePath = $this->productExportFileHandler->getFilePath($productExport);
if ($this->productExportFileHandler->isValidFile($filePath, $exportBehavior, $productExport)) {
return;
}
// ... actual generation
}
```
`ProductExportFileHandler::isValidFile()` collapses two distinct skip reasons (cache not expired vs. scheduler-managed) into a single boolean, so even internally the "why" is discarded before it reaches the command:
```php
// Content/ProductExport/Service/ProductExportFileHandler.php
public function isValidFile(string $filePath, ExportBehavior $behavior, ProductExportEntity $productExport): bool
{
if (!$this->fileSystem->fileExists($filePath)) {
return false;
}
return $productExport->isGenerateByCronjob() || !$this->isCacheExpired($behavior, $productExport);
}
```
## Suggested Fix
- Have `isValidFile()` (or a sibling method) return a reason instead of/alongside the boolean — e.g. an enum `ValidFileReason::{NOT_FOUND, SCHEDULER_MANAGED, CACHE_NOT_EXPIRED}` — so `createFile()` can act on it.
- In `createFile()`, when skipping, dispatch the existing `ProductExportLoggingEvent` (already used for exceptions in this class) at a lower level such as `Level::Notice`, carrying the reason and export identifier.
- In `ProductExportGenerateCommand::execute()`, print a line per skipped export using that event/reason, similar to the reproduction example above.
This is intentionally scoped as a DX/observability improvement independent of the `--force` bug in the companion report — even once `--force` is fixed to actually force scheduler-managed exports, silent no-ops for the ordinary "cache not expired" case are still worth surfacing, since right now there is no way to distinguish "correctly skipped" from "something is wrong" without reading source code.
### Expected behaviour
When generation is skipped, the command should say so and why, e.g.:
```
Skipped "google-shop1.xml" (a1b2c3...): cache not expired yet (generated 2026-07-21 08:00, interval 86400s, next eligible 2026-07-22 08:00). Use --force to regenerate anyway.
Skipped "affiliate-adcell.csv" (d4e5f6...): managed by scheduler ("Generate via scheduler" is enabled) — pass --force to override, or disable the scheduler flag for CLI-only control.
```
and ideally a non-zero-but-distinguishable exit path (or at least a summary line) so scripts/cron wrappers can detect "ran but skipped everything" versus "actually generated N files."
### How to reproduce
1. Have a product export whose cache interval hasn't expired yet (or one with "Generate via scheduler" enabled).
2. Run `bin/console product-export:generate ` (with or without `--force`, depending on which case — see companion `--force` report for the scheduler case).
3. Observe: command output is empty, exit code `0`, no file was touched.
Contributor guide
Research direction
Start with Content/ProductExport/Service/ProductExporter.php and ProductExportFileHandler.php, then trace how ProductExportGenerateCommand.php handles generation results and how ProductExportLoggingEvent is used. Reproduce a skipped export with bin/console product-export:generate and inspect existing tests around these classes. Done means skipped exports report the relevant reason while normal generation and exit behavior remain correct.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php
- Domain
- backend, cli, observability
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100