From aeb182a20c4ad1245a894bcf682df5f6b696f53e Mon Sep 17 00:00:00 2001 From: Soner Sayakci Date: Sat, 23 Sep 2023 23:04:19 +0000 Subject: [PATCH 1/5] feat: add bundle support --- .devcontainer/devcontainer.json | 8 +- .vscode/settings.json | 5 +- cmd/extension/extension_build.go | 15 ++- cmd/extension/extension_zip.go | 9 +- cmd/project/ci.go | 35 ++++-- cmd/project/platform.go | 1 + cmd/project/project_admin_build.go | 14 ++- cmd/project/project_storefront_build.go | 14 ++- extension/asset.go | 44 ++++++++ extension/asset_platform.go | 65 ++++------- extension/bundle.go | 143 ++++++++++++++++++++++++ extension/platform.go | 4 + extension/project.go | 71 ++++++++++++ extension/root.go | 10 +- extension/zip.go | 14 +-- internal/asset/source.go | 6 + 16 files changed, 381 insertions(+), 77 deletions(-) create mode 100644 extension/asset.go create mode 100644 extension/bundle.go create mode 100644 internal/asset/source.go diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 8556740c..6d92c89b 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -13,9 +13,11 @@ "customizations": { "vscode": { "extensions": [ - "golang.go-nightly", - "github.vscode-github-actions" - ] + "golang.go-nightly", + "github.vscode-github-actions", + "redhat.vscode-yaml", + "liuchao.go-struct-tag" + ] } } } diff --git a/.vscode/settings.json b/.vscode/settings.json index 98dfe7d1..45716453 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,5 +15,8 @@ ".idea": true }, "go.toolsManagement.autoUpdate": false, - "go.toolsManagement.checkForUpdates": "off" + "go.toolsManagement.checkForUpdates": "off", + "yaml.schemas": { + "https://raw.githubusercontent.com/FriendsOfShopware/shopware-cli/main/extension/shopware-extension-schema.json": "file:///workspaces/shopware-cli/.shopware-extension.yml" + } } \ No newline at end of file diff --git a/cmd/extension/extension_build.go b/cmd/extension/extension_build.go index 7ffbdedf..7bc64cb1 100644 --- a/cmd/extension/extension_build.go +++ b/cmd/extension/extension_build.go @@ -16,7 +16,11 @@ var extensionAssetBundleCmd = &cobra.Command{ Short: "Builds assets for extensions", Args: cobra.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - assetCfg := extension.AssetBuildConfig{EnableESBuildForAdmin: false, EnableESBuildForStorefront: false} + assetCfg := extension.AssetBuildConfig{ + EnableESBuildForAdmin: false, + EnableESBuildForStorefront: false, + ShopwareRoot: os.Getenv("SHOPWARE_PROJECT_ROOT"), + } validatedExtensions := make([]extension.Extension, 0) for _, arg := range args { @@ -40,7 +44,14 @@ var extensionAssetBundleCmd = &cobra.Command{ assetCfg.EnableESBuildForStorefront = extCfg.Build.Zip.Assets.EnableESBuildForStorefront } - err := extension.BuildAssetsForExtensions(cmd.Context(), os.Getenv("SHOPWARE_PROJECT_ROOT"), validatedExtensions, assetCfg) + constraint, err := validatedExtensions[0].GetShopwareVersionConstraint() + if err != nil { + return fmt.Errorf("cannot get shopware version constraint: %w", err) + } + + assetCfg.ShopwareVersion = constraint + + err = extension.BuildAssetsForExtensions(cmd.Context(), extension.ConvertExtensionsToSources(cmd.Context(), validatedExtensions), assetCfg) if err != nil { return fmt.Errorf("cannot build assets: %w", err) } diff --git a/cmd/extension/extension_zip.go b/cmd/extension/extension_zip.go index 5ec0e456..40d89ced 100644 --- a/cmd/extension/extension_zip.go +++ b/cmd/extension/extension_zip.go @@ -131,13 +131,20 @@ var extensionZipCmd = &cobra.Command{ return err } + shopwareConstraint, err := tempExt.GetShopwareVersionConstraint() + if err != nil { + return fmt.Errorf("get shopware version constraint: %w", err) + } + assetBuildConfig := extension.AssetBuildConfig{ EnableESBuildForAdmin: extCfg.Build.Zip.Assets.EnableESBuildForAdmin, EnableESBuildForStorefront: extCfg.Build.Zip.Assets.EnableESBuildForStorefront, CleanupNodeModules: true, + ShopwareRoot: os.Getenv("SHOPWARE_PROJECT_ROOT"), + ShopwareVersion: shopwareConstraint, } - if err := extension.BuildAssetsForExtensions(cmd.Context(), os.Getenv("SHOPWARE_PROJECT_ROOT"), []extension.Extension{tempExt}, assetBuildConfig); err != nil { + if err := extension.BuildAssetsForExtensions(cmd.Context(), extension.ConvertExtensionsToSources(cmd.Context(), []extension.Extension{tempExt}), assetBuildConfig); err != nil { return fmt.Errorf("building assets: %w", err) } diff --git a/cmd/project/ci.go b/cmd/project/ci.go index 05e06bfd..1880b951 100644 --- a/cmd/project/ci.go +++ b/cmd/project/ci.go @@ -57,6 +57,7 @@ var projectCI = &cobra.Command{ composer := exec.CommandContext(cmd.Context(), "composer", "install", "--no-dev", "--no-interaction", "--no-progress", "--optimize-autoloader", "--classmap-authoritative") composer.Dir = args[0] + composer.Stdin = os.Stdin composer.Stdout = os.Stdout composer.Stderr = os.Stderr @@ -66,11 +67,23 @@ var projectCI = &cobra.Command{ logging.FromContext(cmd.Context()).Infof("Looking for extensions to build assets in project") - extensions := extension.FindExtensionsFromProject(cmd.Context(), args[0]) + sources := extension.FindAssetSourcesOfProject(cmd.Context(), args[0]) + constraint, err := extension.GetShopwareProjectConstraint(args[0]) + if err != nil { + return err + } - assetCfg := extension.AssetBuildConfig{EnableESBuildForAdmin: false, EnableESBuildForStorefront: false, CleanupNodeModules: true} + fmt.Println(sources) + + assetCfg := extension.AssetBuildConfig{ + EnableESBuildForAdmin: false, + EnableESBuildForStorefront: false, + CleanupNodeModules: true, + ShopwareRoot: args[0], + ShopwareVersion: constraint, + } - if err := extension.BuildAssetsForExtensions(cmd.Context(), args[0], extensions, assetCfg); err != nil { + if err := extension.BuildAssetsForExtensions(cmd.Context(), sources, assetCfg); err != nil { return err } @@ -80,8 +93,8 @@ var projectCI = &cobra.Command{ } if !shopCfg.Build.KeepExtensionSource { - for _, ext := range extensions { - if err := cleanupAdministrationFiles(cmd.Context(), ext.GetRootDir()); err != nil { + for _, source := range sources { + if err := cleanupAdministrationFiles(cmd.Context(), source.Path); err != nil { return err } } @@ -126,20 +139,20 @@ var projectCI = &cobra.Command{ if shopCfg.Build.RemoveExtensionAssets { logging.FromContext(cmd.Context()).Infof("Deleting assets of extensions") - for _, ext := range extensions { - if _, err := os.Stat(path.Join(ext.GetRootDir(), "Resources", "public", "administration", "css")); err == nil { - if err := os.WriteFile(path.Join(ext.GetRootDir(), "Resources", ".administration-css"), []byte{}, os.ModePerm); err != nil { + for _, source := range sources { + if _, err := os.Stat(path.Join(source.Path, "Resources", "public", "administration", "css")); err == nil { + if err := os.WriteFile(path.Join(source.Path, "Resources", ".administration-css"), []byte{}, os.ModePerm); err != nil { return err } } - if _, err := os.Stat(path.Join(ext.GetRootDir(), "Resources", "public", "administration", "js")); err == nil { - if err := os.WriteFile(path.Join(ext.GetRootDir(), "Resources", ".administration-js"), []byte{}, os.ModePerm); err != nil { + if _, err := os.Stat(path.Join(source.Path, "Resources", "public", "administration", "js")); err == nil { + if err := os.WriteFile(path.Join(source.Path, "Resources", ".administration-js"), []byte{}, os.ModePerm); err != nil { return err } } - if err := os.RemoveAll(path.Join(ext.GetRootDir(), "Resources", "public")); err != nil { + if err := os.RemoveAll(path.Join(source.Path, "Resources", "public")); err != nil { return err } } diff --git a/cmd/project/platform.go b/cmd/project/platform.go index dcaa95d0..1f6f8955 100644 --- a/cmd/project/platform.go +++ b/cmd/project/platform.go @@ -44,3 +44,4 @@ func findClosestShopwareProject() (string, error) { return "", fmt.Errorf("cannot find Shopware project in current directory") } + diff --git a/cmd/project/project_admin_build.go b/cmd/project/project_admin_build.go index 86ad545d..3bfe1b49 100644 --- a/cmd/project/project_admin_build.go +++ b/cmd/project/project_admin_build.go @@ -23,11 +23,19 @@ var projectAdminBuildCmd = &cobra.Command{ logging.FromContext(cmd.Context()).Infof("Looking for extensions to build assets in project") - extensions := extension.FindExtensionsFromProject(cmd.Context(), projectRoot) + sources := extension.FindAssetSourcesOfProject(cmd.Context(), args[0]) + constraint, err := extension.GetShopwareProjectConstraint(args[0]) + if err != nil { + return err + } - assetCfg := extension.AssetBuildConfig{DisableStorefrontBuild: true} + assetCfg := extension.AssetBuildConfig{ + DisableStorefrontBuild: true, + ShopwareRoot: projectRoot, + ShopwareVersion: constraint, + } - if err := extension.BuildAssetsForExtensions(cmd.Context(), projectRoot, extensions, assetCfg); err != nil { + if err := extension.BuildAssetsForExtensions(cmd.Context(), sources, assetCfg); err != nil { return err } diff --git a/cmd/project/project_storefront_build.go b/cmd/project/project_storefront_build.go index 3bacc9a2..a30612b3 100644 --- a/cmd/project/project_storefront_build.go +++ b/cmd/project/project_storefront_build.go @@ -23,11 +23,19 @@ var projectStorefrontBuildCmd = &cobra.Command{ logging.FromContext(cmd.Context()).Infof("Looking for extensions to build assets in project") - extensions := extension.FindExtensionsFromProject(cmd.Context(), projectRoot) + sources := extension.FindAssetSourcesOfProject(cmd.Context(), args[0]) + constraint, err := extension.GetShopwareProjectConstraint(args[0]) + if err != nil { + return err + } - assetCfg := extension.AssetBuildConfig{DisableAdminBuild: true} + assetCfg := extension.AssetBuildConfig{ + DisableAdminBuild: true, + ShopwareRoot: projectRoot, + ShopwareVersion: constraint, + } - if err := extension.BuildAssetsForExtensions(cmd.Context(), projectRoot, extensions, assetCfg); err != nil { + if err := extension.BuildAssetsForExtensions(cmd.Context(), sources, assetCfg); err != nil { return err } diff --git a/extension/asset.go b/extension/asset.go new file mode 100644 index 00000000..05b887e8 --- /dev/null +++ b/extension/asset.go @@ -0,0 +1,44 @@ +package extension + +import ( + "context" + "path" + "path/filepath" + + "github.com/FriendsOfShopware/shopware-cli/internal/asset" + "github.com/FriendsOfShopware/shopware-cli/logging" +) + +func ConvertExtensionsToSources(ctx context.Context, extensions []Extension) []asset.Source { + sources := make([]asset.Source, 0) + + for _, ext := range extensions { + name, err := ext.GetName() + if err != nil { + logging.FromContext(ctx).Errorf("Skipping extension %s as it has a invalid name", ext.GetPath()) + continue + } + + sources = append(sources, asset.Source{ + Name: name, + Path: ext.GetRootDir(), + }) + + extConfig := ext.GetExtensionConfig() + + for _, bundle := range extConfig.Build.ExtraBundles { + bundleName := bundle.Name + + if bundleName == "" { + bundleName = filepath.Base(bundle.Path) + } + + sources = append(sources, asset.Source{ + Name: bundleName, + Path: path.Join(ext.GetRootDir(), bundle.Path), + }) + } + } + + return sources +} diff --git a/extension/asset_platform.go b/extension/asset_platform.go index 7527feba..635e97c5 100644 --- a/extension/asset_platform.go +++ b/extension/asset_platform.go @@ -10,6 +10,7 @@ import ( "path/filepath" "strings" + "github.com/FriendsOfShopware/shopware-cli/internal/asset" "github.com/FriendsOfShopware/shopware-cli/internal/esbuild" "github.com/FriendsOfShopware/shopware-cli/logging" "github.com/FriendsOfShopware/shopware-cli/version" @@ -31,10 +32,12 @@ type AssetBuildConfig struct { CleanupNodeModules bool DisableAdminBuild bool DisableStorefrontBuild bool + ShopwareRoot string + ShopwareVersion *version.Constraints } -func BuildAssetsForExtensions(ctx context.Context, shopwareRoot string, extensions []Extension, assetConfig AssetBuildConfig) error { // nolint:gocyclo - cfgs := buildAssetConfigFromExtensions(ctx, extensions, shopwareRoot) +func BuildAssetsForExtensions(ctx context.Context, sources []asset.Source, assetConfig AssetBuildConfig) error { // nolint:gocyclo + cfgs := buildAssetConfigFromExtensions(ctx, sources, assetConfig.ShopwareRoot) if len(cfgs) == 1 { return nil @@ -47,9 +50,10 @@ func BuildAssetsForExtensions(ctx context.Context, shopwareRoot string, extensio buildWithoutShopwareSource := assetConfig.EnableESBuildForStorefront && assetConfig.EnableESBuildForAdmin + shopwareRoot := assetConfig.ShopwareRoot var err error if shopwareRoot == "" && !buildWithoutShopwareSource { - shopwareRoot, err = setupShopwareInTemp(ctx, extensions[0]) + shopwareRoot, err = setupShopwareInTemp(ctx, assetConfig.ShopwareVersion) if err != nil { return err @@ -104,13 +108,13 @@ func BuildAssetsForExtensions(ctx context.Context, shopwareRoot string, extensio if !assetConfig.DisableAdminBuild && cfgs.RequiresAdminBuild() { if assetConfig.EnableESBuildForAdmin { - for _, extension := range extensions { - name, _ := extension.GetName() - if !cfgs.Has(name) { + for _, source := range sources { + if !cfgs.Has(source.Name) { continue } - options := esbuild.NewAssetCompileOptionsAdmin(name, extension.GetPath(), extension.GetType()) + // @todo: fix me later + options := esbuild.NewAssetCompileOptionsAdmin(source.Name, source.Path, "") if _, err := esbuild.CompileExtensionAsset(ctx, options); err != nil { return err @@ -137,13 +141,13 @@ func BuildAssetsForExtensions(ctx context.Context, shopwareRoot string, extensio if !assetConfig.DisableStorefrontBuild && cfgs.RequiresStorefrontBuild() { if assetConfig.EnableESBuildForStorefront { - for _, extension := range extensions { - name, _ := extension.GetName() - if !cfgs.Has(name) { + for _, source := range sources { + if !cfgs.Has(source.Name) { continue } - options := esbuild.NewAssetCompileOptionsStorefront(name, extension.GetPath(), extension.GetType()) + // @todo: fix me later + options := esbuild.NewAssetCompileOptionsStorefront(source.Name, source.Path, "") if _, err := esbuild.CompileExtensionAsset(ctx, options); err != nil { return err } @@ -237,40 +241,11 @@ func prepareShopwareForAsset(shopwareRoot string, cfgs map[string]ExtensionAsset return nil } -func buildAssetConfigFromExtensions(ctx context.Context, extensions []Extension, shopwareRoot string) ExtensionAssetConfig { +func buildAssetConfigFromExtensions(ctx context.Context, sources []asset.Source, shopwareRoot string) ExtensionAssetConfig { list := make(ExtensionAssetConfig) - for _, extension := range extensions { - extName, err := extension.GetName() - if err != nil { - logging.FromContext(ctx).Errorf("Skipping extension %s as it has a invalid name", extension.GetPath()) - continue - } - - extPath := extension.GetPath() - - if _, err := os.Stat(path.Join(extension.GetRootDir(), "Resources")); os.IsNotExist(err) { - logging.FromContext(ctx).Infof("Skipping building of assets for extension %s as it doesnt contain assets", extName) - continue - } - - list[extName] = createConfigFromPath(extName, extension.GetRootDir()) - - extCfg, err := readExtensionConfig(extPath) - if err != nil { - logging.FromContext(ctx).Errorf("Skipping extension additional bundles %s as it has a invalid config", extPath) - continue - } - - for _, bundle := range extCfg.Build.ExtraBundles { - bundleName := bundle.Name - - if bundleName == "" { - bundleName = filepath.Base(bundle.Path) - } - - list[bundleName] = createConfigFromPath(bundleName, path.Join(extension.GetRootDir(), bundle.Path)) - } + for _, source := range sources { + list[source.Name] = createConfigFromPath(source.Name, source.Path) } var basePath string @@ -362,8 +337,8 @@ func createConfigFromPath(entryPointName string, extensionRoot string) Extension return cfg } -func setupShopwareInTemp(ctx context.Context, ext Extension) (string, error) { - minVersion, err := lookupForMinMatchingVersion(ctx, ext) +func setupShopwareInTemp(ctx context.Context, shopwareVersionConstraint *version.Constraints) (string, error) { + minVersion, err := lookupForMinMatchingVersion(ctx, shopwareVersionConstraint) if err != nil { return "", err } diff --git a/extension/bundle.go b/extension/bundle.go new file mode 100644 index 00000000..79467a15 --- /dev/null +++ b/extension/bundle.go @@ -0,0 +1,143 @@ +package extension + +import ( + "context" + "encoding/json" + "fmt" + "os" + "path" + + "github.com/FriendsOfShopware/shopware-cli/version" +) + +type ShopwareBundle struct { + path string + composer shopwareBundleComposerJson + config *Config +} + +func newShopwareBundle(path string) (*ShopwareBundle, error) { + composerJsonFile := fmt.Sprintf("%s/composer.json", path) + if _, err := os.Stat(composerJsonFile); err != nil { + return nil, err + } + + jsonFile, err := os.ReadFile(composerJsonFile) + if err != nil { + return nil, fmt.Errorf("newShopwareBundle: %v", err) + } + + var composerJson shopwareBundleComposerJson + err = json.Unmarshal(jsonFile, &composerJson) + + if err != nil { + return nil, fmt.Errorf("newShopwareBundle: %v", err) + } + + cfg, err := readExtensionConfig(path) + if err != nil { + return nil, fmt.Errorf("newShopwareBundle: %v", err) + } + + extension := ShopwareBundle{ + composer: composerJson, + path: path, + config: cfg, + } + + return &extension, nil +} + +type shopwareBundleComposerJson struct { + Name string `json:"name"` + License string `json:"license"` + Version string `json:"version"` + Require map[string]string `json:"require"` + Extra shopwareBundleComposerJsonExtra `json:"extra"` +} + +type shopwareBundleComposerJsonExtra struct { + BundleName string `json:"shopware-bundle-name"` +} + +// GetRootDir returns the src directory of the bundle. +func (p ShopwareBundle) GetRootDir() string { + return p.path +} + +// GetResourcesDir returns the resources directory of the shopware bundle. +func (p ShopwareBundle) GetResourcesDir() string { + return path.Join(p.GetRootDir(), "Resources") +} + +func (p ShopwareBundle) GetName() (string, error) { + if p.composer.Extra.BundleName == "" { + return "", fmt.Errorf("composer.json does not contain shopware-bundle-name in extra") + } + + return p.composer.Extra.BundleName, nil +} + +func (p ShopwareBundle) GetExtensionConfig() *Config { + return p.config +} + +func (p ShopwareBundle) GetShopwareVersionConstraint() (*version.Constraints, error) { + if p.config != nil && p.config.Build.ShopwareVersionConstraint != "" { + constraint, err := version.NewConstraint(p.config.Build.ShopwareVersionConstraint) + if err != nil { + return nil, err + } + + return &constraint, nil + } + + shopwareConstraintString, ok := p.composer.Require["shopware/core"] + + if !ok { + return nil, fmt.Errorf("require.shopware/core is required") + } + + shopwareConstraint, err := version.NewConstraint(shopwareConstraintString) + if err != nil { + return nil, err + } + + return &shopwareConstraint, err +} + +func (ShopwareBundle) GetType() string { + return TypeShopwareBundle +} + +func (p ShopwareBundle) GetVersion() (*version.Version, error) { + return version.NewVersion(p.composer.Version) +} + +func (p ShopwareBundle) GetChangelog() (*extensionTranslated, error) { + return parseExtensionMarkdownChangelog(p) +} + +func (p ShopwareBundle) GetLicense() (string, error) { + return p.composer.License, nil +} + +func (p ShopwareBundle) GetPath() string { + return p.path +} + +func (p ShopwareBundle) GetMetaData() *extensionMetadata { + return &extensionMetadata{ + Label: extensionTranslated{ + German: "FALLBACK", + English: "FALLBACK", + }, + Description: extensionTranslated{ + German: "FALLBACK", + English: "FALLBACK", + }, + } +} + +func (p ShopwareBundle) Validate(c context.Context, ctx *ValidationContext) { +} diff --git a/extension/platform.go b/extension/platform.go index a8765253..25380263 100644 --- a/extension/platform.go +++ b/extension/platform.go @@ -55,6 +55,10 @@ func newPlatformPlugin(path string) (*PlatformPlugin, error) { return nil, fmt.Errorf("newPlatformPlugin: %v", err) } + if composerJson.Type != "shopware-platform-plugin" { + return nil, fmt.Errorf("newPlatformPlugin: composer.json is not type of shopware-platform-plugin", err) + } + cfg, err := readExtensionConfig(path) if err != nil { return nil, fmt.Errorf("newPlatformPlugin: %v", err) diff --git a/extension/project.go b/extension/project.go index fdd3ec65..ae1b8012 100644 --- a/extension/project.go +++ b/extension/project.go @@ -3,12 +3,72 @@ package extension import ( "context" "encoding/json" + "fmt" "os" "path" + "path/filepath" + "github.com/FriendsOfShopware/shopware-cli/internal/asset" "github.com/FriendsOfShopware/shopware-cli/logging" + "github.com/FriendsOfShopware/shopware-cli/version" ) +func GetShopwareProjectConstraint(project string) (*version.Constraints, error) { + composerJson, err := os.ReadFile(path.Join(project, "composer.json")) + var composer rootComposerJson + + err = json.Unmarshal(composerJson, &composer) + if err != nil { + return nil, err + } + + constraint, ok := composer.Require["shopware/core"] + + if !ok { + return nil, fmt.Errorf("cannot find shopware/core in composer.json") + } + + c, err := version.NewConstraint(constraint) + if err != nil { + return nil, err + } + + return &c, nil +} + +func FindAssetSourcesOfProject(ctx context.Context, project string) []asset.Source { + extensions := FindExtensionsFromProject(ctx, project) + sources := ConvertExtensionsToSources(ctx, extensions) + + composerJson, err := os.ReadFile(path.Join(project, "composer.json")) + if err != nil { + logging.FromContext(ctx).Errorf("Cannot read composer.json: %s", err.Error()) + } + + var composer rootComposerJson + + err = json.Unmarshal(composerJson, &composer) + if err != nil { + logging.FromContext(ctx).Errorf("Cannot parse composer.json: %s", err.Error()) + return sources + } + + for bundlePath, bundle := range composer.Extra.Bundles { + name := bundle.Name + + if name == "" { + name = filepath.Base(bundlePath) + } + + sources = append(sources, asset.Source{ + Name: name, + Path: path.Join(project, bundlePath), + }) + } + + return sources +} + func FindExtensionsFromProject(ctx context.Context, project string) []Extension { extensions := make(map[string]Extension) @@ -123,3 +183,14 @@ type composerLock struct { PackageType string `json:"type"` } `json:"packages"` } + +type rootComposerJson struct { + Require map[string]string `json:"require"` + Extra struct { + Bundles map[string]rootShopwareBundle `json:"shopware-bundles"` + } +} + +type rootShopwareBundle struct { + Name string `json:"name"` +} diff --git a/extension/root.go b/extension/root.go index 8a710c74..7c056829 100644 --- a/extension/root.go +++ b/extension/root.go @@ -14,6 +14,7 @@ import ( const ( TypePlatformApp = "app" TypePlatformPlugin = "plugin" + TypeShopwareBundle = "shopware-bundle" ) func GetExtensionByFolder(path string) (Extension, error) { @@ -29,7 +30,14 @@ func GetExtensionByFolder(path string) (Extension, error) { return nil, fmt.Errorf("unknown extension type") } - return newPlatformPlugin(path) + var ext Extension + + ext, err := newPlatformPlugin(path) + if err != nil { + ext, err = newShopwareBundle(path) + } + + return ext, err } func GetExtensionByZip(filePath string) (Extension, error) { diff --git a/extension/zip.go b/extension/zip.go index 2e64679d..af6fe946 100644 --- a/extension/zip.go +++ b/extension/zip.go @@ -214,7 +214,12 @@ func PrepareFolderForZipping(ctx context.Context, path string, ext Extension, ex return fmt.Errorf(errorFormat, err) } - minVersion, err := lookupForMinMatchingVersion(ctx, ext) + minShopwareVersionConstraint, err := ext.GetShopwareVersionConstraint() + if err != nil { + return fmt.Errorf(errorFormat, err) + } + + minVersion, err := lookupForMinMatchingVersion(ctx, minShopwareVersionConstraint) if err != nil { return fmt.Errorf("lookup for min matching version: %w", err) } @@ -376,7 +381,7 @@ func addComposerReplacements(ctx context.Context, composer map[string]interface{ return composer, nil } -func lookupForMinMatchingVersion(ctx context.Context, ext Extension) (string, error) { +func lookupForMinMatchingVersion(ctx context.Context, versionConstraint *version.Constraints) (string, error) { req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://swagger.docs.fos.gg/composer/versions.json", http.NoBody) if err != nil { return "", fmt.Errorf("create composer version request: %w", err) @@ -403,11 +408,6 @@ func lookupForMinMatchingVersion(ctx context.Context, ext Extension) (string, er return "", fmt.Errorf("unmarshal composer versions: %w", err) } - versionConstraint, err := ext.GetShopwareVersionConstraint() - if err != nil { - return "", fmt.Errorf("get shopware version constraint: %w", err) - } - return getMinMatchingVersion(versionConstraint, versions) } diff --git a/internal/asset/source.go b/internal/asset/source.go new file mode 100644 index 00000000..8f79f626 --- /dev/null +++ b/internal/asset/source.go @@ -0,0 +1,6 @@ +package asset + +type Source struct { + Name string + Path string +} From 98655e40e50a0ef71f45cb1cdef57f0fbc5c8763 Mon Sep 17 00:00:00 2001 From: Soner Sayakci Date: Sun, 24 Sep 2023 11:26:38 +0000 Subject: [PATCH 2/5] feat: add some asset tests --- cmd/extension/extension_admin_watch.go | 2 +- extension/asset.go | 22 +-- extension/asset_platform.go | 18 ++- extension/asset_platform_test.go | 209 +++---------------------- extension/asset_test.go | 113 +++++++++++++ extension/platform.go | 2 +- internal/esbuild/esbuild.go | 24 +-- internal/esbuild/esbuild_test.go | 81 ++++++++++ 8 files changed, 248 insertions(+), 223 deletions(-) create mode 100644 extension/asset_test.go create mode 100644 internal/esbuild/esbuild_test.go diff --git a/cmd/extension/extension_admin_watch.go b/cmd/extension/extension_admin_watch.go index 4e7d59f2..c2c30e82 100644 --- a/cmd/extension/extension_admin_watch.go +++ b/cmd/extension/extension_admin_watch.go @@ -67,7 +67,7 @@ var extensionAdminWatchCmd = &cobra.Command{ name, _ := ext.GetName() - options := esbuild.NewAssetCompileOptionsAdmin(name, ext.GetPath(), ext.GetType()) + options := esbuild.NewAssetCompileOptionsAdmin(name, ext.GetPath()) options.ProductionMode = false esbuildContext, esBuildError := esbuild.Context(cmd.Context(), options) diff --git a/extension/asset.go b/extension/asset.go index 05b887e8..b962eecb 100644 --- a/extension/asset.go +++ b/extension/asset.go @@ -26,17 +26,19 @@ func ConvertExtensionsToSources(ctx context.Context, extensions []Extension) []a extConfig := ext.GetExtensionConfig() - for _, bundle := range extConfig.Build.ExtraBundles { - bundleName := bundle.Name - - if bundleName == "" { - bundleName = filepath.Base(bundle.Path) + if extConfig != nil { + for _, bundle := range extConfig.Build.ExtraBundles { + bundleName := bundle.Name + + if bundleName == "" { + bundleName = filepath.Base(bundle.Path) + } + + sources = append(sources, asset.Source{ + Name: bundleName, + Path: path.Join(ext.GetRootDir(), bundle.Path), + }) } - - sources = append(sources, asset.Source{ - Name: bundleName, - Path: path.Join(ext.GetRootDir(), bundle.Path), - }) } } diff --git a/extension/asset_platform.go b/extension/asset_platform.go index 635e97c5..55183ac5 100644 --- a/extension/asset_platform.go +++ b/extension/asset_platform.go @@ -37,7 +37,7 @@ type AssetBuildConfig struct { } func BuildAssetsForExtensions(ctx context.Context, sources []asset.Source, assetConfig AssetBuildConfig) error { // nolint:gocyclo - cfgs := buildAssetConfigFromExtensions(ctx, sources, assetConfig.ShopwareRoot) + cfgs := buildAssetConfigFromExtensions(sources, assetConfig.ShopwareRoot) if len(cfgs) == 1 { return nil @@ -114,7 +114,7 @@ func BuildAssetsForExtensions(ctx context.Context, sources []asset.Source, asset } // @todo: fix me later - options := esbuild.NewAssetCompileOptionsAdmin(source.Name, source.Path, "") + options := esbuild.NewAssetCompileOptionsAdmin(source.Name, source.Path) if _, err := esbuild.CompileExtensionAsset(ctx, options); err != nil { return err @@ -147,7 +147,7 @@ func BuildAssetsForExtensions(ctx context.Context, sources []asset.Source, asset } // @todo: fix me later - options := esbuild.NewAssetCompileOptionsStorefront(source.Name, source.Path, "") + options := esbuild.NewAssetCompileOptionsStorefront(source.Name, source.Path) if _, err := esbuild.CompileExtensionAsset(ctx, options); err != nil { return err } @@ -241,10 +241,20 @@ func prepareShopwareForAsset(shopwareRoot string, cfgs map[string]ExtensionAsset return nil } -func buildAssetConfigFromExtensions(ctx context.Context, sources []asset.Source, shopwareRoot string) ExtensionAssetConfig { +func buildAssetConfigFromExtensions(sources []asset.Source, shopwareRoot string) ExtensionAssetConfig { list := make(ExtensionAssetConfig) for _, source := range sources { + if source.Name == "" { + continue + } + + resourcesDir := path.Join(source.Path, "Resources", "app") + + if _, err := os.Stat(resourcesDir); os.IsNotExist(err) { + continue + } + list[source.Name] = createConfigFromPath(source.Name, source.Path) } diff --git a/extension/asset_platform_test.go b/extension/asset_platform_test.go index 4112a0e6..d6aba847 100644 --- a/extension/asset_platform_test.go +++ b/extension/asset_platform_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/assert" + "github.com/FriendsOfShopware/shopware-cli/internal/asset" "github.com/FriendsOfShopware/shopware-cli/logging" ) @@ -17,24 +18,15 @@ func getTestContext() context.Context { return logging.WithLogger(context.TODO(), logger) } -func TestGenerateConfigForPlugin(t *testing.T) { +func TestGenerateConfigWithAdminAndStorefrontFiles(t *testing.T) { dir := t.TempDir() - plugin := PlatformPlugin{ - path: dir, - composer: platformComposerJson{ - Extra: platformComposerJsonExtra{ - ShopwarePluginClass: "FroshTools\\FroshTools", - }, - }, - } - - assert.NoError(t, os.MkdirAll(path.Join(dir, "src", "Resources", "app", "administration", "src"), os.ModePerm)) - assert.NoError(t, os.WriteFile(path.Join(dir, "src", "Resources", "app", "administration", "src", "main.js"), []byte("test"), os.ModePerm)) - assert.NoError(t, os.MkdirAll(path.Join(dir, "src", "Resources", "app", "storefront", "src"), os.ModePerm)) - assert.NoError(t, os.WriteFile(path.Join(dir, "src", "Resources", "app", "storefront", "src", "main.js"), []byte("test"), os.ModePerm)) + assert.NoError(t, os.MkdirAll(path.Join(dir, "Resources", "app", "administration", "src"), os.ModePerm)) + assert.NoError(t, os.WriteFile(path.Join(dir, "Resources", "app", "administration", "src", "main.js"), []byte("test"), os.ModePerm)) + assert.NoError(t, os.MkdirAll(path.Join(dir, "Resources", "app", "storefront", "src"), os.ModePerm)) + assert.NoError(t, os.WriteFile(path.Join(dir, "Resources", "app", "storefront", "src", "main.js"), []byte("test"), os.ModePerm)) - config := buildAssetConfigFromExtensions(getTestContext(), []Extension{plugin}, "") + config := buildAssetConfigFromExtensions([]asset.Source{{Name: "FroshTools", Path: dir}}, "") assert.True(t, config.Has("FroshTools")) assert.True(t, config.RequiresAdminBuild()) @@ -48,32 +40,23 @@ func TestGenerateConfigForPlugin(t *testing.T) { assert.Equal(t, "Resources/app/storefront/src", config["FroshTools"].Storefront.Path) } -func TestGenerateConfigForPluginInTypeScript(t *testing.T) { +func TestGenerateConfigWithTypeScript(t *testing.T) { dir := t.TempDir() - plugin := PlatformPlugin{ - path: dir, - composer: platformComposerJson{ - Extra: platformComposerJsonExtra{ - ShopwarePluginClass: "FroshTools\\FroshTools", - }, - }, - } - - assert.NoError(t, os.MkdirAll(path.Join(dir, "src", "Resources", "app", "administration", "src"), os.ModePerm)) - assert.NoError(t, os.MkdirAll(path.Join(dir, "src", "Resources", "app", "administration", "build"), os.ModePerm)) + assert.NoError(t, os.MkdirAll(path.Join(dir, "Resources", "app", "administration", "src"), os.ModePerm)) + assert.NoError(t, os.MkdirAll(path.Join(dir, "Resources", "app", "administration", "build"), os.ModePerm)) - assert.NoError(t, os.MkdirAll(path.Join(dir, "src", "Resources", "app", "storefront", "src"), os.ModePerm)) - assert.NoError(t, os.MkdirAll(path.Join(dir, "src", "Resources", "app", "storefront", "build"), os.ModePerm)) + assert.NoError(t, os.MkdirAll(path.Join(dir, "Resources", "app", "storefront", "src"), os.ModePerm)) + assert.NoError(t, os.MkdirAll(path.Join(dir, "Resources", "app", "storefront", "build"), os.ModePerm)) - assert.NoError(t, os.WriteFile(path.Join(dir, "src", "Resources", "app", "administration", "src", "main.ts"), []byte("test"), os.ModePerm)) + assert.NoError(t, os.WriteFile(path.Join(dir, "Resources", "app", "administration", "src", "main.ts"), []byte("test"), os.ModePerm)) - assert.NoError(t, os.WriteFile(path.Join(dir, "src", "Resources", "app", "administration", "build", "webpack.config.js"), []byte("test"), os.ModePerm)) + assert.NoError(t, os.WriteFile(path.Join(dir, "Resources", "app", "administration", "build", "webpack.config.js"), []byte("test"), os.ModePerm)) - assert.NoError(t, os.WriteFile(path.Join(dir, "src", "Resources", "app", "storefront", "src", "main.ts"), []byte("test"), os.ModePerm)) - assert.NoError(t, os.WriteFile(path.Join(dir, "src", "Resources", "app", "storefront", "build", "webpack.config.js"), []byte("test"), os.ModePerm)) + assert.NoError(t, os.WriteFile(path.Join(dir, "Resources", "app", "storefront", "src", "main.ts"), []byte("test"), os.ModePerm)) + assert.NoError(t, os.WriteFile(path.Join(dir, "Resources", "app", "storefront", "build", "webpack.config.js"), []byte("test"), os.ModePerm)) - config := buildAssetConfigFromExtensions(getTestContext(), []Extension{plugin}, "") + config := buildAssetConfigFromExtensions([]asset.Source{{Name: "FroshTools", Path: dir}}, "") assert.True(t, config.Has("FroshTools")) assert.True(t, config.RequiresAdminBuild()) @@ -87,43 +70,8 @@ func TestGenerateConfigForPluginInTypeScript(t *testing.T) { assert.Equal(t, "Resources/app/storefront/src", config["FroshTools"].Storefront.Path) } -func TestGenerateConfigForApp(t *testing.T) { - dir := t.TempDir() - - app := App{ - path: dir, - manifest: appManifest{ - Meta: appManifestMeta{ - Name: "FroshApp", - }, - }, - } - - assert.NoError(t, os.MkdirAll(path.Join(dir, "Resources", "app", "storefront", "src"), os.ModePerm)) - - assert.NoError(t, os.MkdirAll(path.Join(dir, "Resources", "app", "storefront", "build"), os.ModePerm)) - - assert.NoError(t, os.WriteFile(path.Join(dir, "Resources", "app", "storefront", "src", "main.ts"), []byte("test"), os.ModePerm)) - - assert.NoError(t, os.WriteFile(path.Join(dir, "Resources", "app", "storefront", "build", "webpack.config.js"), []byte("test"), os.ModePerm)) - - config := buildAssetConfigFromExtensions(getTestContext(), []Extension{app}, "") - - assert.True(t, config.Has("FroshApp")) - assert.False(t, config.RequiresAdminBuild()) - assert.True(t, config.RequiresStorefrontBuild()) - - assert.Equal(t, "frosh-app", config["FroshApp"].TechnicalName) - assert.Nil(t, config["FroshApp"].Administration.EntryFilePath) - assert.Equal(t, "Resources/app/storefront/src/main.ts", *config["FroshApp"].Storefront.EntryFilePath) - assert.Nil(t, config["FroshApp"].Administration.Webpack) - assert.Equal(t, "Resources/app/storefront/build/webpack.config.js", *config["FroshApp"].Storefront.Webpack) - assert.Equal(t, "Resources/app/administration/src", config["FroshApp"].Administration.Path) - assert.Equal(t, "Resources/app/storefront/src", config["FroshApp"].Storefront.Path) -} - func TestGenerateConfigAddsStorefrontAlwaysAsEntrypoint(t *testing.T) { - config := buildAssetConfigFromExtensions(getTestContext(), []Extension{}, "") + config := buildAssetConfigFromExtensions([]asset.Source{}, "") assert.False(t, config.RequiresStorefrontBuild()) assert.False(t, config.RequiresAdminBuild()) @@ -132,16 +80,7 @@ func TestGenerateConfigAddsStorefrontAlwaysAsEntrypoint(t *testing.T) { func TestGenerateConfigDoesNotAddExtensionWithoutConfig(t *testing.T) { dir := t.TempDir() - app := App{ - path: dir, - manifest: appManifest{ - Meta: appManifestMeta{ - Name: "FroshApp", - }, - }, - } - - config := buildAssetConfigFromExtensions(getTestContext(), []Extension{app}, "") + config := buildAssetConfigFromExtensions([]asset.Source{{Name: "FroshApp", Path: dir}}, "") assert.False(t, config.Has("FroshApp")) } @@ -149,115 +88,7 @@ func TestGenerateConfigDoesNotAddExtensionWithoutConfig(t *testing.T) { func TestGenerateConfigDoesNotAddExtensionWithoutName(t *testing.T) { dir := t.TempDir() - plugin := PlatformPlugin{ - path: dir, - composer: platformComposerJson{ - Extra: platformComposerJsonExtra{ - ShopwarePluginClass: "", - }, - }, - } - - config := buildAssetConfigFromExtensions(getTestContext(), []Extension{plugin}, "") + config := buildAssetConfigFromExtensions([]asset.Source{{Name: "", Path: dir}}, "") assert.Len(t, config, 1) } - -func TestGenerateConfigWithSubBundles(t *testing.T) { - dir := t.TempDir() - - plugin := PlatformPlugin{ - path: dir, - composer: platformComposerJson{ - Extra: platformComposerJsonExtra{ - ShopwarePluginClass: "FroshTools", - }, - }, - } - - assert.NoError(t, os.MkdirAll(path.Join(dir, "src", "Resources", "app", "administration", "src"), os.ModePerm)) - - assert.NoError(t, os.WriteFile(path.Join(dir, "src", "Resources", "app", "administration", "src", "main.ts"), []byte("test"), os.ModePerm)) - - assert.NoError(t, os.MkdirAll(path.Join(dir, "src", "Foo", "Resources", "app", "administration", "src"), os.ModePerm)) - - assert.NoError(t, os.WriteFile(path.Join(dir, "src", "Foo", "Resources", "app", "administration", "src", "main.ts"), []byte("test"), os.ModePerm)) - - cfg := "{\"build\": {\"extraBundles\": [{\"path\": \"Foo\"}]}}" - - assert.NoError(t, os.WriteFile(path.Join(dir, ".shopware-extension.yml"), []byte(cfg), os.ModePerm)) - - config := buildAssetConfigFromExtensions(getTestContext(), []Extension{plugin}, "") - - assert.True(t, config.RequiresAdminBuild()) - assert.False(t, config.RequiresStorefrontBuild()) - assert.True(t, config.Has("FroshTools")) - assert.True(t, config.Has("Foo")) - - assert.Equal(t, "frosh-tools", config["FroshTools"].TechnicalName) - assert.Equal(t, "Resources/app/administration/src/main.ts", *config["FroshTools"].Administration.EntryFilePath) - assert.Nil(t, config["FroshTools"].Administration.Webpack) - assert.Nil(t, config["FroshTools"].Storefront.EntryFilePath) - assert.Nil(t, config["FroshTools"].Storefront.Webpack) - assert.Equal(t, "Resources/app/administration/src", config["FroshTools"].Administration.Path) - assert.Equal(t, "Resources/app/storefront/src", config["FroshTools"].Storefront.Path) - - assert.Equal(t, "foo", config["Foo"].TechnicalName) - assert.Contains(t, config["Foo"].BasePath, "src/Foo") - assert.Equal(t, "Resources/app/administration/src/main.ts", *config["Foo"].Administration.EntryFilePath) - assert.Nil(t, config["Foo"].Administration.Webpack) - assert.Nil(t, config["Foo"].Storefront.EntryFilePath) - assert.Nil(t, config["Foo"].Storefront.Webpack) - assert.Equal(t, "Resources/app/administration/src", config["Foo"].Administration.Path) - assert.Equal(t, "Resources/app/storefront/src", config["Foo"].Storefront.Path) -} - -func TestGenerateConfigWithSubBundlesWithNameOverride(t *testing.T) { - dir := t.TempDir() - - plugin := PlatformPlugin{ - path: dir, - composer: platformComposerJson{ - Extra: platformComposerJsonExtra{ - ShopwarePluginClass: "FroshTools", - }, - }, - } - - assert.NoError(t, os.MkdirAll(path.Join(dir, "src", "Resources", "app", "administration", "src"), os.ModePerm)) - - assert.NoError(t, os.WriteFile(path.Join(dir, "src", "Resources", "app", "administration", "src", "main.ts"), []byte("test"), os.ModePerm)) - - assert.NoError(t, os.MkdirAll(path.Join(dir, "src", "Foo", "Resources", "app", "administration", "src"), os.ModePerm)) - - assert.NoError(t, os.WriteFile(path.Join(dir, "src", "Foo", "Resources", "app", "administration", "src", "main.ts"), []byte("test"), os.ModePerm)) - - cfg := "{\"build\": {\"extraBundles\": [{\"path\": \"Foo\", \"name\": \"Bla\"}]}}" - - assert.NoError(t, os.WriteFile(path.Join(dir, ".shopware-extension.yml"), []byte(cfg), os.ModePerm)) - - config := buildAssetConfigFromExtensions(getTestContext(), []Extension{plugin}, "") - - assert.True(t, config.RequiresAdminBuild()) - assert.False(t, config.RequiresStorefrontBuild()) - - assert.True(t, config.Has("FroshTools")) - assert.True(t, config.Has("Bla")) - - assert.Equal(t, "frosh-tools", config["FroshTools"].TechnicalName) - assert.Equal(t, "Resources/app/administration/src/main.ts", *config["FroshTools"].Administration.EntryFilePath) - assert.Nil(t, config["FroshTools"].Administration.Webpack) - assert.Nil(t, config["FroshTools"].Storefront.EntryFilePath) - assert.Nil(t, config["FroshTools"].Storefront.Webpack) - assert.Equal(t, "Resources/app/administration/src", config["FroshTools"].Administration.Path) - assert.Equal(t, "Resources/app/storefront/src", config["FroshTools"].Storefront.Path) - - assert.Equal(t, "bla", config["Bla"].TechnicalName) - assert.Contains(t, config["Bla"].BasePath, "src/Foo") - assert.Equal(t, "Resources/app/administration/src/main.ts", *config["Bla"].Administration.EntryFilePath) - assert.Nil(t, config["Bla"].Administration.Webpack) - assert.Nil(t, config["Bla"].Storefront.EntryFilePath) - assert.Nil(t, config["Bla"].Storefront.Webpack) - assert.Equal(t, "Resources/app/administration/src", config["Bla"].Administration.Path) - assert.Equal(t, "Resources/app/storefront/src", config["Bla"].Storefront.Path) -} diff --git a/extension/asset_test.go b/extension/asset_test.go new file mode 100644 index 00000000..e734a13f --- /dev/null +++ b/extension/asset_test.go @@ -0,0 +1,113 @@ +package extension + +import ( + "path" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConvertPlugin(t *testing.T) { + plugin := PlatformPlugin{ + path: t.TempDir(), + composer: platformComposerJson{ + Extra: platformComposerJsonExtra{ + ShopwarePluginClass: "FroshTools\\FroshTools", + }, + }, + } + + assetSource := ConvertExtensionsToSources(getTestContext(), []Extension{plugin}) + + assert.Len(t, assetSource, 1) + froshTools := assetSource[0] + + assert.Equal(t, "FroshTools", froshTools.Name) + assert.Equal(t, path.Join(plugin.path, "src"), froshTools.Path) +} + +func TestConvertApp(t *testing.T) { + app := App{ + path: t.TempDir(), + manifest: appManifest{ + Meta: appManifestMeta{ + Name: "TestApp", + }, + }, + } + + assetSource := ConvertExtensionsToSources(getTestContext(), []Extension{app}) + + assert.Len(t, assetSource, 1) + froshTools := assetSource[0] + + assert.Equal(t, "TestApp", froshTools.Name) + assert.Equal(t, app.path, froshTools.Path) +} + +func TestConvertExtraBundlesOfConfig(t *testing.T) { + app := App{ + path: t.TempDir(), + manifest: appManifest{ + Meta: appManifestMeta{ + Name: "TestApp", + }, + }, + config: &Config{ + Build: ConfigBuild{ + ExtraBundles: []ConfigExtraBundle{ + { + Path: "src/Fooo", + }, + }, + }, + }, + } + + assetSource := ConvertExtensionsToSources(getTestContext(), []Extension{app}) + + assert.Len(t, assetSource, 2) + sourceOne := assetSource[0] + + assert.Equal(t, "TestApp", sourceOne.Name) + assert.Equal(t, app.path, sourceOne.Path) + + sourceExtra := assetSource[1] + + assert.Equal(t, "Fooo", sourceExtra.Name) + assert.Equal(t, path.Join(app.path, "src", "Fooo"), sourceExtra.Path) +} + +func TestConvertExtraBundlesOfConfigWithOverride(t *testing.T) { + app := App{ + path: t.TempDir(), + manifest: appManifest{ + Meta: appManifestMeta{ + Name: "TestApp", + }, + }, + config: &Config{ + Build: ConfigBuild{ + ExtraBundles: []ConfigExtraBundle{ + { + Name: "Bla", + Path: "src/Fooo", + }, + }, + }, + }, + } + + assetSource := ConvertExtensionsToSources(getTestContext(), []Extension{app}) + + assert.Len(t, assetSource, 2) + sourceOne := assetSource[0] + + assert.Equal(t, "TestApp", sourceOne.Name) + assert.Equal(t, app.path, sourceOne.Path) + + sourceExtra := assetSource[1] + + assert.Equal(t, "Bla", sourceExtra.Name) + assert.Equal(t, path.Join(app.path, "src", "Fooo"), sourceExtra.Path) +} diff --git a/extension/platform.go b/extension/platform.go index 25380263..4987a12a 100644 --- a/extension/platform.go +++ b/extension/platform.go @@ -56,7 +56,7 @@ func newPlatformPlugin(path string) (*PlatformPlugin, error) { } if composerJson.Type != "shopware-platform-plugin" { - return nil, fmt.Errorf("newPlatformPlugin: composer.json is not type of shopware-platform-plugin", err) + return nil, fmt.Errorf("newPlatformPlugin: composer.json is not type of shopware-platform-plugin") } cfg, err := readExtensionConfig(path) diff --git a/internal/esbuild/esbuild.go b/internal/esbuild/esbuild.go index 80f34091..3a34b81f 100644 --- a/internal/esbuild/esbuild.go +++ b/internal/esbuild/esbuild.go @@ -26,34 +26,22 @@ type AssetCompileOptions struct { Path string } -func NewAssetCompileOptionsAdmin(name, path, extType string) AssetCompileOptions { - root := "src/" - - if extType == "app" { - root = "" - } - +func NewAssetCompileOptionsAdmin(name, path string) AssetCompileOptions { return AssetCompileOptions{ Name: name, Path: path, - EntrypointDir: root + "Resources/app/administration/src", - OutputDir: root + "Resources/public/administration", + EntrypointDir: "Resources/app/administration/src", + OutputDir: "Resources/public/administration", ProductionMode: true, } } -func NewAssetCompileOptionsStorefront(name, path, extType string) AssetCompileOptions { - root := "src/" - - if extType == "app" { - root = "" - } - +func NewAssetCompileOptionsStorefront(name, path string) AssetCompileOptions { return AssetCompileOptions{ Name: name, Path: path, - EntrypointDir: root + "Resources/app/storefront/src", - OutputDir: root + "Resources/app/storefront/dist/storefront", + EntrypointDir: "Resources/app/storefront/src", + OutputDir: "Resources/app/storefront/dist/storefront", ProductionMode: true, } } diff --git a/internal/esbuild/esbuild_test.go b/internal/esbuild/esbuild_test.go new file mode 100644 index 00000000..9f699be6 --- /dev/null +++ b/internal/esbuild/esbuild_test.go @@ -0,0 +1,81 @@ +package esbuild + +import ( + "context" + "os" + "path" + "testing" + + "github.com/FriendsOfShopware/shopware-cli/logging" + "github.com/stretchr/testify/assert" +) + +func getTestContext() context.Context { + logger := logging.NewLogger() + + return logging.WithLogger(context.TODO(), logger) +} + +func TestESBuildAdminNoEntrypoint(t *testing.T) { + dir := t.TempDir() + + options := NewAssetCompileOptionsAdmin("Bla", dir) + _, err := CompileExtensionAsset(getTestContext(), options) + assert.Error(t, err) + assert.Contains(t, err.Error(), "cannot find entrypoint") +} + +func TestESBuildAdmin(t *testing.T) { + dir := t.TempDir() + + adminDir := path.Join(dir, "Resources", "app", "administration", "src") + _ = os.MkdirAll(adminDir, os.ModePerm) + + _ = os.WriteFile(path.Join(adminDir, "main.js"), []byte("console.log('bla')"), os.ModePerm) + + options := NewAssetCompileOptionsAdmin("Bla", dir) + _, err := CompileExtensionAsset(getTestContext(), options) + + assert.NoError(t, err) + + compiledFilePath := path.Join(dir, "Resources", "public", "administration", "js", "bla.js") + _, err = os.Stat(compiledFilePath) + assert.NoError(t, err) +} + +func TestESBuildAdminTypeScript(t *testing.T) { + dir := t.TempDir() + + adminDir := path.Join(dir, "Resources", "app", "administration", "src") + _ = os.MkdirAll(adminDir, os.ModePerm) + + _ = os.WriteFile(path.Join(adminDir, "main.ts"), []byte("console.log('bla')"), os.ModePerm) + + options := NewAssetCompileOptionsAdmin("Bla", dir) + result, err := CompileExtensionAsset(getTestContext(), options) + + assert.NoError(t, err) + assert.Contains(t, result.Entrypoint, "main.ts") + + compiledFilePath := path.Join(dir, "Resources", "public", "administration", "js", "bla.js") + _, err = os.Stat(compiledFilePath) + assert.NoError(t, err) +} + +func TestESBuildStorefront(t *testing.T) { + dir := t.TempDir() + + storefrontDir := path.Join(dir, "Resources", "app", "storefront", "src") + _ = os.MkdirAll(storefrontDir, os.ModePerm) + + _ = os.WriteFile(path.Join(storefrontDir, "main.js"), []byte("console.log('bla')"), os.ModePerm) + + options := NewAssetCompileOptionsStorefront("Bla", dir) + _, err := CompileExtensionAsset(getTestContext(), options) + + assert.NoError(t, err) + + compiledFilePath := path.Join(dir, "Resources", "app", "storefront", "dist", "storefront", "js", "bla.js") + _, err = os.Stat(compiledFilePath) + assert.NoError(t, err) +} From 63c1713717950102d463933fe7c20f78299b1612 Mon Sep 17 00:00:00 2001 From: Soner Sayakci Date: Sun, 24 Sep 2023 13:59:03 +0000 Subject: [PATCH 3/5] fix: cleanup to fix golangci-lint --- .devcontainer/devcontainer.json | 13 ++--- .vscode/settings.json | 2 +- cmd/project/platform.go | 1 - extension/bundle.go | 13 +++-- extension/bundle_test.go | 92 +++++++++++++++++++++++++++++++++ extension/platform.go | 4 +- extension/project.go | 20 +++++-- extension/root.go | 4 ++ 8 files changed, 131 insertions(+), 18 deletions(-) create mode 100644 extension/bundle_test.go diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 6d92c89b..9d95c5df 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -13,11 +13,12 @@ "customizations": { "vscode": { "extensions": [ - "golang.go-nightly", - "github.vscode-github-actions", - "redhat.vscode-yaml", - "liuchao.go-struct-tag" - ] + "golang.go-nightly", + "github.vscode-github-actions", + "redhat.vscode-yaml", + "liuchao.go-struct-tag", + "redhat.vscode-yaml" + ] } } -} +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 45716453..2eac898a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,5 @@ { - "go.formatTool": "gofumpt", + "go.formatTool": "gofmt", "go.useLanguageServer": true, "gopls": { "formatting.gofumpt": true, diff --git a/cmd/project/platform.go b/cmd/project/platform.go index 1f6f8955..dcaa95d0 100644 --- a/cmd/project/platform.go +++ b/cmd/project/platform.go @@ -44,4 +44,3 @@ func findClosestShopwareProject() (string, error) { return "", fmt.Errorf("cannot find Shopware project in current directory") } - diff --git a/extension/bundle.go b/extension/bundle.go index 79467a15..65912910 100644 --- a/extension/bundle.go +++ b/extension/bundle.go @@ -34,6 +34,14 @@ func newShopwareBundle(path string) (*ShopwareBundle, error) { return nil, fmt.Errorf("newShopwareBundle: %v", err) } + if composerJson.Type != "shopware-bundle" { + return nil, fmt.Errorf("newShopwareBundle: composer.json type is not shopware-bundle") + } + + if composerJson.Extra.BundleName == "" { + return nil, fmt.Errorf("composer.json does not contain shopware-bundle-name in extra") + } + cfg, err := readExtensionConfig(path) if err != nil { return nil, fmt.Errorf("newShopwareBundle: %v", err) @@ -50,6 +58,7 @@ func newShopwareBundle(path string) (*ShopwareBundle, error) { type shopwareBundleComposerJson struct { Name string `json:"name"` + Type string `json:"type"` License string `json:"license"` Version string `json:"version"` Require map[string]string `json:"require"` @@ -71,10 +80,6 @@ func (p ShopwareBundle) GetResourcesDir() string { } func (p ShopwareBundle) GetName() (string, error) { - if p.composer.Extra.BundleName == "" { - return "", fmt.Errorf("composer.json does not contain shopware-bundle-name in extra") - } - return p.composer.Extra.BundleName, nil } diff --git a/extension/bundle_test.go b/extension/bundle_test.go new file mode 100644 index 00000000..d725812a --- /dev/null +++ b/extension/bundle_test.go @@ -0,0 +1,92 @@ +package extension + +import ( + "os" + "path" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCreateBundleEmptyFolder(t *testing.T) { + dir := t.TempDir() + + bundle, err := newShopwareBundle(dir) + assert.Error(t, err) + assert.Nil(t, bundle) +} + +func TestCreateBundleInvalidComposerType(t *testing.T) { + dir := t.TempDir() + + // Create composer.json + composer := []byte(`{ + "name": "shopware/invalid", + "type": "invalid" + } + `) + _ = os.WriteFile(path.Join(dir, "composer.json"), composer, 0o644) + + bundle, err := newShopwareBundle(dir) + assert.Error(t, err) + assert.Contains(t, err.Error(), "composer.json type is not shopware-bundle") + assert.Nil(t, bundle) +} + +func TestCreateBundleMissingName(t *testing.T) { + dir := t.TempDir() + + // Create composer.json + composer := []byte(`{ + "name": "shopware/invalid", + "type": "shopware-bundle" + } + `) + _ = os.WriteFile(path.Join(dir, "composer.json"), composer, 0o644) + + bundle, err := newShopwareBundle(dir) + assert.Error(t, err) + assert.Contains(t, err.Error(), "composer.json does not contain shopware-bundle-name") + assert.Nil(t, bundle) +} + +func TestCreateBundle(t *testing.T) { + dir := t.TempDir() + + // Create composer.json + composer := []byte(`{ + "name": "shopware/invalid", + "version": "1.0.0", + "type": "shopware-bundle", + "extra": { + "shopware-bundle-name": "TestBundle" + } + } + `) + _ = os.WriteFile(path.Join(dir, "composer.json"), composer, 0o644) + + bundle, err := newShopwareBundle(dir) + assert.NoError(t, err) + + name, err := bundle.GetName() + assert.NoError(t, err) + + assert.Equal(t, "TestBundle", name) + assert.Equal(t, dir, bundle.GetRootDir()) + assert.Equal(t, dir, bundle.GetPath()) + assert.Equal(t, path.Join(dir, "Resources"), bundle.GetResourcesDir()) + assert.Equal(t, TypeShopwareBundle, bundle.GetType()) + + _, err = bundle.GetChangelog() + // changelog is missing + assert.Error(t, err) + + version, err := bundle.GetVersion() + assert.NoError(t, err) + assert.Equal(t, "1.0.0", version.String()) + + // does notthing + bundle.Validate(getTestContext(), &ValidationContext{}) + + assert.Equal(t, "FALLBACK", bundle.GetMetaData().Description.German) +} diff --git a/extension/platform.go b/extension/platform.go index 4987a12a..3523351a 100644 --- a/extension/platform.go +++ b/extension/platform.go @@ -55,7 +55,7 @@ func newPlatformPlugin(path string) (*PlatformPlugin, error) { return nil, fmt.Errorf("newPlatformPlugin: %v", err) } - if composerJson.Type != "shopware-platform-plugin" { + if composerJson.Type != ComposerTypePlugin { return nil, fmt.Errorf("newPlatformPlugin: composer.json is not type of shopware-platform-plugin") } @@ -179,7 +179,7 @@ func (p PlatformPlugin) Validate(c context.Context, ctx *ValidationContext) { if p.composer.Type == "" { ctx.AddError("Key `type` is required") - } else if p.composer.Type != "shopware-platform-plugin" { + } else if p.composer.Type != ComposerTypePlugin { ctx.AddError("The composer type must be shopware-platform-plugin") } diff --git a/extension/project.go b/extension/project.go index ae1b8012..65254dd3 100644 --- a/extension/project.go +++ b/extension/project.go @@ -15,6 +15,10 @@ import ( func GetShopwareProjectConstraint(project string) (*version.Constraints, error) { composerJson, err := os.ReadFile(path.Join(project, "composer.json")) + if err != nil { + return nil, err + } + var composer rootComposerJson err = json.Unmarshal(composerJson, &composer) @@ -134,19 +138,27 @@ func addExtensionsByComposer(project string) []Extension { } var composer composerLock - err = json.Unmarshal(lock, &composer) - - if err != nil { + if err := json.Unmarshal(lock, &composer); err != nil { return list } for _, pkg := range composer.Packages { - if pkg.PackageType == "shopware-platform-plugin" { + if pkg.PackageType == ComposerTypePlugin || pkg.PackageType == ComposerTypeBundle || pkg.PackageType == ComposerTypeApp { ext, err := GetExtensionByFolder(path.Join(project, "vendor", pkg.Name)) if err != nil { continue } + // The extension in the vendor folder has maybe not filled the version in this composer.json. Let's overwrite it with the version from composer.lock + switch pkg.PackageType { + case ComposerTypePlugin: + ext.(*PlatformPlugin).composer.Version = pkg.Version + case ComposerTypeApp: + ext.(*App).manifest.Meta.Version = pkg.Version + case ComposerTypeBundle: + ext.(*ShopwareBundle).composer.Version = pkg.Version + } + list = append(list, ext) } } diff --git a/extension/root.go b/extension/root.go index 7c056829..71159a68 100644 --- a/extension/root.go +++ b/extension/root.go @@ -15,6 +15,10 @@ const ( TypePlatformApp = "app" TypePlatformPlugin = "plugin" TypeShopwareBundle = "shopware-bundle" + + ComposerTypePlugin = "shopware-platform-plugin" + ComposerTypeApp = "shopware-app" + ComposerTypeBundle = "shopware-bundle" ) func GetExtensionByFolder(path string) (Extension, error) { From e6c74771e8cae1a16d85c8c598fced4a570a87f6 Mon Sep 17 00:00:00 2001 From: Soner Sayakci Date: Sun, 24 Sep 2023 14:04:34 +0000 Subject: [PATCH 4/5] fix: zip flip report --- cmd/project/ci.go | 2 -- extension/root.go | 8 +++++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cmd/project/ci.go b/cmd/project/ci.go index 1880b951..6c50fd04 100644 --- a/cmd/project/ci.go +++ b/cmd/project/ci.go @@ -73,8 +73,6 @@ var projectCI = &cobra.Command{ return err } - fmt.Println(sources) - assetCfg := extension.AssetBuildConfig{ EnableESBuildForAdmin: false, EnableESBuildForStorefront: false, diff --git a/extension/root.go b/extension/root.go index 71159a68..38f31122 100644 --- a/extension/root.go +++ b/extension/root.go @@ -66,7 +66,13 @@ func GetExtensionByZip(filePath string) (Extension, error) { return nil, err } - extName := strings.Split(file.File[0].Name, "/")[0] + fileName := file.File[0].Name + + if strings.Contains(fileName, "..") { + return nil, fmt.Errorf("invalid zip file") + } + + extName := strings.Split(fileName, "/")[0] return GetExtensionByFolder(fmt.Sprintf("%s/%s", dir, extName)) } From efd87bd5babb52dcc787ebf6e79561bec01a247b Mon Sep 17 00:00:00 2001 From: Soner Sayakci Date: Sun, 24 Sep 2023 19:29:58 +0000 Subject: [PATCH 5/5] fix: snippet merging for app installations --- cmd/project/ci.go | 2 +- extension/bundle.go | 2 +- extension/bundle_test.go | 4 ++-- extension/project.go | 2 ++ 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cmd/project/ci.go b/cmd/project/ci.go index 6c50fd04..3d24d8e5 100644 --- a/cmd/project/ci.go +++ b/cmd/project/ci.go @@ -303,7 +303,7 @@ func cleanupAdministrationFiles(ctx context.Context, folder string) error { logging.FromContext(ctx).Infof("Migrating generated snippet file for %s", folder) - snippetFolder := path.Join(adminFolder, "src", "snippet") + snippetFolder := path.Join(adminFolder, "src", "app", "snippet") if err := os.MkdirAll(snippetFolder, os.ModePerm); err != nil { return err } diff --git a/extension/bundle.go b/extension/bundle.go index 65912910..a3aef2b9 100644 --- a/extension/bundle.go +++ b/extension/bundle.go @@ -71,7 +71,7 @@ type shopwareBundleComposerJsonExtra struct { // GetRootDir returns the src directory of the bundle. func (p ShopwareBundle) GetRootDir() string { - return p.path + return path.Join(p.path, "src") } // GetResourcesDir returns the resources directory of the shopware bundle. diff --git a/extension/bundle_test.go b/extension/bundle_test.go index d725812a..4340afe3 100644 --- a/extension/bundle_test.go +++ b/extension/bundle_test.go @@ -72,9 +72,9 @@ func TestCreateBundle(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "TestBundle", name) - assert.Equal(t, dir, bundle.GetRootDir()) + assert.Equal(t, path.Join(dir, "src"), bundle.GetRootDir()) assert.Equal(t, dir, bundle.GetPath()) - assert.Equal(t, path.Join(dir, "Resources"), bundle.GetResourcesDir()) + assert.Equal(t, path.Join(dir, "src", "Resources"), bundle.GetResourcesDir()) assert.Equal(t, TypeShopwareBundle, bundle.GetType()) _, err = bundle.GetChangelog() diff --git a/extension/project.go b/extension/project.go index 65254dd3..49d1aeb4 100644 --- a/extension/project.go +++ b/extension/project.go @@ -64,6 +64,8 @@ func FindAssetSourcesOfProject(ctx context.Context, project string) []asset.Sour name = filepath.Base(bundlePath) } + logging.FromContext(ctx).Infof("Found bundle in project: %s (path: %s)", name, bundlePath) + sources = append(sources, asset.Source{ Name: name, Path: path.Join(project, bundlePath),