mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
* define contract for package executors * introduce npx executor * add npx and pnpx cmd support * fix typo * rm PackageExecutor and depend on PackageManager interface * add support for PTY to handle parent-child process interaction * refactor PTY handling in proxy flow * enforce interactiveSession interface check * close reader explicitly and clean npm version for pkg executors * rm interaction from interceptors * add docs and wait for outputRouter before exit * add support for non interactive TTY for proxy mode * add support for CI env var check for non interactive tty proxy mode * update readme to include npx, pnpx support * Update internal/flows/proxy_flow.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Sahil Bansal <bansalsahil315@gmail.com> * update ptyx lib * fix docs typo --------- Signed-off-by: Sahil Bansal <bansalsahil315@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
124 lines
2.8 KiB
Go
124 lines
2.8 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)
|
|
}
|
|
}
|
|
|
|
// Unlike npx, pnpx does not separate package and binary;
|
|
// the first arg is always an install target.
|
|
if n.Config.CommandName == "pnpx" && len(flagSet.Args()) > 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
|
|
}
|