Skip to content

Commit

Permalink
Initial (stub) implementation of heroku/nodejs-pnpm-engine (#751)
Browse files Browse the repository at this point in the history
* Add stubbed implementation for heroku/nodejs-pnpm-engine

* Update error message, add test fixture

* Update heroku/nodejs-pnpm-engine stub implementation

* Update pnpm-engine registry badge

* Add a changelog for heroku/nodejs-pnpm-engine

* Add a changelog entry for heroku/nodejs-pnpm-engine

* Update all buildpacks to 2.5.0 after rebase

* Update buildpacks/nodejs-pnpm-engine/README.md

Co-authored-by: Colin Casey <[email protected]>

* Update buildpacks/nodejs-pnpm-engine/buildpack.toml

Co-authored-by: Colin Casey <[email protected]>

---------

Co-authored-by: Colin Casey <[email protected]>
  • Loading branch information
joshwlewis and colincasey authored Dec 14, 2023
1 parent aa9768e commit 600c723
Show file tree
Hide file tree
Showing 17 changed files with 737 additions and 1 deletion.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ members = [
"buildpacks/nodejs-function-invoker",
"buildpacks/nodejs-npm-engine",
"buildpacks/nodejs-npm-install",
"buildpacks/nodejs-pnpm-engine",
"buildpacks/nodejs-pnpm-install",
"buildpacks/nodejs-yarn",
"common/nodejs-utils",
Expand Down
14 changes: 14 additions & 0 deletions buildpacks/nodejs-pnpm-engine/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Initial release

[unreleased]: https://github.com/heroku/buildpacks-nodejs/compare/v2.5.0...HEAD
19 changes: 19 additions & 0 deletions buildpacks/nodejs-pnpm-engine/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "heroku-pnpm-engine-buildpack"
description = "Heroku Node.js pnpm Engine Cloud Native Buildpack"
version.workspace = true
rust-version.workspace = true
edition.workspace = true
publish.workspace = true

[lints]
workspace = true

[dependencies]
commons = { git = "https://github.com/heroku/buildpacks-ruby", branch = "main" }
indoc = "2"
libcnb = { version = "=0.17.0", features = ["trace"] }

[dev-dependencies]
libcnb-test = "=0.17.0"
test_support.workspace = true
47 changes: 47 additions & 0 deletions buildpacks/nodejs-pnpm-engine/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Heroku Cloud Native pnpm Engine Buildpack

[![CI][CI BADGE]][CI LINK] [![Registry][Registry BADGE]][Registry LINK]

Heroku's official Cloud Native Buildpack for installing a version of `pnpm`.

> [!IMPORTANT]
> This buildpack is a stub implementation, and does not yet install `pnpm`.
> To install `pnpm` during a build, use [heroku/nodejs-corepack] instead.
## How it works

The buildpack will pass detection if:

- A `pnpm-lock.yaml` file is found at the root of the application source.

The buildpack will not install `pnpm`, yet. This buildpack will recommend using
`corepack` to install `pnpm` via the [heroku/nodejs-corepack](heroku/nodejs-corepack)
buildpack.

## Build Plan

### Requires

| Name | Description |
|----------------|-------------------------------------------------------------------------------------------------------------------------------------------------|
| `node` | To install `pnpm` a [Node.js][Node.js] runtime is required. It can be provided by the [`heroku/nodejs-engine`][heroku/nodejs-engine] buildpack. |

### Provides

| Name | Description |
|--------|----------------------------------------------------------------------------------------|
| `pnpm` | Allows other buildpacks that require [pnpm][pnpm] tooling to depend on this buildpack. |

## License

See [LICENSE](../../LICENSE) file.

[CI BADGE]: https://github.com/heroku/buildpacks-nodejs/actions/workflows/ci.yml/badge.svg
[CI LINK]: https://github.com/heroku/buildpacks-nodejs/actions/workflows/ci.yml
[Registry BADGE]: https://img.shields.io/badge/dynamic/json?url=https://registry.buildpacks.io/api/v1/buildpacks/heroku/nodejs-pnpm-engine&label=version&query=$.latest.version&color=DF0A6B&logo=data:image/png;base64,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&labelColor=white
[Registry LINK]: https://registry.buildpacks.io/buildpacks/heroku/nodejs-pnpm-engine
[Node.js]: https://nodejs.org/
[pnpm]: https://pnpm.io/
[heroku/nodejs-engine]: ../nodejs-engine/README.md
[heroku/nodejs-corepack]: ../nodejs-corepack/README.md

18 changes: 18 additions & 0 deletions buildpacks/nodejs-pnpm-engine/buildpack.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
api = "0.9"

[buildpack]
id = "heroku/nodejs-pnpm-engine"
version = "2.5.0"
name = "Heroku Node.js pnpm Engine"
homepage = "https://github.com/heroku/buildpacks-nodejs"
description = "Heroku's Node.js pnpm Engine buildpack. A component of the 'heroku/nodejs' buildpack."
keywords = ["pnpm", "heroku"]

[[buildpack.licenses]]
type = "MIT"

[[stacks]]
id = "*"

[metadata.release]
image = { repository = "docker.io/heroku/buildpack-nodejs-pnpm-engine" }
85 changes: 85 additions & 0 deletions buildpacks/nodejs-pnpm-engine/src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use crate::BUILDPACK_NAME;
use commons::output::build_log::{BuildLog, Logger, StartedLogger};
use commons::output::fmt;
use commons::output::fmt::DEBUG_INFO;
use indoc::formatdoc;
use std::fmt::Display;
use std::io::stdout;

#[derive(Debug, Copy, Clone)]
pub(crate) enum PnpmEngineBuildpackError {
CorepackRequired,
}

pub(crate) fn on_error(error: libcnb::Error<PnpmEngineBuildpackError>) {
let logger = BuildLog::new(stdout()).without_buildpack_name();
match error {
libcnb::Error::BuildpackError(buildpack_error) => {
on_buildpack_error(buildpack_error, logger);
}
framework_error => on_framework_error(&framework_error, logger),
}
}

fn on_buildpack_error(error: PnpmEngineBuildpackError, logger: Box<dyn StartedLogger>) {
match error {
PnpmEngineBuildpackError::CorepackRequired => {
print_error_details(logger, &"Corepack Requirement Error")
.announce()
.error(&formatdoc! {"
A pnpm lockfile ({pnpm_lockfile}) was detected, but the
version of {pnpm} to install could not be determined.
{pnpm} may be installed via the {heroku_nodejs_corepack}
buildpack. It requires the desired {pnpm} version to be set
via the {package_manager} key in {package_json}.
To set {package_manager} in {package_json} to the latest
{pnpm}, run:
{corepack_enable}
{corepack_use_pnpm}
Then commit the result, and try again.
",
corepack_enable = fmt::command("corepack enable"),
corepack_use_pnpm = fmt::command("corepack use pnpm@*"),
heroku_nodejs_corepack = fmt::command("heroku/nodejs-corepack"),
package_manager = fmt::value("packageManager"),
pnpm = fmt::value("pnpm"),
pnpm_lockfile = fmt::value("pnpm-lock.yaml"),
package_json = fmt::value("package.json")});
}
}
}

fn on_framework_error(
error: &libcnb::Error<PnpmEngineBuildpackError>,
logger: Box<dyn StartedLogger>,
) {
print_error_details(logger, &error)
.announce()
.error(&formatdoc! {"
{buildpack_name} internal error.
The framework used by this buildpack encountered an unexpected error.
If you can't deploy to Heroku due to this issue, check the official Heroku Status page at \
status.heroku.com for any ongoing incidents. After all incidents resolve, retry your build.
If the issue persists and you think you found a bug in the buildpack or framework, reproduce \
the issue locally with a minimal example. Open an issue in the buildpack's GitHub repository \
and include the details.
", buildpack_name = fmt::value(BUILDPACK_NAME) });
}

fn print_error_details(
logger: Box<dyn StartedLogger>,
error: &impl Display,
) -> Box<dyn StartedLogger> {
logger
.section(DEBUG_INFO)
.step(&error.to_string())
.end_section()
}
55 changes: 55 additions & 0 deletions buildpacks/nodejs-pnpm-engine/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
mod errors;

use crate::errors::PnpmEngineBuildpackError;
use libcnb::build::{BuildContext, BuildResult};
use libcnb::data::build_plan::BuildPlanBuilder;
use libcnb::detect::{DetectContext, DetectResult, DetectResultBuilder};
use libcnb::generic::{GenericMetadata, GenericPlatform};
use libcnb::{buildpack_main, Buildpack};
#[cfg(test)]
use libcnb_test as _;
#[cfg(test)]
use test_support as _;

const BUILDPACK_NAME: &str = "Heroku Node.js pnpm Engine Buildpack";

struct PnpmEngineBuildpack;

impl Buildpack for PnpmEngineBuildpack {
type Platform = GenericPlatform;
type Metadata = GenericMetadata;
type Error = PnpmEngineBuildpackError;

fn detect(&self, context: DetectContext<Self>) -> libcnb::Result<DetectResult, Self::Error> {
// pass detect if a `pnpm-lock.yaml` is found
if context.app_dir.join("pnpm-lock.yaml").exists() {
return DetectResultBuilder::pass()
.build_plan(
BuildPlanBuilder::new()
.provides("pnpm")
.requires("node")
.build(),
)
.build();
}
DetectResultBuilder::fail().build()
}

fn build(&self, _context: BuildContext<Self>) -> libcnb::Result<BuildResult, Self::Error> {
// This buildpack does not install pnpm yet, suggest using
// `heroku/nodejs-corepack` instead.
Err(PnpmEngineBuildpackError::CorepackRequired)?
}

fn on_error(&self, error: libcnb::Error<Self::Error>) {
errors::on_error(error);
}
}

impl From<PnpmEngineBuildpackError> for libcnb::Error<PnpmEngineBuildpackError> {
fn from(value: PnpmEngineBuildpackError) -> Self {
libcnb::Error::BuildpackError(value)
}
}

buildpack_main!(PnpmEngineBuildpack);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "pnpm-unknown-version",
"version": "1.0.0",
"dependencies": {
"express": "^4.18.2"
}
}
Loading

0 comments on commit 600c723

Please sign in to comment.