pulumi/sdk/nodejs/npm/manager.go

114 lines
4.2 KiB
Go
Raw Permalink Normal View History

package npm
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/cmdutil"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/logging"
)
// A `PackageManager` is responsible for installing dependencies,
// packaging Pulumi programs, and executing Node in the context of
// installed packages. In practice, each implementation of this
// interface represents one of the package managers in the Node ecosystem
// e.g. Yarn, NPM, etc.
// The language host will dynamically dispatch to an instance of PackageManager
// in response to RPC requests.
type PackageManager interface {
2023-05-24 17:18:22 +00:00
// Install will install dependencies with the given package manager.
Install(ctx context.Context, dir string, production bool, stdout, stderr io.Writer) error
Pack(ctx context.Context, dir string, stderr io.Writer) ([]byte, error)
// Name is the name of the binary executable used to invoke this package manager.
// e.g. yarn or npm
Name() string
}
// Pack runs `npm pack` in the given directory, packaging the Node.js app located there into a
// tarball and returning it as `[]byte`. `stdout` is ignored for the command, as it does not
// generate useful data. If the `PULUMI_PREFER_YARN` environment variable is set, `yarn pack` is run
// instead of `npm pack`.
func Pack(ctx context.Context, dir string, stderr io.Writer) ([]byte, error) {
pkgManager, err := ResolvePackageManager(dir)
if err != nil {
return nil, err
}
return pkgManager.Pack(ctx, dir, stderr)
}
// Install runs `npm install` in the given directory, installing the dependencies for the Node.js
// app located there. If the `PULUMI_PREFER_YARN` environment variable is set, `yarn install` is used
// instead of `npm install`.
// The returned string is the name of the package manager used during installation.
func Install(ctx context.Context, dir string, production bool, stdout, stderr io.Writer) (string, error) {
pkgManager, err := ResolvePackageManager(dir)
if err != nil {
return "", err
}
name := pkgManager.Name()
err = pkgManager.Install(ctx, dir, production, stdout, stderr)
if err != nil {
return name, err
}
// Ensure the "node_modules" directory exists.
Use pnpm as package manager if we find a pnpm-lock.yaml file (#15456) <!--- Thanks so much for your contribution! If this is your first time contributing, please ensure that you have read the [CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md) documentation. --> # Description Initial support pnpm. Note that this does not support pnpm workspaces yet. This also does not handle passing the package manager through from `pulumi new`. Once a user manually runs pnpm, creating a pnpm-lock.yaml, we'll detect that and pnpm. Fixes https://github.com/pulumi/pulumi/issues/15455 ## Checklist - [x] I have run `make tidy` to update any new dependencies - [x] I have run `make lint` to verify my code passes the lint check - [x] I have formatted my code using `gofumpt` <!--- Please provide details if the checkbox below is to be left unchecked. --> - [x] I have added tests that prove my fix is effective or that my feature works <!--- User-facing changes require a CHANGELOG entry. --> - [x] I have run `make changelog` and committed the `changelog/pending/<file>` documenting my change <!-- If the change(s) in this PR is a modification of an existing call to the Pulumi Cloud, then the service should honor older versions of the CLI where this change would not exist. You must then bump the API version in /pkg/backend/httpstate/client/api.go, as well as add it to the service. --> - [ ] Yes, there are changes in this PR that warrants bumping the Pulumi Cloud API version <!-- @Pulumi employees: If yes, you must submit corresponding changes in the service repo. -->
2024-02-21 13:41:21 +00:00
// NB: This is only appropriate for certain package managers.
// Yarn with Plug'n'Play enabled won't produce a node_modules directory,
// either for Yarn Classic or Yarn Berry.
nodeModulesPath := filepath.Join(dir, "node_modules")
2023-05-24 17:18:22 +00:00
if _, err := os.Stat(nodeModulesPath); err != nil {
if os.IsNotExist(err) {
return name, fmt.Errorf("%s install reported success, but node_modules directory is missing", name)
}
// If the node_modules dir exists but we can't stat it, we might be able to proceed
// without issue, but it's bizarre enough that we should warn.
logging.Warningf("failed to read node_modules metadata: %v", err)
}
return name, nil
}
Use pnpm as package manager if we find a pnpm-lock.yaml file (#15456) <!--- Thanks so much for your contribution! If this is your first time contributing, please ensure that you have read the [CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md) documentation. --> # Description Initial support pnpm. Note that this does not support pnpm workspaces yet. This also does not handle passing the package manager through from `pulumi new`. Once a user manually runs pnpm, creating a pnpm-lock.yaml, we'll detect that and pnpm. Fixes https://github.com/pulumi/pulumi/issues/15455 ## Checklist - [x] I have run `make tidy` to update any new dependencies - [x] I have run `make lint` to verify my code passes the lint check - [x] I have formatted my code using `gofumpt` <!--- Please provide details if the checkbox below is to be left unchecked. --> - [x] I have added tests that prove my fix is effective or that my feature works <!--- User-facing changes require a CHANGELOG entry. --> - [x] I have run `make changelog` and committed the `changelog/pending/<file>` documenting my change <!-- If the change(s) in this PR is a modification of an existing call to the Pulumi Cloud, then the service should honor older versions of the CLI where this change would not exist. You must then bump the API version in /pkg/backend/httpstate/client/api.go, as well as add it to the service. --> - [ ] Yes, there are changes in this PR that warrants bumping the Pulumi Cloud API version <!-- @Pulumi employees: If yes, you must submit corresponding changes in the service repo. -->
2024-02-21 13:41:21 +00:00
// ResolvePackageManager determines which package manager to use. If the
// `PULUMI_PREFER_YARN` environment variable is set, or if a yarn.lock file
// exists, then YarnClassic is used. If a pnpm-lock.yaml file exists, then
// pnpm is used. Otherwise npm is used. The argument pwd is the directory
// we're checking for the presence of a lockfile.
func ResolvePackageManager(pwd string) (PackageManager, error) {
// Prefer yarn if PULUMI_PREFER_YARN is truthy, or if yarn.lock exists.
if preferYarn() || checkYarnLock(pwd) {
yarn, err := newYarnClassic()
// If we can't find the Yarn executable, then we should default to NPM.
if err == nil {
return yarn, nil
}
Use pnpm as package manager if we find a pnpm-lock.yaml file (#15456) <!--- Thanks so much for your contribution! If this is your first time contributing, please ensure that you have read the [CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md) documentation. --> # Description Initial support pnpm. Note that this does not support pnpm workspaces yet. This also does not handle passing the package manager through from `pulumi new`. Once a user manually runs pnpm, creating a pnpm-lock.yaml, we'll detect that and pnpm. Fixes https://github.com/pulumi/pulumi/issues/15455 ## Checklist - [x] I have run `make tidy` to update any new dependencies - [x] I have run `make lint` to verify my code passes the lint check - [x] I have formatted my code using `gofumpt` <!--- Please provide details if the checkbox below is to be left unchecked. --> - [x] I have added tests that prove my fix is effective or that my feature works <!--- User-facing changes require a CHANGELOG entry. --> - [x] I have run `make changelog` and committed the `changelog/pending/<file>` documenting my change <!-- If the change(s) in this PR is a modification of an existing call to the Pulumi Cloud, then the service should honor older versions of the CLI where this change would not exist. You must then bump the API version in /pkg/backend/httpstate/client/api.go, as well as add it to the service. --> - [ ] Yes, there are changes in this PR that warrants bumping the Pulumi Cloud API version <!-- @Pulumi employees: If yes, you must submit corresponding changes in the service repo. -->
2024-02-21 13:41:21 +00:00
logging.Warningf("could not find yarn on the $PATH, trying pnpm instead: %v", err)
}
Use pnpm as package manager if we find a pnpm-lock.yaml file (#15456) <!--- Thanks so much for your contribution! If this is your first time contributing, please ensure that you have read the [CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md) documentation. --> # Description Initial support pnpm. Note that this does not support pnpm workspaces yet. This also does not handle passing the package manager through from `pulumi new`. Once a user manually runs pnpm, creating a pnpm-lock.yaml, we'll detect that and pnpm. Fixes https://github.com/pulumi/pulumi/issues/15455 ## Checklist - [x] I have run `make tidy` to update any new dependencies - [x] I have run `make lint` to verify my code passes the lint check - [x] I have formatted my code using `gofumpt` <!--- Please provide details if the checkbox below is to be left unchecked. --> - [x] I have added tests that prove my fix is effective or that my feature works <!--- User-facing changes require a CHANGELOG entry. --> - [x] I have run `make changelog` and committed the `changelog/pending/<file>` documenting my change <!-- If the change(s) in this PR is a modification of an existing call to the Pulumi Cloud, then the service should honor older versions of the CLI where this change would not exist. You must then bump the API version in /pkg/backend/httpstate/client/api.go, as well as add it to the service. --> - [ ] Yes, there are changes in this PR that warrants bumping the Pulumi Cloud API version <!-- @Pulumi employees: If yes, you must submit corresponding changes in the service repo. -->
2024-02-21 13:41:21 +00:00
// Prefer pnpm if pnpm-lock.yaml exists.
if checkPnpmLock(pwd) {
pnpm, err := newPnpm()
if err == nil {
return pnpm, nil
}
logging.Warningf("could not find pnpm on the $PATH, trying npm instead: %v", err)
}
// Finally, fall back to npm.
node, err := newNPM()
if err != nil {
return nil, fmt.Errorf("could not find npm on the $PATH; npm is installed with Node.js "+
"available at https://nodejs.org/: %w", err)
}
return node, nil
}
// preferYarn returns true if the `PULUMI_PREFER_YARN` environment variable is set.
func preferYarn() bool {
return cmdutil.IsTruthy(os.Getenv("PULUMI_PREFER_YARN"))
2023-05-24 17:18:22 +00:00
}