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
15 changes: 13 additions & 2 deletions cmd/extension/extension_fix.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var extensionFixCmd = &cobra.Command{
Use: "fix path",
Short: "Apply code-quality fixes to an extension",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
PreRunE: func(cmd *cobra.Command, args []string) error {
Comment thread
MalteJanz marked this conversation as resolved.
allTools := verifier.GetToolsOf[verifier.FixTool]()
only, _ := cmd.Flags().GetString("only")
exclude, _ := cmd.Flags().GetString("exclude")
Expand All @@ -37,6 +37,17 @@ var extensionFixCmd = &cobra.Command{
return errors.New("no fixers selected after applying --exclude")
}

return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
allTools := verifier.GetToolsOf[verifier.FixTool]()
only, _ := cmd.Flags().GetString("only")
exclude, _ := cmd.Flags().GetString("exclude")

// Tool selection was validated in PreRunE.
requestedTools, _ := allTools.Only(only)
tools, _ := requestedTools.Exclude(exclude)

allowNonGit, _ := cmd.Flags().GetBool("allow-non-git")

if !allowNonGit {
Expand Down Expand Up @@ -81,6 +92,6 @@ var extensionFixCmd = &cobra.Command{
func init() {
extensionRootCmd.AddCommand(extensionFixCmd)
extensionFixCmd.Flags().String("only", "", "Run only the specified fixers (comma-separated, e.g. eslint,rector)")
extensionFixCmd.Flags().String("exclude", "", "Skip these fixers; must be in the --only list if set (comma-separated, e.g. eslint,rector)")
extensionFixCmd.Flags().String("exclude", "", "Exclude specified fixers from running (comma-separated, e.g. eslint,rector). When combined with --only, exclude fixers from the selected set")
extensionFixCmd.Flags().Bool("allow-non-git", false, "Allow fix to run outside a Git repository")
}
15 changes: 13 additions & 2 deletions cmd/extension/extension_format.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var extensionFormat = &cobra.Command{
Use: "format path",
Short: "Format an extension's PHP, JavaScript, and SCSS files",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
PreRunE: func(cmd *cobra.Command, args []string) error {
allTools := verifier.GetToolsOf[verifier.FormatTool]()
only, _ := cmd.Flags().GetString("only")
exclude, _ := cmd.Flags().GetString("exclude")
Expand All @@ -37,6 +37,17 @@ var extensionFormat = &cobra.Command{
return errors.New("no formatters selected after applying --exclude")
}

return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
allTools := verifier.GetToolsOf[verifier.FormatTool]()
only, _ := cmd.Flags().GetString("only")
exclude, _ := cmd.Flags().GetString("exclude")

// Tool selection was validated in PreRunE.
requestedTools, _ := allTools.Only(only)
tools, _ := requestedTools.Exclude(exclude)

dryRun, _ := cmd.Flags().GetBool("dry-run")

path, err := filepath.Abs(args[0])
Expand Down Expand Up @@ -75,6 +86,6 @@ var extensionFormat = &cobra.Command{
func init() {
extensionRootCmd.AddCommand(extensionFormat)
extensionFormat.Flags().String("only", "", "Run only the specified formatters (comma-separated, e.g. prettier,php-cs-fixer)")
extensionFormat.Flags().String("exclude", "", "Skip these formatters; must be in the --only list if set (comma-separated, e.g. prettier,php-cs-fixer)")
extensionFormat.Flags().String("exclude", "", "Exclude specified formatters from running (comma-separated, e.g. prettier,php-cs-fixer). When combined with --only, exclude formatters from the selected set")
extensionFormat.Flags().Bool("dry-run", false, "Run in dry run mode")
}
37 changes: 18 additions & 19 deletions cmd/extension/extension_validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,16 @@ var extensionValidateCmd = &cobra.Command{
Long: "Validate an extension folder or ZIP file. With --store-compliance (or SHOPWARE_CLI_STORE_COMPLIANCE=1), the Store's rules apply and the extension's validation.ignore list is not used.",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
logger := logging.FromContext(cmd.Context())
storeCompliance, _ := cmd.Flags().GetBool("store-compliance")
reportingFormat, err := extensionValidationFormat(cmd)
if err != nil {
return err
}
// Reporting format and tool selection were validated in PreRunE.
reportingFormat, _ := extensionValidationFormat(cmd)
checkAgainst, _ := cmd.Flags().GetString("check-against")
only, _ := cmd.Flags().GetString("only")
exclude, _ := cmd.Flags().GetString("exclude")
noCopy, _ := cmd.Flags().GetBool("no-copy")
verifier.WarnOnDeprecatedToolName(cmd.Context(), only, exclude)

tools, statuses, err := selectExtensionValidationTools(only, exclude)
if err != nil {
return err
}
tools, statuses, _ := selectExtensionValidationTools(only, exclude)
needsTools := slices.ContainsFunc(tools, requiresToolSetup)

path, err := filepath.Abs(args[0])
Expand All @@ -55,7 +50,7 @@ var extensionValidateCmd = &cobra.Command{
if stat.IsDir() {
validationPath := path
if noCopy {
logging.FromContext(cmd.Context()).Debugf("Skipping copying extension files to temporary directory due to --no-copy flag")
logger.Debugf("Skipping copying extension files to temporary directory due to --no-copy flag")
} else if needsTools {
tmpDir, err := os.MkdirTemp(os.TempDir(), "analyse-extension-*")
if err != nil {
Expand All @@ -64,17 +59,17 @@ var extensionValidateCmd = &cobra.Command{
defer func() {
beforeDeleteTime := time.Now()
if err := os.RemoveAll(tmpDir); err != nil {
logging.FromContext(cmd.Context()).Errorf("Failed to remove temporary directory: %v", err)
logger.Errorf("Failed to remove temporary directory: %v", err)
}
logging.FromContext(cmd.Context()).Debugf("Removed temporary directory in %s", time.Since(beforeDeleteTime).String())
logger.Debugf("Removed temporary directory in %s", time.Since(beforeDeleteTime).String())
}()

beforeCopyTime := time.Now()
if err := system.CopyFiles(cmd.Context(), path, tmpDir); err != nil {
return err
}

logging.FromContext(cmd.Context()).Debugf("Copied extension files to temporary directory in %s", time.Since(beforeCopyTime).String())
logger.Debugf("Copied extension files to temporary directory in %s", time.Since(beforeCopyTime).String())
validationPath = tmpDir
}

Expand All @@ -83,7 +78,7 @@ var extensionValidateCmd = &cobra.Command{
return err
}

toolCfg, err = verifier.ConvertExtensionToToolConfig(ext)
toolCfg, err = verifier.ConvertExtensionToToolConfig(cmd.Context(), ext)
if err != nil {
return err
}
Expand All @@ -95,16 +90,16 @@ var extensionValidateCmd = &cobra.Command{
return err
}

toolCfg, err = verifier.ConvertExtensionToToolConfig(ext)
toolCfg, err = verifier.ConvertExtensionToToolConfig(cmd.Context(), ext)
if err != nil {
return err
}
}

if storeCompliance || os.Getenv("SHOPWARE_CLI_STORE_COMPLIANCE") == "1" {
toolCfg.Extension.GetExtensionConfig().Validation.StoreCompliance = true
toolCfg.Extensions[0].GetExtensionConfig().Validation.StoreCompliance = true
// The user is not allowed to provide a custom ignore list when store compliance is enabled
toolCfg.Extension.GetExtensionConfig().Validation.Ignore = extension.ConfigValidationList{}
toolCfg.Extensions[0].GetExtensionConfig().Validation.Ignore = extension.ConfigValidationList{}
toolCfg.ValidationIgnores = nil
}

Expand Down Expand Up @@ -183,7 +178,7 @@ func init() {
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 (comma-separated, e.g. phpstan,eslint)")
extensionValidateCmd.PersistentFlags().String("exclude", "", "Exclude specific tools by name (comma-separated, e.g. phpstan,eslint)")
extensionValidateCmd.PersistentFlags().String("exclude", "", "Exclude specified validation checks from running (comma-separated, e.g. phpstan,eslint). When combined with --only, exclude validation checks from the selected set")
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 builtin")
Expand All @@ -199,6 +194,10 @@ func init() {
return fmt.Errorf("invalid --check-against value %q, allowed values: highest, lowest", mode)
}

return nil
only, _ := cmd.Flags().GetString("only")
exclude, _ := cmd.Flags().GetString("exclude")
verifier.WarnOnDeprecatedToolName(cmd.Context(), only, exclude)
_, _, err := selectExtensionValidationTools(only, exclude)
return err
}
}
33 changes: 21 additions & 12 deletions cmd/project/project_fix.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"golang.org/x/sync/errgroup"

"github.com/shopware/shopware-cli/internal/shop"
"github.com/shopware/shopware-cli/internal/validation"
"github.com/shopware/shopware-cli/internal/verifier"
)

Expand All @@ -18,11 +19,23 @@ var projectFixCmd = &cobra.Command{
Long: "Run code-quality fixers on the project's own code, such as extensions in custom/ and configured bundles, and change the files directly. Packages that Composer installs into vendor/ are not changed. Requires a Git repository so the changes can be reviewed, unless --allow-non-git is passed.",
Args: cobra.MaximumNArgs(1),
PreRunE: func(cmd *cobra.Command, args []string) error {
Comment thread
MalteJanz marked this conversation as resolved.
return verifier.SetupTools(cmd.Context(), cmd.Root().Version)
only, _ := cmd.Flags().GetString("only")
exclude, _ := cmd.Flags().GetString("exclude")
verifier.WarnOnDeprecatedToolName(cmd.Context(), only, exclude)
Comment thread
MalteJanz marked this conversation as resolved.
_, _, err := selectProjectTools(verifier.GetToolsOf[verifier.FixTool](), only, exclude, "fixers")
return err
},
RunE: func(cmd *cobra.Command, args []string) error {
var err error
only, _ := cmd.Flags().GetString("only")
exclude, _ := cmd.Flags().GetString("exclude")

// Tool selection was validated in PreRunE.
tools, statuses, _ := selectProjectTools(verifier.GetToolsOf[verifier.FixTool](), only, exclude, "fixers")
if err := verifier.SetupTools(cmd.Context(), cmd.Root().Version); err != nil {
return err
}

var err error
projectPath := ""

if len(args) > 0 {
Expand All @@ -46,34 +59,30 @@ var projectFixCmd = &cobra.Command{
}
}

only, _ := cmd.Flags().GetString("only")

toolCfg, err := verifier.GetConfigFromProject(cmd.Context(), projectPath, false)
if err != nil {
return err
}

var gr errgroup.Group

tools := verifier.GetToolsOf[verifier.FixTool]()

tools, err = tools.Only(only)
if err != nil {
return err
}

for _, tool := range tools {
gr.Go(func() error {
return tool.Fix(cmd.Context(), *toolCfg)
})
}

return gr.Wait()
runErr := gr.Wait()
if err := validation.PrintToolInvocationTable(os.Stdout, "Fixers", statuses); err != nil {
return err
}
return runErr
},
}

func init() {
projectRootCmd.AddCommand(projectFixCmd)
projectFixCmd.PersistentFlags().String("only", "", "Run only the specified fixers (comma-separated, e.g. eslint,rector)")
projectFixCmd.PersistentFlags().Bool("allow-non-git", false, "Allow fix to run outside a Git repository")
projectFixCmd.PersistentFlags().String("exclude", "", "Exclude specified fixers from running (comma-separated, e.g. eslint,rector). When combined with --only, exclude fixers from the selected set")
}
4 changes: 4 additions & 0 deletions cmd/project/project_fix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
)

func TestProjectFixNoArgsOutsideProjectReturnsError(t *testing.T) {
t.Setenv("SHOPWARE_CLI_TOOLS_DIR", t.TempDir())
chdirOutsideProject(t)

projectFixCmd.SetContext(t.Context())
Expand All @@ -19,6 +20,7 @@ func TestProjectFixNoArgsOutsideProjectReturnsError(t *testing.T) {
}

func TestProjectFixNoArgsAppliesGitGuardToResolvedProject(t *testing.T) {
t.Setenv("SHOPWARE_CLI_TOOLS_DIR", t.TempDir())
cwd := t.TempDir()
require.NoError(t, os.Mkdir(filepath.Join(cwd, ".git"), 0o755))
t.Chdir(cwd)
Expand All @@ -34,6 +36,7 @@ func TestProjectFixNoArgsAppliesGitGuardToResolvedProject(t *testing.T) {
}

func TestProjectFixRejectsNonGitDirectory(t *testing.T) {
t.Setenv("SHOPWARE_CLI_TOOLS_DIR", t.TempDir())
dir := t.TempDir()
projectFixCmd.SetContext(t.Context())
err := projectFixCmd.RunE(projectFixCmd, []string{dir})
Expand All @@ -43,6 +46,7 @@ func TestProjectFixRejectsNonGitDirectory(t *testing.T) {
}

func TestProjectFixPassesGitGuardWithGitDirectory(t *testing.T) {
t.Setenv("SHOPWARE_CLI_TOOLS_DIR", t.TempDir())
dir := t.TempDir()
require.NoError(t, os.Mkdir(filepath.Join(dir, ".git"), 0o755))

Expand Down
31 changes: 21 additions & 10 deletions cmd/project/project_format.go
Comment thread
MalteJanz marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package project

import (
"fmt"
"os"
"path/filepath"

"github.com/spf13/cobra"
"golang.org/x/sync/errgroup"

"github.com/shopware/shopware-cli/internal/shop"
"github.com/shopware/shopware-cli/internal/validation"
"github.com/shopware/shopware-cli/internal/verifier"
)

Expand All @@ -17,13 +19,24 @@ var projectFormatCmd = &cobra.Command{
Long: "Format the project's own code, such as extensions in custom/ and configured bundles, and change the files directly. Packages that Composer installs into vendor/ are not changed. PHP-CS-Fixer uses the project's .php-cs-fixer.dist.php if present; Prettier always uses the CLI's own config. Use --dry-run to only report files that would change.",
Args: cobra.MaximumNArgs(1),
PreRunE: func(cmd *cobra.Command, args []string) error {
return verifier.SetupTools(cmd.Context(), cmd.Root().Version)
only, _ := cmd.Flags().GetString("only")
exclude, _ := cmd.Flags().GetString("exclude")
verifier.WarnOnDeprecatedToolName(cmd.Context(), only, exclude)
_, _, err := selectProjectTools(verifier.GetToolsOf[verifier.FormatTool](), only, exclude, "formatters")
return err
},
RunE: func(cmd *cobra.Command, args []string) error {
var err error
only, _ := cmd.Flags().GetString("only")
exclude, _ := cmd.Flags().GetString("exclude")

// Tool selection was validated in PreRunE.
tools, statuses, _ := selectProjectTools(verifier.GetToolsOf[verifier.FormatTool](), only, exclude, "formatters")
if err := verifier.SetupTools(cmd.Context(), cmd.Root().Version); err != nil {
return err
}
dryRun, _ := cmd.Flags().GetBool("dry-run")

var err error
projectPath := ""

if len(args) > 0 {
Expand All @@ -47,25 +60,23 @@ var projectFormatCmd = &cobra.Command{

var gr errgroup.Group

tools := verifier.GetToolsOf[verifier.FormatTool]()

tools, err = tools.Only(only)
if err != nil {
return err
}

for _, tool := range tools {
gr.Go(func() error {
return tool.Format(cmd.Context(), *toolCfg, dryRun)
})
}

return gr.Wait()
runErr := gr.Wait()
if err := validation.PrintToolInvocationTable(os.Stdout, "Formatters", statuses); err != nil {
return err
}
return runErr
},
}

func init() {
projectRootCmd.AddCommand(projectFormatCmd)
projectFormatCmd.PersistentFlags().String("only", "", "Run only the specified formatters (comma-separated, e.g. prettier,php-cs-fixer)")
projectFormatCmd.PersistentFlags().Bool("dry-run", false, "Report files that would change, without changing them")
projectFormatCmd.PersistentFlags().String("exclude", "", "Exclude specified formatters from running (comma-separated, e.g. prettier,php-cs-fixer). When combined with --only, exclude formatters from the selected set")
}
42 changes: 42 additions & 0 deletions cmd/project/project_tool_invocation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package project

import (
"fmt"
"slices"
"sort"

"github.com/shopware/shopware-cli/internal/validation"
"github.com/shopware/shopware-cli/internal/verifier"
)

func projectToolInvocationStatuses[T verifier.Tool](all, requested, selected verifier.ToolList[T]) []validation.ToolInvocationStatus {
statuses := make([]validation.ToolInvocationStatus, 0, len(all))
for _, tool := range all {
status := validation.ToolInvocationStatus{Name: tool.Name(), Status: "skipped", Reason: "not selected by --only"}
switch {
case slices.ContainsFunc(selected, func(selected T) bool { return selected.Name() == tool.Name() }):
status.Status = "invoked"
status.Reason = ""
case slices.ContainsFunc(requested, func(requested T) bool { return requested.Name() == tool.Name() }):
status.Reason = "excluded by --exclude"
}
statuses = append(statuses, status)
}
sort.Slice(statuses, func(i, j int) bool { return statuses[i].Name < statuses[j].Name })
return statuses
}

func selectProjectTools[T verifier.Tool](all verifier.ToolList[T], only, exclude, kind string) (verifier.ToolList[T], []validation.ToolInvocationStatus, error) {
requested, err := all.Only(only)
if err != nil {
return nil, nil, err
}
selected, err := requested.Exclude(exclude)
if err != nil {
return nil, nil, err
}
if len(selected) == 0 {
return nil, nil, fmt.Errorf("no %s selected after applying --exclude", kind)
}
return selected, projectToolInvocationStatuses(all, requested, selected), nil
}
Loading
Loading