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
1 change: 1 addition & 0 deletions cmd/project/ci.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ var projectCI = &cobra.Command{
lookingForExtensionsSection.End(cmd.Context())

assetCfg := extension.AssetBuildConfig{
EnableAssetCaching: shopCfg.Build.AssetCaching,
CleanupNodeModules: true,
ShopwareRoot: args[0],
ShopwareVersion: shopwareConstraint,
Expand Down
6 changes: 6 additions & 0 deletions internal/asset/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,10 @@ type Source struct {
StorefrontEsbuildCompatible bool
DisableSass bool
NpmStrict bool
AdditionalCaches []AdditionalCache
}

type AdditionalCache struct {
Path string
SourcePaths []string
}
17 changes: 17 additions & 0 deletions internal/extension/asset.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,22 @@ import (
"github.com/shopware/shopware-cli/logging"
)

func convertAdditionalCaches(configCaches []ConfigBuildZipAssetsAdditionalCache) []asset.AdditionalCache {
if len(configCaches) == 0 {
return nil
}

caches := make([]asset.AdditionalCache, len(configCaches))
for i, cp := range configCaches {
caches[i] = asset.AdditionalCache{
Path: cp.Path,
SourcePaths: cp.SourcePaths,
}
}

return caches
}

func ConvertExtensionsToSources(ctx context.Context, extensions []Extension) []asset.Source {
sources := make([]asset.Source, 0)

Expand All @@ -25,6 +41,7 @@ func ConvertExtensionsToSources(ctx context.Context, extensions []Extension) []a
StorefrontEsbuildCompatible: ext.GetExtensionConfig().Build.Zip.Assets.EnableESBuildForStorefront,
DisableSass: ext.GetExtensionConfig().Build.Zip.Assets.DisableSass,
NpmStrict: ext.GetExtensionConfig().Build.Zip.Assets.NpmStrict,
AdditionalCaches: convertAdditionalCaches(ext.GetExtensionConfig().Build.Zip.Assets.AdditionalCaches),
})

extConfig := ext.GetExtensionConfig()
Expand Down
65 changes: 42 additions & 23 deletions internal/extension/asset_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,29 @@ import (
"context"
"errors"
"fmt"
"os"
"path"
"slices"

"github.com/cespare/xxhash/v2"
"golang.org/x/sync/errgroup"

"github.com/shopware/shopware-cli/internal/system"
"github.com/shopware/shopware-cli/logging"
)

var experimentalCachingEnabled bool

func init() {
experimentalCachingEnabled = os.Getenv("SHOPWARE_CLI_EXPERIMENTAL_ASSET_CACHING") == "1"
func hashCacheKeySuffix(p string) string {
return fmt.Sprintf("%x", xxhash.Sum64String(p))
}

func restoreAssetCaches(ctx context.Context, sources ExtensionAssetConfig, assetCfg AssetBuildConfig) error {
if !experimentalCachingEnabled {
if !assetCfg.EnableAssetCaching {
return nil
}

var errgrp errgroup.Group

for name, source := range sources {
if source.RequiresBuild() && !slices.Contains(assetCfg.ForceExtensionBuild, name) {
if (source.RequiresBuild() || len(source.AdditionalCaches) > 0) && !slices.Contains(assetCfg.ForceExtensionBuild, name) {
errgrp.Go(func() error {
return restoreAssetCache(ctx, source, assetCfg)
})
Expand All @@ -38,14 +37,14 @@ func restoreAssetCaches(ctx context.Context, sources ExtensionAssetConfig, asset
}

func storeAssetCaches(ctx context.Context, sources ExtensionAssetConfig, assetCfg AssetBuildConfig) error {
if !experimentalCachingEnabled {
if !assetCfg.EnableAssetCaching {
return nil
}

var errgrp errgroup.Group

for name, source := range sources {
if source.RequiresBuild() && !slices.Contains(assetCfg.ForceExtensionBuild, name) {
if (source.RequiresBuild() || len(source.AdditionalCaches) > 0) && !slices.Contains(assetCfg.ForceExtensionBuild, name) {
errgrp.Go(func() error {
return storeAssetCache(ctx, source, assetCfg)
})
Expand All @@ -68,32 +67,43 @@ func restoreAssetCache(ctx context.Context, source *ExtensionAssetConfigEntry, a

if source.Administration.EntryFilePath != nil {
if err := system.GetDefaultCache().RestoreFolderCache(ctx, cacheKey+"-administration", source.GetOutputAdminPath()); err != nil {
if errors.Is(err, system.ErrCacheNotFound) {
return nil
if !errors.Is(err, system.ErrCacheNotFound) {
return err
}
} else {
logging.FromContext(ctx).Infof("Restored administration assets for %s from cache", source.TechnicalName)

return err
source.Administration.EntryFilePath = nil
source.Administration.Webpack = nil
}

logging.FromContext(ctx).Infof("Restored administration assets for %s from cache", source.TechnicalName)

source.Administration.EntryFilePath = nil
source.Administration.Webpack = nil
}

if source.Storefront.EntryFilePath != nil {
if err := system.GetDefaultCache().RestoreFolderCache(ctx, cacheKey+"-storefront", source.GetOutputStorefrontPath()); err != nil {
if errors.Is(err, system.ErrCacheNotFound) {
return nil
if !errors.Is(err, system.ErrCacheNotFound) {
return err
}
} else {
logging.FromContext(ctx).Infof("Restored storefront assets for %s from cache", source.TechnicalName)

return err
source.Storefront.EntryFilePath = nil
source.Storefront.Webpack = nil
}
}

for _, cachePath := range source.AdditionalCaches {
outputPath := path.Join(source.BasePath, cachePath.Path)
suffix := hashCacheKeySuffix(cachePath.Path)

logging.FromContext(ctx).Infof("Restored storefront assets for %s from cache", source.TechnicalName)
if err := system.GetDefaultCache().RestoreFolderCache(ctx, cacheKey+"-"+suffix, outputPath); err != nil {
if !errors.Is(err, system.ErrCacheNotFound) {
return err
}

continue
}

source.Storefront.EntryFilePath = nil
source.Storefront.Webpack = nil
logging.FromContext(ctx).Infof("Restored additional cache path %s for %s from cache", cachePath.Path, source.TechnicalName)
}
Comment thread
shyim marked this conversation as resolved.

return nil
Expand Down Expand Up @@ -122,5 +132,14 @@ func storeAssetCache(ctx context.Context, source *ExtensionAssetConfigEntry, ass
}
}

for _, cachePath := range source.AdditionalCaches {
outputPath := path.Join(source.BasePath, cachePath.Path)
suffix := hashCacheKeySuffix(cachePath.Path)

if err := system.GetDefaultCache().StoreFolderCache(ctx, cacheKey+"-"+suffix, outputPath); err != nil {
return fmt.Errorf("additional_caches path %q for extension %s: %w", cachePath.Path, source.TechnicalName, err)
}
}

return nil
}
52 changes: 50 additions & 2 deletions internal/extension/asset_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const (
)

type AssetBuildConfig struct {
EnableAssetCaching bool
CleanupNodeModules bool
DisableAdminBuild bool
DisableStorefrontBuild bool
Expand Down Expand Up @@ -158,6 +159,7 @@ type ExtensionAssetConfigEntry struct {
EnableESBuildForStorefront bool
DisableSass bool
NpmStrict bool
AdditionalCaches []ConfigBuildZipAssetsAdditionalCache

// internal cache
cachedPossibleNodePaths []string
Expand Down Expand Up @@ -280,6 +282,21 @@ func (e *ExtensionAssetConfigEntry) GetContentHash() (string, error) {
}
}

// Include asset build config in hash so any config change invalidates the cache
if _, err := fmt.Fprintf(hasher, "%v%v%v%v", e.EnableESBuildForAdmin, e.EnableESBuildForStorefront, e.DisableSass, e.NpmStrict); err != nil {
return "", err
}
for _, cache := range e.AdditionalCaches {
if _, err := fmt.Fprintf(hasher, "%s", cache.Path); err != nil {
return "", err
}
for _, sp := range cache.SourcePaths {
if _, err := fmt.Fprintf(hasher, "%s", sp); err != nil {
return "", err
}
}
}

e.sumOfFiles = fmt.Sprintf("%x", hasher.Sum64())
return e.sumOfFiles, nil
}
Expand Down Expand Up @@ -319,6 +336,16 @@ func (e *ExtensionAssetConfigEntry) collectFilesForHashing() ([]string, error) {
}
}

// Collect files from additional cache source paths
for _, cachePath := range e.AdditionalCaches {
for _, sourcePath := range cachePath.SourcePaths {
absSourcePath := path.Join(e.BasePath, sourcePath)
if err := e.collectFilesFromDir(absSourcePath, &files); err != nil {
return nil, fmt.Errorf("failed to collect custom cache source files from %s: %w", sourcePath, err)
}
}
Comment thread
shyim marked this conversation as resolved.
}
Comment thread
shyim marked this conversation as resolved.
Comment thread
shyim marked this conversation as resolved.

// Add package.json files
files = append(files, e.getPossibleNodePaths()...)

Expand All @@ -336,8 +363,12 @@ func (e *ExtensionAssetConfigEntry) collectFilesFromDir(dir string, files *[]str
return err
}

// Skip directories and node_modules
if info.IsDir() || strings.Contains(path, "node_modules") {
// Skip node_modules directories entirely
if info.IsDir() && info.Name() == "node_modules" {
return filepath.SkipDir
}

if info.IsDir() {
return nil
}

Expand Down Expand Up @@ -436,6 +467,7 @@ func BuildAssetConfigFromExtensions(ctx context.Context, sources []asset.Source,
sourceConfig.EnableESBuildForStorefront = source.StorefrontEsbuildCompatible
sourceConfig.DisableSass = source.DisableSass
sourceConfig.NpmStrict = source.NpmStrict
sourceConfig.AdditionalCaches = convertSourceAdditionalCaches(source.AdditionalCaches)

if assetCfg.SkipExtensionsWithBuildFiles {
expectedAdminCompiledFile := path.Join(source.Path, "Resources", "public", "administration", "js", esbuild.ToKebabCase(source.Name)+".js")
Expand Down Expand Up @@ -542,3 +574,19 @@ func createConfigFromPath(entryPointName string, extensionRoot string) *Extensio
}
return &cfg
}

func convertSourceAdditionalCaches(paths []asset.AdditionalCache) []ConfigBuildZipAssetsAdditionalCache {
if len(paths) == 0 {
return nil
}

result := make([]ConfigBuildZipAssetsAdditionalCache, len(paths))
for i, cp := range paths {
result[i] = ConfigBuildZipAssetsAdditionalCache{
Path: cp.Path,
SourcePaths: cp.SourcePaths,
}
}

return result
}
Loading
Loading