Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Danielle Adams committed Dec 6, 2019
1 parent 021f897 commit ec1fef0
Show file tree
Hide file tree
Showing 9 changed files with 265 additions and 9 deletions.
Empty file added CHANGELOG.md
Empty file.
26 changes: 26 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.EXPORT_ALL_VARIABLES:

.PHONY: clean \
package \
release

SHELL=/bin/bash -o pipefail

VERSION := "v$$(cat buildpack.toml | grep version | sed -e 's/version = //g' | xargs)"

test:
make shellcheck

clean:
-rm -f nodejs-yarn-buildpack-$(VERSION).tgz

package: clean
@tar cvzf nodejs-yarn-buildpack-$(VERSION).tgz bin/ lib/** buildpack.toml README.md

release:
@git tag $(VERSION)
@git push --tags origin master

shellcheck:
@shellcheck -x bin/build bin/detect
@shellcheck -x lib/*.sh lib/utils/*.sh
99 changes: 99 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Node.js Yarn Cloud Native Buildpack

This buildpack builds on top of the existing [Node.js Engine Cloud Native Buildpack](https://github.com/heroku/nodejs-engine-buildpack). It runs subsequent scripts after Node is install.

- Run automatically
- `yarn install`
- Run when configured in `package.json`
- `yarn build` or `yarn heroku-postbuild`

## Usage

### Install pack

Using `brew` (assuming development is done on MacOS), install `pack`.

```sh
brew tap buildpack/tap
brew install pack
```

If you're using Windows or Linux, follow instructions [here](https://buildpacks.io/docs/install-pack/).

### Clone the buildpack

Right now, we are prototyping with a local version of the buildpack. Clone it to your machine.

```sh
git clone [email protected]:heroku/nodejs-yarn-buildpack.git
```

Clone the Heroku Node.js Engine Cloud Native Buildpack.

```sh
cd .. # change from nodejs-npm-buildpack directory
git clone [email protected]:heroku/nodejs-engine-buildpack.git
```

### Build the image

#### with buildpacks

Using pack, you're ready to create an image from the buildpack and source code. You will need to add flags that point to the path of the source code (`--path`) and the paths of the buildpacks (`--buildpack`).

```sh
cd nodejs-yarn-buildpack
pack build TEST_IMAGE_NAME --path ../TEST_REPO_PATH --buildpack ../nodejs-engine-buildpack --buildpack ../nodejs-yarn-buildpack
```

#### with a builder

You can also create a `builder.toml` file that will have explicit directions when creating a buildpack. This is useful when there are multiple "detect" paths a build can take (ie. yarn vs. npm commands).

In a directory outside of this buildpack, create a builder file:

```sh
cd ..
mkdir heroku_nodejs_builder
touch heroku_nodejs_builder/builder.toml
```

For local development, you'll want the file to look like this:

```toml
[[buildpacks]]
id = "heroku/nodejs-engine-buildpack"
uri = "../nodejs-engine-buildpack"

[[buildpacks]]
id = "heroku/nodejs-yarn-buildpack"
uri = "../nodejs-yarn-buildpack"

[[order]]
group = [
{ id = "heroku/nodejs-engine-buildpack", version = "0.0.1" },
{ id = "heroku/nodejs-yarn-buildpack", version = "0.0.1" }
]

[stack]
id = "heroku-18"
build-image = "heroku/pack:18"
run-image = "heroku/pack:18"
```

Create the builder with `pack`:

```sh
pack create-builder nodejs --builder-config ../heroku-nodejs-builder/builder.toml
```

Now you can use the builder image instead of chaining the buildpacks.

```sh
pack build TEST_IMAGE_NAME --path ../TEST_REPO_PATH --builder nodejs
```

## Glossary

- buildpacks: provide framework and a runtime for source code. Read more [here](https://buildpacks.io).
- OCI image: [OCI (Open Container Initiative)](https://www.opencontainers.org/) is a project to create open sourced standards for OS-level virtualization, most importantly in Linux containers.
19 changes: 14 additions & 5 deletions bin/build
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
#!/usr/bin/env bash
set -eu
set -o pipefail

rm -rf node_modules
npm -g install yarn
yarn install
bp_dir=$(cd "$(dirname "$0")"/..; pwd)
build_dir=$(pwd)
layers_dir=$1
platform_dir=$2

# if yarn build, then build
# if yarn heroku-build, then heroku-build
# shellcheck source=/dev/null
source "$bp_dir/lib/build.sh"
rm -rf "$build_dir/node_modules"

run_prebuild "$build_dir"
install_or_reuse_node_modules "$build_dir" "$layers_dir/node_modules"
run_build "$build_dir"
write_launch_toml "$build_dir/package.json" "$layers_dir/launch.toml"
10 changes: 9 additions & 1 deletion bin/detect
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
#!/usr/bin/env bash

if [[ ! -f yarn.lock ]]; then
set -eo pipefail

bp_dir=$(cd "$(dirname "$0")"/..; pwd)
build_dir=$(pwd)

# shellcheck source=/dev/null
source "$bp_dir/lib/detect.sh"

if ! detect_yarn_lock "$build_dir" ; then
exit 100
fi
6 changes: 3 additions & 3 deletions buildpack.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Buildpack ID and metadata
api = "0.2"

[buildpack]
id = "heroku/nodejs-yarn-buildpack"
id = "heroku/nodejs-yarn"
version = "0.0.1"
name = "Yarn Buildpack"

# Stacks that the buildpack will work with
[[stacks]]
id = "heroku-18"
96 changes: 96 additions & 0 deletions lib/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/usr/bin/env bash

set -e

# shellcheck disable=SC2128
bp_dir=$(cd "$(dirname "$BASH_SOURCE")"; cd ..; pwd)

# shellcheck source=/dev/null
source "$bp_dir/lib/utils/json.sh"
# shellcheck source=/dev/null
source "$bp_dir/lib/detect.sh"

run_prebuild() {
local build_dir=$1
local heroku_prebuild_script

heroku_prebuild_script=$(json_get_key "$build_dir/package.json" ".scripts[\"heroku-prebuild\"]")

if [[ $heroku_prebuild_script ]] ; then
yarn heroku-prebuild
fi
}

install_modules() {
local build_dir=$1
local layer_dir=$2

if detect_yarn_lock "$build_dir" ; then
echo "---> Installing node modules from ./yarn.lock"
yarn install
else
echo "---> Installing node modules"
yarn install --no-lockfile
fi
}

install_or_reuse_node_modules() {
local build_dir=$1
local layer_dir=$2
local local_lock_checksum
local cached_lock_checksum

touch "$layer_dir.toml"
mkdir -p "${layer_dir}"

local_lock_checksum=$(sha256sum "$build_dir/yarn.lock" | cut -d " " -f 1)
cached_lock_checksum=$(yj -t < "${layer_dir}.toml" | jq -r ".metadata.yarn_lock_checksum")

if [[ "$local_lock_checksum" == "$cached_lock_checksum" ]] ; then
echo "---> Reusing node modules"
cp -r "$layer_dir" "$build_dir/node_modules"
else
echo "cache = true" > "${layer_dir}.toml"

{
echo "build = false"
echo "launch = false"
echo -e "[metadata]\nyarn_lock_checksum = \"$local_lock_checksum\""
} >> "${layer_dir}.toml"

install_modules "$build_dir" "$layer_dir"

if [[ -d "$build_dir/node_modules" && -n "$(ls -A "$build_dir/node_modules")" ]] ; then
cp -r "$build_dir/node_modules/." "$layer_dir"
fi
fi
}

run_build() {
local build_dir=$1
local build_script
local heroku_postbuild_script

build_script=$(json_get_key "$build_dir/package.json" ".scripts.build")
heroku_postbuild_script=$(json_get_key "$build_dir/package.json" ".scripts[\"heroku-postbuild\"]")

if [[ $heroku_postbuild_script ]] ; then
yarn heroku-postbuild
elif [[ $build_script ]] ; then
yarn build
fi
}

write_launch_toml() {
local package_json=$1
local launch_toml=$2

if [ "null" != "$(jq -r .scripts.start < "$package_json")" ]; then
cat <<TOML > "$launch_toml"
[[processes]]
type = "web"
command = "yarn start"
TOML
fi

}
6 changes: 6 additions & 0 deletions lib/detect.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env bash

detect_yarn_lock() {
local build_dir=$1
[[ -f "$build_dir/yarn.lock" ]]
}
12 changes: 12 additions & 0 deletions lib/utils/json.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env bash

json_get_key() {
local file="$1"
local key="$2"

if test -f "$file"; then
jq -c -M --raw-output "$key // \"\"" < "$file" || return 1
else
echo ""
fi
}

0 comments on commit ec1fef0

Please sign in to comment.