mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
* add e2e and alias for npx, pnpx * Update .github/workflows/pmg-e2e.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Sahil Bansal <bansalsahil315@gmail.com> --------- Signed-off-by: Sahil Bansal <bansalsahil315@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
126 lines
3.0 KiB
Go
126 lines
3.0 KiB
Go
package packagemanager
|
|
|
|
import (
|
|
"io"
|
|
"slices"
|
|
"strings"
|
|
|
|
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
|
|
"github.com/spf13/pflag"
|
|
)
|
|
|
|
type NpmPackageExecutorConfig struct {
|
|
CommandName string
|
|
}
|
|
|
|
func DefaultNpxPackageExecutorConfig() NpmPackageExecutorConfig {
|
|
return NpmPackageExecutorConfig{
|
|
CommandName: "npx",
|
|
}
|
|
}
|
|
|
|
func DefaultPnpxPackageExecutorConfig() NpmPackageExecutorConfig {
|
|
return NpmPackageExecutorConfig{
|
|
CommandName: "pnpx",
|
|
}
|
|
}
|
|
|
|
type npmPackageExecutor struct {
|
|
Config NpmPackageExecutorConfig
|
|
}
|
|
|
|
func NewNpmPackageExecutor(config NpmPackageExecutorConfig) (*npmPackageExecutor, error) {
|
|
return &npmPackageExecutor{
|
|
Config: config,
|
|
}, nil
|
|
}
|
|
|
|
var _ PackageManager = &npmPackageExecutor{}
|
|
|
|
func (n *npmPackageExecutor) Name() string {
|
|
return n.Config.CommandName
|
|
}
|
|
|
|
func (n *npmPackageExecutor) Ecosystem() packagev1.Ecosystem {
|
|
return packagev1.Ecosystem_ECOSYSTEM_NPM
|
|
}
|
|
|
|
func (n *npmPackageExecutor) ParseCommand(args []string) (*ParsedCommand, error) {
|
|
if len(args) > 0 && (args[0] == "npx" || args[0] == "pnpx") {
|
|
args = args[1:]
|
|
}
|
|
|
|
command := Command{Exe: n.Config.CommandName, Args: args}
|
|
|
|
if len(args) < 1 {
|
|
return &ParsedCommand{
|
|
Command: command,
|
|
}, nil
|
|
}
|
|
|
|
flagSet := pflag.NewFlagSet(n.Config.CommandName, pflag.ContinueOnError)
|
|
flagSet.SetOutput(io.Discard)
|
|
flagSet.ParseErrorsAllowlist.UnknownFlags = true
|
|
|
|
var packages []string
|
|
switch n.Config.CommandName {
|
|
case "npx":
|
|
flagSet.StringArrayVarP(&packages, "package", "p", []string{}, "Package List")
|
|
case "pnpx":
|
|
flagSet.StringArrayVar(&packages, "package", []string{}, "Package List")
|
|
}
|
|
|
|
err := flagSet.Parse(args)
|
|
if err != nil {
|
|
return &ParsedCommand{Command: command}, nil
|
|
}
|
|
|
|
for _, arg := range flagSet.Args() {
|
|
// Append the scoped package
|
|
if strings.HasPrefix(arg, "@") && !slices.Contains(packages, arg) {
|
|
packages = append(packages, arg)
|
|
}
|
|
}
|
|
|
|
// For both npx and pnpx, the first positional argument is typically
|
|
// the package to execute (e.g., `npx cowsay@1.6.0` or `pnpx cowsay@1.6.0`).
|
|
// However, if -p/--package flags are provided, the first positional arg
|
|
// is the binary to run, not the package (e.g., `npx -p typescript tsc`).
|
|
if len(flagSet.Args()) > 0 && len(packages) == 0 {
|
|
pkg := flagSet.Args()[0]
|
|
if !slices.Contains(packages, pkg) {
|
|
packages = append(packages, pkg)
|
|
}
|
|
}
|
|
|
|
var installTargets []*PackageInstallTarget
|
|
|
|
for _, pkg := range packages {
|
|
packageName, version, err := npmParsePackageInfo(pkg)
|
|
if err != nil {
|
|
return nil, ErrFailedToParsePackage.Wrap(err)
|
|
}
|
|
|
|
if version != "" {
|
|
version = npmCleanVersion(version)
|
|
}
|
|
|
|
installTarget := &PackageInstallTarget{
|
|
PackageVersion: &packagev1.PackageVersion{
|
|
Package: &packagev1.Package{
|
|
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
|
Name: packageName,
|
|
},
|
|
Version: version,
|
|
},
|
|
}
|
|
|
|
installTargets = append(installTargets, installTarget)
|
|
}
|
|
|
|
return &ParsedCommand{
|
|
Command: command,
|
|
InstallTargets: installTargets,
|
|
}, nil
|
|
}
|