github.com/goravel/framework@v1.13.9/foundation/console/vendor_publish_command.go (about)

     1  package console
     2  
     3  import (
     4  	"errors"
     5  	"go/build"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  
    10  	"github.com/gookit/color"
    11  
    12  	"github.com/goravel/framework/contracts/console"
    13  	"github.com/goravel/framework/contracts/console/command"
    14  	"github.com/goravel/framework/support/file"
    15  )
    16  
    17  type VendorPublishCommand struct {
    18  	publishes     map[string]map[string]string
    19  	publishGroups map[string]map[string]string
    20  }
    21  
    22  func NewVendorPublishCommand(publishes, publishGroups map[string]map[string]string) *VendorPublishCommand {
    23  	return &VendorPublishCommand{
    24  		publishes:     publishes,
    25  		publishGroups: publishGroups,
    26  	}
    27  }
    28  
    29  // Signature The name and signature of the console command.
    30  func (receiver *VendorPublishCommand) Signature() string {
    31  	return "vendor:publish"
    32  }
    33  
    34  // Description The console command description.
    35  func (receiver *VendorPublishCommand) Description() string {
    36  	return "Publish any publishable assets from vendor packages"
    37  }
    38  
    39  // Extend The console command extend.
    40  func (receiver *VendorPublishCommand) Extend() command.Extend {
    41  	return command.Extend{
    42  		Category: "vendor",
    43  		Flags: []command.Flag{
    44  			&command.BoolFlag{
    45  				Name:    "existing",
    46  				Aliases: []string{"e"},
    47  				Usage:   "Publish and overwrite only the files that have already been published",
    48  			},
    49  			&command.BoolFlag{
    50  				Name:    "force",
    51  				Aliases: []string{"f"},
    52  				Usage:   "Overwrite any existing files",
    53  			},
    54  			&command.StringFlag{
    55  				Name:    "package",
    56  				Aliases: []string{"p"},
    57  				Usage:   "Package name to publish",
    58  			},
    59  			&command.StringFlag{
    60  				Name:    "tag",
    61  				Aliases: []string{"t"},
    62  				Usage:   "One tag that have assets you want to publish",
    63  			},
    64  		},
    65  	}
    66  }
    67  
    68  // Handle Execute the console command.
    69  func (receiver *VendorPublishCommand) Handle(ctx console.Context) error {
    70  	packageName := ctx.Option("package")
    71  	paths := receiver.pathsForPackageOrGroup(packageName, ctx.Option("tag"))
    72  	if len(paths) == 0 {
    73  		return errors.New("no vendor found")
    74  	}
    75  
    76  	packageDir, err := receiver.packageDir(packageName)
    77  	if err != nil {
    78  		return err
    79  	}
    80  
    81  	for sourcePath, targetValue := range paths {
    82  		targetValue = strings.TrimPrefix(strings.TrimPrefix(targetValue, "/"), "./")
    83  		packagePath := filepath.Join(packageDir, sourcePath)
    84  
    85  		res, err := receiver.publish(packagePath, targetValue, ctx.OptionBool("existing"), ctx.OptionBool("force"))
    86  		if err != nil {
    87  			return err
    88  		}
    89  
    90  		if len(res) > 0 {
    91  			for sourceFile, targetFile := range res {
    92  				color.Greenp("Copied Directory ")
    93  				color.Yellowf("[%s]", sourceFile)
    94  				color.Greenp(" To ")
    95  				color.Yellowf("%s\n", targetFile)
    96  			}
    97  		}
    98  	}
    99  
   100  	color.Greenln("Publishing complete")
   101  
   102  	return nil
   103  }
   104  
   105  func (receiver *VendorPublishCommand) pathsForPackageOrGroup(packageName, group string) map[string]string {
   106  	if packageName != "" && group != "" {
   107  		return receiver.pathsForProviderAndGroup(packageName, group)
   108  	} else if group != "" {
   109  		if paths, exist := receiver.publishGroups[group]; exist {
   110  			return paths
   111  		}
   112  	} else if packageName != "" {
   113  		if paths, exist := receiver.publishes[packageName]; exist {
   114  			return paths
   115  		}
   116  	}
   117  
   118  	return nil
   119  }
   120  
   121  func (receiver *VendorPublishCommand) pathsForProviderAndGroup(packageName, group string) map[string]string {
   122  	packagePaths, exist := receiver.publishes[packageName]
   123  	if !exist {
   124  		return nil
   125  	}
   126  
   127  	groupPaths, exist := receiver.publishGroups[group]
   128  	if !exist {
   129  		return nil
   130  	}
   131  
   132  	paths := make(map[string]string)
   133  	for key, path := range packagePaths {
   134  		if _, exist := groupPaths[key]; exist {
   135  			paths[key] = path
   136  		}
   137  	}
   138  
   139  	return paths
   140  }
   141  
   142  func (receiver *VendorPublishCommand) packageDir(packageName string) (string, error) {
   143  	var srcDir string
   144  	if build.IsLocalImport(packageName) {
   145  		srcDir = "./"
   146  	}
   147  
   148  	pkg, err := build.Import(packageName, srcDir, build.FindOnly)
   149  	if err != nil {
   150  		return "", err
   151  	}
   152  
   153  	return pkg.Dir, nil
   154  }
   155  
   156  func (receiver *VendorPublishCommand) publish(sourcePath, targetPath string, existing, force bool) (map[string]string, error) {
   157  	result := make(map[string]string)
   158  	isTargetPathDir := filepath.Ext(targetPath) == ""
   159  	isSourcePathDir := filepath.Ext(sourcePath) == ""
   160  
   161  	sourceFiles, err := receiver.getSourceFiles(sourcePath)
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  
   166  	for _, sourceFile := range sourceFiles {
   167  		relativePath := ""
   168  		if isSourcePathDir {
   169  			relativePath, err = filepath.Rel(sourcePath, sourceFile)
   170  			if err != nil {
   171  				return nil, err
   172  			}
   173  		} else {
   174  			relativePath = filepath.Base(sourcePath)
   175  		}
   176  
   177  		targetFile := targetPath
   178  		if isTargetPathDir {
   179  			targetFile = filepath.Join(targetPath, relativePath)
   180  		}
   181  
   182  		success, err := receiver.publishFile(sourceFile, targetFile, existing, force)
   183  		if err != nil {
   184  			return nil, err
   185  		}
   186  		if success {
   187  			result[sourceFile] = targetFile
   188  		}
   189  	}
   190  
   191  	return result, nil
   192  }
   193  
   194  func (receiver *VendorPublishCommand) getSourceFiles(sourcePath string) ([]string, error) {
   195  	sourcePathStat, err := os.Stat(sourcePath)
   196  	if err != nil {
   197  		return nil, err
   198  	}
   199  
   200  	if sourcePathStat.IsDir() {
   201  		return receiver.getSourceFilesForDir(sourcePath)
   202  	} else {
   203  		return []string{sourcePath}, nil
   204  	}
   205  }
   206  
   207  func (receiver *VendorPublishCommand) getSourceFilesForDir(sourcePath string) ([]string, error) {
   208  	dirEntries, err := os.ReadDir(sourcePath)
   209  	if err != nil {
   210  		return nil, err
   211  	}
   212  
   213  	var sourceFiles []string
   214  	for _, dirEntry := range dirEntries {
   215  		if dirEntry.IsDir() {
   216  			sourcePaths, err := receiver.getSourceFilesForDir(filepath.Join(sourcePath, dirEntry.Name()))
   217  			if err != nil {
   218  				return nil, err
   219  			}
   220  			sourceFiles = append(sourceFiles, sourcePaths...)
   221  		} else {
   222  			sourceFiles = append(sourceFiles, filepath.Join(sourcePath, dirEntry.Name()))
   223  		}
   224  	}
   225  
   226  	return sourceFiles, nil
   227  }
   228  
   229  func (receiver *VendorPublishCommand) publishFile(sourceFile, targetFile string, existing, force bool) (bool, error) {
   230  	content, err := os.ReadFile(sourceFile)
   231  	if err != nil {
   232  		return false, err
   233  	}
   234  
   235  	if !file.Exists(targetFile) && existing {
   236  		return false, nil
   237  	}
   238  	if file.Exists(targetFile) && !force && !existing {
   239  		return false, nil
   240  	}
   241  
   242  	if err := file.Create(targetFile, string(content)); err != nil {
   243  		return false, err
   244  	}
   245  
   246  	return true, nil
   247  }