From d88cf9bd5990a327b7f36b19b028eb5df34b7252 Mon Sep 17 00:00:00 2001 From: AidanDelaney Date: Thu, 30 Nov 2023 17:24:09 +0000 Subject: [PATCH] Merge pull request #620 from buildpacks/nodejs-port Port Buildpack author guide to NodeJS --- .../adding-bill-of-materials/index.html | 161 ++++------- .../create-buildpack/build-app/index.html | 92 +++--- .../building-blocks-cnb/index.html | 26 +- .../create-buildpack/caching/index.html | 272 ++++++------------ .../create-buildpack/detection/index.html | 30 +- .../create-buildpack/index.xml | 36 ++- .../make-app-runnable/index.html | 53 ++-- .../make-buildpack-configurable/index.html | 120 ++++---- .../setup-local-environment/index.html | 53 ++-- .../specify-multiple-process-types/index.html | 84 +++--- index.xml | 36 ++- 11 files changed, 368 insertions(+), 595 deletions(-) diff --git a/docs/buildpack-author-guide/create-buildpack/adding-bill-of-materials/index.html b/docs/buildpack-author-guide/create-buildpack/adding-bill-of-materials/index.html index fb445a0dc..e0f06bb02 100644 --- a/docs/buildpack-author-guide/create-buildpack/adding-bill-of-materials/index.html +++ b/docs/buildpack-author-guide/create-buildpack/adding-bill-of-materials/index.html @@ -750,33 +750,29 @@

Adding Bill-of-Materials

  • And more…!
  • You can find some of this information using pack via its inspect-image command. The bill-of-materials information will be available using pack sbom download.

    - -
    pack inspect-image test-ruby-app
    -
    -

    You should see the following:

    - +
    pack inspect-image test-node-js-app
    +

    You should see the following:

    Run Images:
       cnbs/sample-base-run:jammy
     ...
     
     Buildpacks:
       ID                   VERSION        HOMEPAGE
    -  examples/ruby        0.0.1          -
    +  examples/node-js        0.0.1          -
     
     Processes:
       TYPE                 SHELL        COMMAND                           ARGS        WORK DIR
    -  web (default)        bash         bundle exec ruby app.rb                       /workspace
    -  worker               bash         bundle exec ruby worker.rb                    /workspace
    -

    Apart from the above standard metadata, buildpacks can also populate information about the dependencies they have provided in form of a Bill-of-Materials. Let’s see how we can use this to populate information about the version of ruby that was installed in the output app image.

    -

    To add the ruby version to the output of pack download sbom, we will have to provide a Software Bill-of-Materials (SBOM) containing this information. There are three “standard” ways to report SBOM data. You’ll need to choose to use one of CycloneDX, SPDX or Syft update the ruby.sbom.<ext> (where <ext> is the extension appropriate for your SBOM standard, one of cdx.json, spdx.json or syft.json) at the end of your build script. Discussion of which SBOM format to choose is outside the scope of this tutorial, but we will note that the SBOM format you choose to use is likely to be the output format of any SBOM scanner (eg: syft cli) you might choose to use. In this example we will use the CycloneDX json format.

    + web (default) bash node-js app.js /workspace +

    Apart from the above standard metadata, buildpacks can also populate information about the dependencies they have provided in form of a Bill-of-Materials. Let’s see how we can use this to populate information about the version of node-js that was installed in the output app image.

    +

    To add the node-js version to the output of pack download sbom, we will have to provide a Software Bill-of-Materials (SBOM) containing this information. There are three “standard” ways to report SBOM data. You’ll need to choose to use one of CycloneDX, SPDX or Syft update the node-js.sbom.<ext> (where <ext> is the extension appropriate for your SBOM standard, one of cdx.json, spdx.json or syft.json) at the end of your build script. Discussion of which SBOM format to choose is outside the scope of this tutorial, but we will note that the SBOM format you choose to use is likely to be the output format of any SBOM scanner (eg: syft cli) you might choose to use. In this example we will use the CycloneDX json format.

    First, annotate the buildpack.toml to specify that it emits CycloneDX:

    - +
    # Buildpack API version
     api = "0.8"
     
     # Buildpack ID and metadata
     [buildpack]
    -  id = "examples/ruby"
    +  id = "examples/node-js"
       version = "0.0.1"
       sbom-formats = [ "application/vnd.cyclonedx+json" ]
     
    @@ -790,8 +786,8 @@ 

    Adding Bill-of-Materials

    Then, in our buildpack implementation we will generate the necessary SBOM metadata:

    # ...
     
    -# Append a Bill-of-Materials containing metadata about the provided ruby version
    -cat >> "$layersdir/ruby.sbom.cdx.json" << EOL
    +# Append a Bill-of-Materials containing metadata about the provided node-js version
    +cat >> "${layersdir}/node-js.sbom.cdx.json" << EOL
     {
       "bomFormat": "CycloneDX",
       "specVersion": "1.4",
    @@ -799,15 +795,15 @@ 

    Adding Bill-of-Materials

    "components": [ { "type": "library", - "name": "ruby", - "version": "$ruby_version" + "name": "node-js", + "version": "${node_js_version}" } ] } EOL -

    We can also add an SBOM entry for each dependency listed in Gemfile.lock. Here we use jq to add a new record to the components array in bundler.sbom.cdx.json:

    -
    crubybom="${layersdir}/ruby.sbom.cdx.json"
    -cat >> ${rubybom} << EOL
    +

    We can also add an SBOM entry for each dependency listed in package.json. Here we use jq to add a new record to the components array in bundler.sbom.cdx.json:

    +
    node-jsbom="${layersdir}/node-js.sbom.cdx.json"
    +cat >> ${node-jsbom} << EOL
     {
       "bomFormat": "CycloneDX",
       "specVersion": "1.4",
    @@ -815,94 +811,61 @@ 

    Adding Bill-of-Materials

    "components": [ { "type": "library", - "name": "ruby", - "version": "$ruby_version" + "name": "node-js", + "version": "${node_js_version}" } ] } EOL -if [[ -f Gemfile.lock ]] ; then - for gem in $(gem dep -q | grep ^Gem | sed 's/^Gem //') - do - version=${gem##*-} - name=${gem%-${version}} - DEP=$(jq --arg name "${name}" --arg version "${version}" \ - '.components[.components| length] |= . + {"type": "library", "name": $name, "version": $version}' \ - "${rubybom}") - echo ${DEP} > "${rubybom}" - done -fi -

    Your ruby-buildpack/bin/build script should look like the following:

    - +

    Your node-js-buildpack/bin/build script should look like the following:

    +
    #!/usr/bin/env bash
     set -eo pipefail
     
    -echo "---> Ruby Buildpack"
    +echo "---> NodeJS Buildpack"
     
    +# ======= MODIFIED =======
     # 1. GET ARGS
     layersdir=$1
     plan=$3
     
     # 2. CREATE THE LAYER DIRECTORY
    -rubylayer="$layersdir"/ruby
    -mkdir -p "$rubylayer"
    -
    -# 3. DOWNLOAD RUBY
    -ruby_version=$(cat "$plan" | yj -t | jq -r '.entries[] | select(.name == "ruby") | .metadata.version')
    -echo "---> Downloading and extracting Ruby $ruby_version"
    -ruby_url=https://s3-external-1.amazonaws.com/heroku-buildpack-ruby/heroku-22/ruby-$ruby_version.tgz
    -wget -q -O - "$ruby_url" | tar -xzf - -C "$rubylayer"
    -
    -# 4. MAKE RUBY AVAILABLE DURING LAUNCH
    -echo -e '[types]\nlaunch = true' > "$layersdir/ruby.toml"
    -
    -# 5. MAKE RUBY AVAILABLE TO THIS SCRIPT
    -export PATH="$rubylayer"/bin:$PATH
    -export LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}"$rubylayer/lib"
    -
    -# 6. INSTALL GEMS
    -# Compares previous Gemfile.lock checksum to the current Gemfile.lock
    -bundlerlayer="$layersdir/bundler"
    -local_bundler_checksum=$((sha256sum Gemfile.lock || echo 'DOES_NOT_EXIST') | cut -d ' ' -f 1)
    -remote_bundler_checksum=$(cat "$layersdir/bundler.toml" | yj -t | jq -r .metadata.checksum 2>/dev/null || echo 'DOES_NOT_EXIST')
    -# Always set the types table so that we re-use the appropriate layers
    -echo -e '[types]\ncache = true\nlaunch = true' >> "$layersdir/bundler.toml"
    +node_js_layer="${layersdir}"/node-js
    +mkdir -p "${node_js_layer}"
     
    -if [[ -f Gemfile.lock && $local_bundler_checksum == $remote_bundler_checksum ]] ; then
    -    # Determine if no gem dependencies have changed, so it can reuse existing gems without running bundle install
    -    echo "---> Reusing gems"
    -    bundle config --local path "$bundlerlayer" >/dev/null
    -    bundle config --local bin "$bundlerlayer/bin" >/dev/null
    +# 3. DOWNLOAD node-js
    +default_node_js_version="18.18.1"
    +node_js_version=$(cat "$plan" | yj -t | jq -r '.entries[] | select(.name == "node-js") | .metadata.version' || echo ${default_node_js_version})
    +node_js_url=https://nodejs.org/dist/v${node_js_version}/node-v${node_js_version}-linux-x64.tar.xz
    +remote_nodejs_version=$(cat "${layersdir}/node-js.toml" 2> /dev/null | yj -t | jq -r .metadata.nodejs_version 2>/dev/null || echo 'NOT FOUND')
    +if [[ "${node_js_url}" != *"${remote_nodejs_version}"* ]] ; then
    +    echo "-----> Downloading and extracting NodeJS"
    +    wget -q -O - "${node_js_url}" | tar -xJf - --strip-components 1 -C "${node_js_layer}"
     else
    -    # Determine if there has been a gem dependency change and install new gems to the bundler layer; re-using existing and un-changed gems
    -    echo "---> Installing gems"
    -    mkdir -p "$bundlerlayer"
    -    cat >> "$layersdir/bundler.toml" << EOL
    +    echo "-----> Reusing NodeJS"
    +fi
    +
    +# 4. MAKE node-js AVAILABLE DURING LAUNCH and CACHE the LAYER
    +    cat > "${layersdir}/node-js.toml" << EOL
    +[types]
    +cache = true
    +launch = true
     [metadata]
    -checksum = "$local_bundler_checksum"
    +nodejs_version = "${node_js_version}"
     EOL
    -    bundle config set --local path "$bundlerlayer" && bundle install && bundle binstubs --all --path "$bundlerlayer/bin"
    -
    -fi
     
    -# 7. SET DEFAULT START COMMAND
    -cat > "$layersdir/launch.toml" << EOL
    -# our web process
    +# 5. SET DEFAULT START COMMAND
    +cat >> "${layersdir}/launch.toml" << EOL
     [[processes]]
     type = "web"
    -command = "bundle exec ruby app.rb"
    +command = "node app.js"
     default = true
    -
    -# our worker process
    -[[processes]]
    -type = "worker"
    -command = "bundle exec ruby worker.rb"
     EOL
     
     # ========== ADDED ===========
    -# 8. ADD A SBOM
    -rubybom="${layersdir}/ruby.sbom.cdx.json"
    -cat >> ${rubybom} << EOL
    +# 6. ADD A SBOM
    +node_jsbom="${layersdir}/node-js.sbom.cdx.json"
    +cat >> ${node_jsbom} << EOL
     {
       "bomFormat": "CycloneDX",
       "specVersion": "1.4",
    @@ -910,36 +873,24 @@ 

    Adding Bill-of-Materials

    "components": [ { "type": "library", - "name": "ruby", - "version": "$ruby_version" + "name": "node-js", + "version": "${node_js_version}" } ] } EOL -if [[ -f Gemfile.lock ]] ; then - for gem in $(gem dep -q | grep ^Gem | sed 's/^Gem //') - do - version=${gem##*-} - name=${gem%-${version}} - DEP=$(jq --arg name "${name}" --arg version "${version}" \ - '.components[.components| length] |= . + {"type": "library", "name": $name, "version": $version}' \ - "${rubybom}") - echo ${DEP} > "${rubybom}" - done -fi

    Then rebuild your app using the updated buildpack:

    -
    pack build test-ruby-app --path ./ruby-sample-app --buildpack ./ruby-buildpack
    +
    pack build test-node-js-app --path ./node-js-sample-app --buildpack ./node-js-buildpack
     

    Viewing your bill-of-materials requires extracting (or downloading) the bill-of-materials from your local image. This command can take some time to return.

    -
    pack sbom download test-ruby-app
    +
    pack sbom download test-node-js-app
     

    The SBOM information is now downloaded to the local file system:

    -
    cat layers/sbom/launch/examples_ruby/ruby/sbom.cdx.json | jq -M
    -

    You should find that the included ruby version is 3.1.0 as expected.

    - +
    cat layers/sbom/launch/examples_node-js/node-js/sbom.cdx.json | jq -M
    +

    You should find that the included node-js version is 18.18.1 as expected.

    {
       "bomFormat": "CycloneDX",
       "specVersion": "1.4",
    @@ -947,9 +898,9 @@ 

    Adding Bill-of-Materials

    "components": [ { "type": "library", - "name": "ruby", - "version": "3.1.0" - }, + "name": "node-js", + "version": "18.18.1" + } ... ] } @@ -957,7 +908,7 @@

    Adding Bill-of-Materials

    Going further

    Now that you’ve finished your buildpack, how about extending it? Try:

    diff --git a/docs/buildpack-author-guide/create-buildpack/build-app/index.html b/docs/buildpack-author-guide/create-buildpack/build-app/index.html index 22b1b6f56..b89bae524 100644 --- a/docs/buildpack-author-guide/create-buildpack/build-app/index.html +++ b/docs/buildpack-author-guide/create-buildpack/build-app/index.html @@ -9,7 +9,7 @@ +Create a layer for the…"/> @@ -24,7 +24,7 @@ +Create a layer for the…"> @@ -743,92 +743,72 @@

    Building your application

    Now we’ll change the build step you created to install application dependencies. This will require updates to the build script such that it performs the following steps:

      -
    1. Creates a layer for the Ruby runtime
    2. -
    3. Downloads the Ruby runtime and installs it to the layer
    4. -
    5. Installs Bundler (the Ruby dependency manager)
    6. -
    7. Uses Bundler to install dependencies
    8. +
    9. Create a layer for the NodeJS runtime
    10. +
    11. Download the NodeJS runtime and installs it to the layer

    By doing this, you’ll learn how to create arbitrary layers with your buildpack, and how to read the contents of the app in order to perform actions like downloading dependencies.

    -

    Let’s begin by changing the ruby-buildpack/bin/build so that it creates a layer for Ruby.

    +

    Let’s begin by changing the node-js-buildpack/bin/build so that it creates a layer for NodeJS.

    Creating a Layer

    -

    A Buildpack layer is represented by a directory inside the layers directory provided to our buildpack by the Buildpack execution environment. To create a new layer directory representing the Ruby runtime, change the build script to look like this:

    - +

    A Buildpack layer is represented by a directory inside the layers directory provided to our buildpack by the Buildpack execution environment. As defined by the buildpack specification, the layers directory is always passed to the build script as the first positional parameter. To create a new layer directory representing the NodeJS runtime, change the build script to look like this:

    +
    #!/usr/bin/env bash
     set -eo pipefail
     
    -echo "---> Ruby Buildpack"
    +echo "---> NodeJS Buildpack"
     
     layersdir=$1
     
    -rubylayer="$layersdir"/ruby
    -mkdir -p "$rubylayer"
    -

    The rubylayer directory is a sub-directory of the directory provided as the first positional argument to the build script (the layers directory), and this is where we’ll store the Ruby runtime.

    -

    Next, we’ll download the Ruby runtime and install it into the layer directory. Add the following code to the end of the build script:

    - -
    echo "---> Downloading and extracting Ruby"
    -ruby_url=https://s3-external-1.amazonaws.com/heroku-buildpack-ruby/heroku-22/ruby-3.1.3.tgz
    -wget -q -O - "$ruby_url" | tar -xzf - -C "$rubylayer"
    -

    This code uses the wget tool to download the Ruby binaries from the given URL, and extracts it to the rubylayer directory.

    -

    The last step in creating a layer is writing a TOML file that contains metadata about the layer. The TOML file’s name must match the name of the layer (in this example it’s ruby.toml). Without this file, the Buildpack lifecycle will ignore the layer directory. For the Ruby layer, we need to ensure it is available in the launch image by setting the launch key to true. Add the following code to the build script:

    - -
    echo -e '[types]\nlaunch = true' > "$layersdir/ruby.toml"
    -

    Installing Dependencies

    -

    Next, we’ll use the Ruby runtime you installed to download the application’s dependencies. First, we need to make the Ruby executables available to our script by putting it on the Path. Add the following code to the end of the build script:

    - -
    export PATH="$rubylayer"/bin:$PATH
    -export LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}"$rubylayer/lib"
    -

    Now we can install Bundler, a dependency manager for Ruby, and run the bundle install command. Append the following code to the script:

    - -
    echo "---> Installing gems"
    -bundle install
    +node_js_layer="${layersdir}"/node-js
    +mkdir -p "${node_js_layer}"
    +

    The node_js_layer directory is a sub-directory of the directory provided as the first positional argument to the build script (the layers directory), and this is where we’ll store the NodeJS runtime.

    +

    Next, we’ll download the NodeJS runtime and install it into the layer directory. Add the following code to the end of the build script:

    + +
    echo "---> Downloading and extracting NodeJS"
    +node_js_url=https://nodejs.org/dist/v18.18.1/node-v18.18.1-linux-x64.tar.xz
    +wget -q -O - "${node_js_url}" | tar -xJf - --strip-components 1 -C "${node_js_layer}"
    +

    This code uses the wget tool to download the NodeJS binaries from the given URL, and extracts it to the node_js_layer directory. We use tar to extract the NodeJS distribution into the node_js_layer. During the extraction we remove the top level directory (i.e. --strip-components 1). This means that we will end up with ${node_js_layer}/bin and ${node_js_layer}/lib. When starting the container the layers bin will automatically be added to the runtime ${PATH}.

    +

    The last step in creating a layer is writing a TOML file that contains metadata about the layer. The TOML file’s name must match the name of the layer (in this example it’s node-js.toml). Without this file, the Buildpack lifecycle will ignore the layer directory. For the NodeJS layer, we need to ensure it is available in the launch image by setting the launch key to true. Add the following code to the build script:

    + +
    echo -e '[types]\nlaunch = true' > "${layersdir}/node-js.toml"
     

    Now the Buildpack is ready to test.

    Running the Build

    -

    Your complete ruby-buildpack/bin/build script should look like this:

    - +

    Your complete node-js-buildpack/bin/build script should look like this:

    +
    #!/usr/bin/env bash
     set -eo pipefail
     
    -echo "---> Ruby Buildpack"
    +echo "---> NodeJS Buildpack"
     
     # 1. GET ARGS
     layersdir=$1
     
     # 2. CREATE THE LAYER DIRECTORY
    -rubylayer="$layersdir"/ruby
    -mkdir -p "$rubylayer"
    +node_js_layer="${layersdir}"/node-js
    +mkdir -p "${node_js_layer}"
     
    -# 3. DOWNLOAD RUBY
    -echo "---> Downloading and extracting Ruby"
    -ruby_url=https://s3-external-1.amazonaws.com/heroku-buildpack-ruby/heroku-22/ruby-3.1.3.tgz
    -wget -q -O - "$ruby_url" | tar -xzf - -C "$rubylayer"
    +# 3. DOWNLOAD node-js
    +echo "---> Downloading and extracting NodeJS"
    +node_js_url=https://nodejs.org/dist/v18.18.1/node-v18.18.1-linux-x64.tar.xz
    +wget -q -O - "${node_js_url}" | tar -xJf - --strip-components 1 -C "${node_js_layer}"
     
    -# 4. MAKE RUBY AVAILABLE DURING LAUNCH
    -echo -e '[types]\nlaunch = true' > "$layersdir/ruby.toml"
    -
    -# 5. MAKE RUBY AVAILABLE TO THIS SCRIPT
    -export PATH="$rubylayer"/bin:$PATH
    -export LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}"$rubylayer/lib"
    -
    -# 6. INSTALL GEMS
    -echo "---> Installing gems"
    -bundle install
    +# 4. MAKE node-js AVAILABLE DURING LAUNCH
    +echo -e '[types]\nlaunch = true' > "${layersdir}/node-js.toml"
     

    Build your app again:

    -
    pack build test-ruby-app --path ./ruby-sample-app --buildpack ./ruby-buildpack
    +
    pack build test-node-js-app --path ./node-js-sample-app --buildpack ./node-js-buildpack
     

    You will see the following output:

    ===> DETECTING
     ...
     ===> RESTORING
     ===> BUILDING
    ----> Ruby Buildpack
    ----> Downloading and extracting Ruby
    ----> Installing gems
    +---> NodeJS Buildpack
    +---> Downloading and extracting NodeJS
     ...
     ===> EXPORTING
     ...
    -Successfully built image 'test-ruby-app'
    -

    A new image named test-ruby-app was created in your Docker daemon with a layer containing the Ruby runtime. However, your app image is not yet runnable. We’ll make the app image runnable in the next section.

    +Successfully built image 'test-node-js-app' +

    A new image named test-node-js-app was created in your Docker daemon with a layer containing the NodeJS runtime. However, your app image is not yet runnable. We’ll make the app image runnable in the next section.


    Next Step

    diff --git a/docs/buildpack-author-guide/create-buildpack/building-blocks-cnb/index.html b/docs/buildpack-author-guide/create-buildpack/building-blocks-cnb/index.html index 9dab55eb3..0555db968 100644 --- a/docs/buildpack-author-guide/create-buildpack/building-blocks-cnb/index.html +++ b/docs/buildpack-author-guide/create-buildpack/building-blocks-cnb/index.html @@ -749,13 +749,13 @@

    Using the Pack CLI

    The buildpack new <id> command will create a directory named for the buildpack ID.

    Example:

    -
    pack buildpack new examples/ruby \
    +
    pack buildpack new examples/node-js \
         --api 0.8 \
    -    --path ruby-buildpack \
    +    --path node-js-buildpack \
         --version 0.0.1 \
         --stacks io.buildpacks.samples.stacks.jammy
     
    -

    This command will create ruby-buildpack directory which contains buildpack.toml, bin/build, bin/detect files.

    +

    This command will create node-js-buildpack directory which contains buildpack.toml, bin/build, bin/detect files.

    Additional Parameters

    • -a, --api Buildpack API compatibility of the generated buildpack
    • @@ -765,14 +765,14 @@

      Additional Parameters

    • -V, --version the version of the buildpack in buildpack.toml

    buildpack.toml

    -

    You will have ruby-buildpack/buildpack.toml in your buildpack directory to describe our buildpack.

    - +

    You will have node-js-buildpack/buildpack.toml in your buildpack directory to describe our buildpack.

    +
    # Buildpack API version
     api = "0.8"
     
     # Buildpack ID and metadata
     [buildpack]
    -  id = "examples/ruby"
    +  id = "examples/node-js"
       version = "0.0.1"
     
     # Targets the buildpack will work with
    @@ -787,18 +787,18 @@ 

    buildpack.toml

    The stack ID (deprecated) uniquely identifies a build and run image configuration the buildpack will work with. This example can be run on Ubuntu Jammy.

    detect and build

    Next, we will cover the detect and build scripts. These files are created in bin directory in your buildpack directory.

    -

    Now update your ruby-buildpack/bin/detect file and copy in the following contents:

    - +

    Now update your node-js-buildpack/bin/detect file and copy in the following contents:

    +
    #!/usr/bin/env bash
     set -eo pipefail
     
     exit 1
    -

    Also update your ruby-buildpack/bin/build file and copy in the following contents:

    - +

    Also update your node-js-buildpack/bin/build file and copy in the following contents:

    +
    #!/usr/bin/env bash
     set -eo pipefail
     
    -echo "---> Ruby Buildpack"
    +echo "---> NodeJS Buildpack"
     exit 1
     

    These two files are executable detect and build scripts. You are now able to use this buildpack.

    Using your buildpack with pack

    @@ -813,14 +813,14 @@

    Using your buildpack with pack

    Then run the following pack command:

    -
    pack build test-ruby-app --path ./ruby-sample-app --buildpack ./ruby-buildpack
    +
    pack build test-node-js-app --path ./node-js-sample-app --buildpack ./node-js-buildpack
     

    The pack build command takes in your Ruby sample app as the --path argument and your buildpack as the --buildpack argument.

    After running the command, you should see that it failed to detect, as the detect script is currently written to simply error out.

    ===> DETECTING
     ...
    -err:  examples/ruby@0.0.1 (1)
    +err:  examples/node-js@0.0.1 (1)
     ...
     ERROR: No buildpack groups passed detection.
     ERROR: failed to detect: buildpack(s) failed with err
    diff --git a/docs/buildpack-author-guide/create-buildpack/caching/index.html b/docs/buildpack-author-guide/create-buildpack/caching/index.html
    index b69349321..62774f8c8 100644
    --- a/docs/buildpack-author-guide/create-buildpack/caching/index.html
    +++ b/docs/buildpack-author-guide/create-buildpack/caching/index.html
    @@ -8,8 +8,8 @@
     
     
     
    -
    +
     
     
     
    @@ -23,8 +23,8 @@
     
     
     
    -
    +
     
     
     
    @@ -741,225 +741,113 @@ 

    Improving performance with caching

    -

    We can improve performance by caching dependencies between builds, only re-downloading when necessary. To begin, let’s create a cacheable bundler layer.

    -

    Creating the bundler layer

    +

    We can improve performance by caching the runtime between builds, only re-downloading when necessary. To begin, let’s cache the runtime layer.

    +

    Cache the runtime layer

    To do this, replace the following lines in the build script:

    -
    echo "---> Installing gems"
    -bundle install
    +
    # 4. MAKE node-js AVAILABLE DURING LAUNCH
    +echo -e '[types]\nlaunch = true' > "${layersdir}/node-js.toml"
     

    with the following:

    -
    echo "---> Installing gems"
    -bundlerlayer="$layersdir/bundler"
    -mkdir -p "$bundlerlayer"
    -echo -e '[types]\ncache = true\nlaunch = true' > "$layersdir/bundler.toml"
    -bundle config set --local path "$bundlerlayer" && bundle install && bundle binstubs --all --path "$bundlerlayer/bin"
    -

    Your full ruby-buildpack/bin/build script should now look like the following:

    - +
    # 4. MAKE node-js AVAILABLE DURING LAUNCH and CACHE it
    +echo -e '[types]\ncache = true\nlaunch = true' > "${layersdir}/node-js.toml"
    +

    Your full node-js-buildpack/bin/build script should now look like the following:

    +
    #!/usr/bin/env bash
     set -eo pipefail
     
    -echo "---> Ruby Buildpack"
    +echo "---> NodeJS Buildpack"
     
     # 1. GET ARGS
     layersdir=$1
     
     # 2. CREATE THE LAYER DIRECTORY
    -rubylayer="$layersdir"/ruby
    -mkdir -p "$rubylayer"
    +node_js_layer="${layersdir}"/node-js
    +mkdir -p "${node_js_layer}"
     
    -# 3. DOWNLOAD RUBY
    -echo "---> Downloading and extracting Ruby"
    -ruby_url=https://s3-external-1.amazonaws.com/heroku-buildpack-ruby/heroku-22/ruby-3.1.3.tgz
    -wget -q -O - "$ruby_url" | tar -xzf - -C "$rubylayer"
    +# 3. DOWNLOAD NodeJS
    +echo "---> Downloading and extracting NodeJS"
    +node_js_url=https://nodejs.org/dist/v18.18.1/node-v18.18.1-linux-x64.tar.xz
    +wget -q -O - "$node_js_url" | tar -xJf - --strip-components 1 -C "${node_js_layer}"
     
    -# 4. MAKE RUBY AVAILABLE DURING LAUNCH
    -echo -e '[types]\nlaunch = true' > "$layersdir/ruby.toml"
    +# 4. MAKE NodeJS AVAILABLE DURING LAUNCH and CACHE the LAYER
    +# ========== MODIFIED ===========
    +echo -e '[types]\ncache = true\nlaunch = true' > "${layersdir}/node-js.toml"
     
    -# 5. MAKE RUBY AVAILABLE TO THIS SCRIPT
    -export PATH="$rubylayer"/bin:$PATH
    -export LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}"$rubylayer/lib"
    -
    -# ======= MODIFIED =======
    -# 6. INSTALL GEMS
    -echo "---> Installing gems"
    -bundlerlayer="$layersdir/bundler"
    -mkdir -p "$bundlerlayer"
    -echo -e '[types]\ncache = true\nlaunch = true' > "$layersdir/bundler.toml"
    -bundle config set --local path "$bundlerlayer" && bundle install && bundle binstubs --all --path "$bundlerlayer/bin"
    -
    -# 7. SET DEFAULT START COMMAND
    -cat > "$layersdir/launch.toml" << EOL
    +# 5. SET DEFAULT START COMMAND
    +cat > "${layersdir}/launch.toml" << EOL
     # our web process
     [[processes]]
     type = "web"
    -command = "bundle exec ruby app.rb"
    +command = "node app.js"
     default = true
     
    -# our worker process
    +# our debug process
     [[processes]]
    -type = "worker"
    -command = "bundle exec ruby worker.rb"
    +type = "debug"
    +command = "node --inspect app.js"
     EOL
    -

    Now when we run:

    +

    Now when we build the image twice we should see the node-js layer is reused on the second build:

    -
    pack build test-ruby-app --path ./ruby-sample-app --buildpack ./ruby-buildpack
    +
    pack build test-node-js-app --path ./node-js-sample-app --buildpack ./node-js-buildpack
    +pack build test-node-js-app --path ./node-js-sample-app --buildpack ./node-js-buildpack
     

    You will see something similar to the following during the EXPORTING phase:

    -
    Adding layer 'examples/ruby:bundler'
    +
    Reusing layer 'examples/node-js:node-js'
     

    Caching dependencies

    -

    Now, let’s implement the caching logic. We’ll first need to create a ruby-sample-app/Gemfile.lock file with the contents given below:

    -
    -

    Typically you would run bundle install locally to generate this file, but for the sake -of simplicity we’ll create ruby-sample-app/Gemfile.lock manually.

    -
    - -
    GEM
    -  remote: https://rubygems.org/
    -  specs:
    -    mustermann (1.0.3)
    -    rack (2.0.7)
    -    rack-protection (2.0.7)
    -      rack
    -    sinatra (2.0.7)
    -      mustermann (~> 1.0)
    -      rack (~> 2.0)
    -      rack-protection (= 2.0.7)
    -      tilt (~> 2.0)
    -    tilt (2.0.9)
    -
    -PLATFORMS
    -  ruby
    -
    -DEPENDENCIES
    -  sinatra
    -
    -BUNDLED WITH
    -   2.0.2
    -

    Replace the gem installation logic from the previous step:

    -
    # ...
    -
    -echo "---> Installing gems"
    -bundlerlayer="$layersdir/bundler"
    -mkdir -p "$bundlerlayer"
    -echo -e '[types]\ncache = true\nlaunch = true' > "$layersdir/bundler.toml"
    -bundle config set --local path "$bundlerlayer" && bundle install && bundle binstubs --all --path "$bundlerlayer/bin"
    -
    -
    -# ...
    -

    with the new logic below that checks to see if any gems have been changed. This simply creates a checksum for the previous Gemfile.lock and compares it to the checksum of the current Gemfile.lock. If they are the same, the gems are reused. If they are not, the new gems are installed.

    -

    We’ll now write additional metadata to our bundler.toml of the form cache = true and launch = true. This directs the lifecycle to cache our gems and provide them when launching our application. With cache = true the lifecycle can keep existing gems around so that build times are fast, even with minor Gemfile.lock changes.

    -

    Note that there may be times when you would want to clean the cached layer from the previous build, in which case you should always ensure to remove the contents of the layer before proceeding with the build. In the case below this can be done using a simple rm -rf "$bundlerlayer"/* after the mkdir -p "$bundlerlayer" command.

    -
    # Compares previous Gemfile.lock checksum to the current Gemfile.lock
    -bundlerlayer="$layersdir/bundler"
    -local_bundler_checksum=$((sha256sum Gemfile.lock || echo 'DOES_NOT_EXIST') | cut -d ' ' -f 1)
    -remote_bundler_checksum=$(cat "$layersdir/bundler.toml" | yj -t | jq -r .metadata.checksum 2>/dev/null || echo 'DOES_NOT_EXIST')
    -
    -# Always set the types table so that we re-use the appropriate layers
    -echo -e '[types]\ncache = true\nlaunch = true' >> "$layersdir/bundler.toml"
    -
    -if [[ -f Gemfile.lock && $local_bundler_checksum == $remote_bundler_checksum ]] ; then
    -    # Determine if no gem dependencies have changed, so it can reuse existing gems without running bundle install
    -    echo "---> Reusing gems"
    -    bundle config --local path "$bundlerlayer" >/dev/null
    -    bundle config --local bin "$bundlerlayer/bin" >/dev/null
    -else
    -    # Determine if there has been a gem dependency change and install new gems to the bundler layer; re-using existing and un-changed gems
    -    echo "---> Installing gems"
    -    mkdir -p "$bundlerlayer"
    -    cat >> "$layersdir/bundler.toml" << EOL
    -[metadata]
    -checksum = "$local_bundler_checksum"
    -EOL
    -    bundle config set --local path "$bundlerlayer" && bundle install && bundle binstubs --all --path "$bundlerlayer/bin"
    -
    -fi
    -

    Your full ruby-buildpack/bin/build script will now look like this:

    - -
    #!/usr/bin/env bash
    -set -eo pipefail
    -
    -echo "---> Ruby Buildpack"
    -
    -# 1. GET ARGS
    -layersdir=$1
    -
    -# 2. CREATE THE LAYER DIRECTORY
    -rubylayer="$layersdir"/ruby
    -mkdir -p "$rubylayer"
    -
    -# 3. DOWNLOAD RUBY
    -echo "---> Downloading and extracting Ruby"
    -ruby_url=https://s3-external-1.amazonaws.com/heroku-buildpack-ruby/heroku-22/ruby-3.1.3.tgz
    -wget -q -O - "$ruby_url" | tar -xzf - -C "$rubylayer"
    -
    -# 4. MAKE RUBY AVAILABLE DURING LAUNCH
    -echo -e '[types]\nlaunch = true' > "$layersdir/ruby.toml"
    -
    -# 5. MAKE RUBY AVAILABLE TO THIS SCRIPT
    -export PATH="$rubylayer"/bin:$PATH
    -export LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}"$rubylayer/lib"
    -
    -# ======= MODIFIED =======
    -# 6. INSTALL GEMS
    -# Compares previous Gemfile.lock checksum to the current Gemfile.lock
    -bundlerlayer="$layersdir/bundler"
    -local_bundler_checksum=$((sha256sum Gemfile.lock || echo 'DOES_NOT_EXIST') | cut -d ' ' -f 1)
    -remote_bundler_checksum=$(cat "$layersdir/bundler.toml" | yj -t | jq -r .metadata.checksum 2>/dev/null || echo 'DOES_NOT_EXIST')
    -# Always set the types table so that we re-use the appropriate layers
    -echo -e '[types]\ncache = true\nlaunch = true' >> "$layersdir/bundler.toml"
    -
    -if [[ -f Gemfile.lock && $local_bundler_checksum == $remote_bundler_checksum ]] ; then
    -    # Determine if no gem dependencies have changed, so it can reuse existing gems without running bundle install
    -    echo "---> Reusing gems"
    -    bundle config --local path "$bundlerlayer" >/dev/null
    -    bundle config --local bin "$bundlerlayer/bin" >/dev/null
    -else
    -    # Determine if there has been a gem dependency change and install new gems to the bundler layer; re-using existing and un-changed gems
    -    echo "---> Installing gems"
    -    mkdir -p "$bundlerlayer"
    -    cat >> "$layersdir/bundler.toml" << EOL
    -[metadata]
    -checksum = "$local_bundler_checksum"
    -EOL
    -    bundle config set --local path "$bundlerlayer" && bundle install && bundle binstubs --all --path "$bundlerlayer/bin"
    -
    -fi
    -
    -# 7. SET DEFAULT START COMMAND
    -cat > "$layersdir/launch.toml" << EOL
    -# our web process
    -[[processes]]
    -type = "web"
    -command = "bundle exec ruby app.rb"
    -default = true
    -
    -# our worker process
    -[[processes]]
    -type = "worker"
    -command = "bundle exec ruby worker.rb"
    -EOL
    -

    Now when you build your app:

    - -
    pack build test-ruby-app --path ./ruby-sample-app --buildpack ./ruby-buildpack
    -
    -

    it will download the gems:

    - -
    ===> BUILDING
    -...
    ----> Ruby Buildpack
    ----> Downloading and extracting Ruby
    ----> Installing gems
    -

    If you build the app again:

    +

    Now, let’s implement the caching logic. We need to record the version of the NodeJS runtime that is used in a build. On subsequent builds, the caching logic will detect if the NodeJS version is the same as the version in the cached layer. We restore the previous layer from the cache if the current requested NodeJS version matches the previous NodeJS runtime version.

    + +
    #!/usr/bin/env bash
    +set -eo pipefail
    +
    +echo "---> NodeJS Buildpack"
    +
    +# 1. GET ARGS
    +layersdir=$1
    +
    +# 2. CREATE THE LAYER DIRECTORY
    +node_js_layer="${layersdir}"/node-js
    +mkdir -p "${node_js_layer}"
    +
    +# ======= MODIFIED =======
    +# 3. DOWNLOAD node-js
    +node_js_version="18.18.1"
    +node_js_url=https://nodejs.org/dist/v${node_js_version}/node-v${node_js_version}-linux-x64.tar.xz
    +cached_nodejs_version=$(cat "${layersdir}/node-js.toml" 2> /dev/null | yj -t | jq -r .metadata.nodejs_version 2>/dev/null || echo 'NOT FOUND')
    +if [[ "${node_js_url}" != *"${cached_nodejs_version}"* ]] ; then
    +    echo "-----> Downloading and extracting NodeJS"
    +    wget -q -O - "${node_js_url}" | tar -xJf - --strip-components 1 -C "${node_js_layer}"
    +else
    +    echo "-----> Reusing NodeJS"
    +fi
    +
    +# ======= MODIFIED =======
    +# 4. MAKE node-js AVAILABLE DURING LAUNCH and CACHE the LAYER
    +    cat > "${layersdir}/node-js.toml" << EOL
    +[types]
    +cache = true
    +launch = true
    +[metadata]
    +nodejs_version = "${node_js_version}"
    +EOL
    +
    +# 5. SET DEFAULT START COMMAND
    +cat >> "${layersdir}/launch.toml" << EOL
    +[[processes]]
    +type = "web"
    +command = "node app.js"
    +default = true
    +EOL
    +

    Now when you build your app, the second call will reuse the layer:

    -
    pack build test-ruby-app --path ./ruby-sample-app --buildpack ./ruby-buildpack
    +
    pack build test-node-js-app --path ./node-js-sample-app --buildpack ./node-js-buildpack
    +pack build test-node-js-app --path ./node-js-sample-app --buildpack ./node-js-buildpack
     

    you will see the new caching logic at work during the BUILDING phase:

    ===> BUILDING
     ...
    ----> Ruby Buildpack
    ----> Downloading and extracting Ruby
    ----> Reusing gems
    +---> NodeJS Buildpack
    +-----> Reusing NodeJS
     

    Next, let’s see how buildpack users may be able to provide configuration to the buildpack.


    diff --git a/docs/buildpack-author-guide/create-buildpack/detection/index.html b/docs/buildpack-author-guide/create-buildpack/detection/index.html index 6b889c652..b4107939c 100644 --- a/docs/buildpack-author-guide/create-buildpack/detection/index.html +++ b/docs/buildpack-author-guide/create-buildpack/detection/index.html @@ -8,8 +8,8 @@ - + @@ -23,8 +23,8 @@ - + @@ -741,33 +741,33 @@

    Detecting your application

    -

    Next, you will want to actually detect that the app you are building is a Ruby app. In order to do this, you will need to check for a Gemfile.

    +

    Next, you will want to actually detect that the app you are building is a node-js app. In order to do this, you will need to check for a package.json.

    Replace exit 1 in the detect script with the following check:

    -
    if [[ ! -f Gemfile ]]; then
    +
    if [[ ! -f package.json ]]; then
        exit 100
     fi
    -

    Your ruby-buildpack/bin/detect script should look like this:

    - +

    Your node-js-buildpack/bin/detect script should look like this:

    +
    #!/usr/bin/env bash
     set -eo pipefail
     
    -if [[ ! -f Gemfile ]]; then
    +if [[ ! -f package.json ]]; then
        exit 100
     fi
     

    Next, rebuild your app with the updated buildpack:

    - -
    pack build test-ruby-app --path ./ruby-sample-app --buildpack ./ruby-buildpack
    +
    +
    pack build test-node-js-app --path ./node-js-sample-app --buildpack ./node-js-buildpack
     

    You should see the following output:

    -
    Previous image with name "test-ruby-app" not found
    +
    Previous image with name "test-node-js-app" not found
     ===> DETECTING
    -examples/ruby 0.0.1
    +examples/node-js 0.0.1
     ===> RESTORING
     ===> BUILDING
    ----> Ruby Buildpack
    +---> node-js Buildpack
     ERROR: failed to build: exit status 1
     ERROR: failed to build: executing lifecycle: failed with status code: 51
    -

    Notice that detect now passes because there is a valid Gemfile in the Ruby app at ruby-sample-app, but now build fails because it is currently written to error out.

    +

    Notice that detect now passes because there is a valid package.json in the NodeJS app at node-js-sample-app, but now build fails because it is currently written to error out.

    You will also notice that RESTORING now appears in the build output. This step is part of the buildpack lifecycle that looks to see if any previous image builds have layers that the buildpack can re-use. We will get into this topic in more detail later.


    diff --git a/docs/buildpack-author-guide/create-buildpack/index.xml b/docs/buildpack-author-guide/create-buildpack/index.xml index 63063e01d..bbdef5bbd 100644 --- a/docs/buildpack-author-guide/create-buildpack/index.xml +++ b/docs/buildpack-author-guide/create-buildpack/index.xml @@ -13,9 +13,8 @@ Mon, 01 Jan 0001 00:00:00 +0000 https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/setup-local-environment/ First, we&rsquo;ll create a sample Ruby app that you can use when developing your buildpack: -mkdir ruby-sample-app Create a file in the current directory called ruby-sample-app/app.rb with the following contents: -require &#39;sinatra&#39; set :bind, &#39;0.0.0.0&#39; set :port, 8080 get &#39;/&#39; do &#39;Hello World!&#39; end Then, create a file called ruby-sample-app/Gemfile with the following contents: -source &#39;http://rubygems.org&#39; git_source(:github) {|repo_name| &#34;https://github.com/#{repo_name}&#34; } gem &#39;sinatra&#39; gem &#39;webrick&#39; Finally, make sure your local Docker daemon is running by executing: +mkdir node-js-sample-app Create a file in the current directory called node-js-sample-app/app.js with the following contents: +const http = require(&#39;http&#39;); const hostname = &#39;0.0.0.0&#39;; const port = 8080; const server = http.createServer((req, res) =&gt; { res.statusCode = 200; res.setHeader(&#39;Content-Type&#39;, &#39;text/plain&#39;); res.end(&#39;Hello World!&#39;); }); // For demo purposes we do not actually start the server. This // allows us pretend to start the server and check if the output // message is correct. Building blocks of a Cloud Native Buildpack @@ -26,7 +25,7 @@ source &#39;http://rubygems.org&#39; git_source(:github) {|repo_name| &a Let&rsquo;s create the directory where your buildpack will live: Using the Pack CLI The buildpack new &lt;id&gt; command will create a directory named for the buildpack ID. Example: -pack buildpack new examples/ruby \ --api 0.8 \ --path ruby-buildpack \ --version 0.0.1 \ --stacks io.buildpacks.samples.stacks.jammy This command will create ruby-buildpack directory which contains buildpack.toml, bin/build, bin/detect files. +pack buildpack new examples/node-js \ --api 0.8 \ --path node-js-buildpack \ --version 0.0.1 \ --stacks io.buildpacks.samples.stacks.jammy This command will create node-js-buildpack directory which contains buildpack.toml, bin/build, bin/detect files. Additional Parameters -a, --api Buildpack API compatibility of the generated buildpack -h, --help Help for &rsquo;new' --path the location on the filesystem to generate the artifacts --stacks Stacks (deprecated) the buildpack will work with -V, --version the version of the buildpack in buildpack. @@ -34,10 +33,10 @@ Additional Parameters -a, --api Buildpack API compatibility of the generated bui https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/detection/ Mon, 01 Jan 0001 00:00:00 +0000 https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/detection/ - Next, you will want to actually detect that the app you are building is a Ruby app. In order to do this, you will need to check for a Gemfile. + Next, you will want to actually detect that the app you are building is a node-js app. In order to do this, you will need to check for a package.json. Replace exit 1 in the detect script with the following check: -if [[ ! -f Gemfile ]]; then exit 100 fi Your ruby-buildpack/bin/detect script should look like this: -#!/usr/bin/env bash set -eo pipefail if [[ ! -f Gemfile ]]; then exit 100 fi Next, rebuild your app with the updated buildpack: +if [[ ! -f package.json ]]; then exit 100 fi Your node-js-buildpack/bin/detect script should look like this: +#!/usr/bin/env bash set -eo pipefail if [[ ! -f package.json ]]; then exit 100 fi Next, rebuild your app with the updated buildpack: Building your application @@ -45,7 +44,7 @@ if [[ ! -f Gemfile ]]; then exit 100 fi Your ruby-buildpack/bin/detect script sh Mon, 01 Jan 0001 00:00:00 +0000 https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/build-app/ Now we&rsquo;ll change the build step you created to install application dependencies. This will require updates to the build script such that it performs the following steps: -Creates a layer for the Ruby runtime Downloads the Ruby runtime and installs it to the layer Installs Bundler (the Ruby dependency manager) Uses Bundler to install dependencies By doing this, you&rsquo;ll learn how to create arbitrary layers with your buildpack, and how to read the contents of the app in order to perform actions like downloading dependencies. +Create a layer for the NodeJS runtime Download the NodeJS runtime and installs it to the layer By doing this, you&rsquo;ll learn how to create arbitrary layers with your buildpack, and how to read the contents of the app in order to perform actions like downloading dependencies. Make your application runnable @@ -53,35 +52,34 @@ Creates a layer for the Ruby runtime Downloads the Ruby runtime and installs it Mon, 01 Jan 0001 00:00:00 +0000 https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/make-app-runnable/ To make your app runnable, a default start command must be set. You&rsquo;ll need to add the following to the end of your build script: -# ... # Set default start command cat &gt; &#34;$layersdir/launch.toml&#34; &lt;&lt; EOL [[processes]] type = &#34;web&#34; command = &#34;bundle exec ruby app.rb&#34; default = true EOL # ... Your full ruby-buildpack/bin/build script should now look like the following: -#!/usr/bin/env bash set -eo pipefail echo &#34;---&gt; Ruby Buildpack&#34; # 1. +# ... # Set default start command cat &gt; &#34;${layersdir}/launch.toml&#34; &lt;&lt; EOL [[processes]] type = &#34;web&#34; command = &#34;node app.js&#34; default = true EOL # ... Your full node-js-buildpack/bin/build script should now look like the following: +#!/usr/bin/env bash set -eo pipefail echo &#34;---&gt; NodeJS Buildpack&#34; # 1. Specify multiple process types https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/specify-multiple-process-types/ Mon, 01 Jan 0001 00:00:00 +0000 https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/specify-multiple-process-types/ - One of the benefits of buildpacks is that they are multi-process - an image can have multiple entrypoints for each operational mode. Let&rsquo;s see how this works. We will extend our app to have a worker process. -Let&rsquo;s create a worker file, ruby-sample-app/worker.rb, with the following contents: -for i in 0..5 puts &#34;Running a worker task...&#34; end After building our app, we could run the resulting image with the web process (currently the default) or our new worker process. + One of the benefits of buildpacks is that they are multi-process - an image can have multiple entrypoints for each operational mode. Let&rsquo;s see how this works. We will extend our app to have an entrypoint that allows a debugger to attach to it. +To enable running the debug process, we&rsquo;ll need to have our buildpack define a &ldquo;process type&rdquo; for the worker. Modify the section where processes are defined to: Improving performance with caching https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/caching/ Mon, 01 Jan 0001 00:00:00 +0000 https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/caching/ - We can improve performance by caching dependencies between builds, only re-downloading when necessary. To begin, let&rsquo;s create a cacheable bundler layer. -Creating the bundler layer To do this, replace the following lines in the build script: -echo &#34;---&gt; Installing gems&#34; bundle install with the following: -echo &#34;---&gt; Installing gems&#34; bundlerlayer=&#34;$layersdir/bundler&#34; mkdir -p &#34;$bundlerlayer&#34; echo -e &#39;[types]\ncache = true\nlaunch = true&#39; &gt; &#34;$layersdir/bundler.toml&#34; bundle config set --local path &#34;$bundlerlayer&#34; &amp;&amp; bundle install &amp;&amp; bundle binstubs --all --path &#34;$bundlerlayer/bin&#34; Your full ruby-buildpack/bin/build script should now look like the following: + We can improve performance by caching the runtime between builds, only re-downloading when necessary. To begin, let&rsquo;s cache the runtime layer. +Cache the runtime layer To do this, replace the following lines in the build script: +# 4. MAKE node-js AVAILABLE DURING LAUNCH echo -e &#39;[types]\nlaunch = true&#39; &gt; &#34;${layersdir}/node-js.toml&#34; with the following: +# 4. MAKE node-js AVAILABLE DURING LAUNCH and CACHE it echo -e &#39;[types]\ncache = true\nlaunch = true&#39; &gt; &#34;${layersdir}/node-js. Making your buildpack configurable https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/make-buildpack-configurable/ Mon, 01 Jan 0001 00:00:00 +0000 https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/make-buildpack-configurable/ - It&rsquo;s likely that not all Ruby apps will want to use the same version of Ruby. Let&rsquo;s make the Ruby version configurable. -Select Ruby version We&rsquo;ll allow buildpack users to define the desired Ruby version via a .ruby-version file in their app. We&rsquo;ll first update the detect script to check for this file. We will then record the dependency we can provide (Ruby), as well as the specific dependency the application will require, in the Build Plan, a document the lifecycle uses to determine if the buildpack will provide everything the application needs. + It&rsquo;s likely that not all NodeJS apps will want to use the same version of NodeJS. Let&rsquo;s make the NodeJS version configurable. +Select NodeJS version We&rsquo;ll allow buildpack users to define the desired NodeJS version via a .node-js-version file in their app. We&rsquo;ll first update the detect script to check for this file. We will then record the dependency we can provide (NodeJS), as well as the specific dependency the application will require, in the Build Plan, a document the lifecycle uses to determine if the buildpack will provide everything the application needs. Adding Bill-of-Materials diff --git a/docs/buildpack-author-guide/create-buildpack/make-app-runnable/index.html b/docs/buildpack-author-guide/create-buildpack/make-app-runnable/index.html index dc63cba4c..ab3b6b4b1 100644 --- a/docs/buildpack-author-guide/create-buildpack/make-app-runnable/index.html +++ b/docs/buildpack-author-guide/create-buildpack/make-app-runnable/index.html @@ -742,68 +742,57 @@

    Make your application runnable

    To make your app runnable, a default start command must be set. You’ll need to add the following to the end of your build script:

    - +
    # ...
     
     # Set default start command
    -cat > "$layersdir/launch.toml" << EOL
    +cat > "${layersdir}/launch.toml" << EOL
     [[processes]]
     type = "web"
    -command = "bundle exec ruby app.rb"
    +command = "node app.js"
     default = true
     EOL
     
     # ...
    -

    Your full ruby-buildpack/bin/build script should now look like the following:

    - +

    Your full node-js-buildpack/bin/build script should now look like the following:

    +
    #!/usr/bin/env bash
     set -eo pipefail
     
    -echo "---> Ruby Buildpack"
    +echo "---> NodeJS Buildpack"
     
     # 1. GET ARGS
     layersdir=$1
     
     # 2. CREATE THE LAYER DIRECTORY
    -rubylayer="$layersdir"/ruby
    -mkdir -p "$rubylayer"
    +node_js_layer="${layersdir}"/node-js
    +mkdir -p "${node_js_layer}"
     
    -# 3. DOWNLOAD RUBY
    -echo "---> Downloading and extracting Ruby"
    -ruby_url=https://s3-external-1.amazonaws.com/heroku-buildpack-ruby/heroku-22/ruby-3.1.3.tgz
    -wget -q -O - "$ruby_url" | tar -xzf - -C "$rubylayer"
    +# 3. DOWNLOAD node-js
    +echo "---> Downloading and extracting NodeJS"
    +node_js_url=https://nodejs.org/dist/v18.18.1/node-v18.18.1-linux-x64.tar.xz
    +wget -q -O - "$node_js_url" | tar -xJf - --strip-components 1 -C "${node_js_layer}"
     
    -# 4. MAKE RUBY AVAILABLE DURING LAUNCH
    -echo -e '[types]\nlaunch = true' > "$layersdir/ruby.toml"
    -
    -# 5. MAKE RUBY AVAILABLE TO THIS SCRIPT
    -export PATH="$rubylayer"/bin:$PATH
    -export LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}"$rubylayer/lib"
    -
    -# 6. INSTALL GEMS
    -echo "---> Installing gems"
    -bundle install
    +# 4. MAKE node-js AVAILABLE DURING LAUNCH
    +echo -e '[types]\nlaunch = true' > "${layersdir}/node-js.toml"
     
     # ========== ADDED ===========
    -# 7. SET DEFAULT START COMMAND
    -cat > "$layersdir/launch.toml" << EOL
    +# 5. SET DEFAULT START COMMAND
    +cat > "${layersdir}/launch.toml" << EOL
     [[processes]]
     type = "web"
    -command = "bundle exec ruby app.rb"
    +command = "node app.js"
     default = true
     EOL
     

    Then rebuild your app using the updated buildpack:

    -
    pack build test-ruby-app --path ./ruby-sample-app --buildpack ./ruby-buildpack
    +
    pack build test-node-js-app --path ./node-js-sample-app --buildpack ./node-js-buildpack
     
    -

    You should then be able to run your new Ruby app:

    -
    docker run --rm -p 8080:8080 test-ruby-app
    +

    You should then be able to run your new NodeJS app:

    +
    docker run --rm -p 8080:8080 test-node-js-app
     

    and see the server log output:

    -
    [2019-04-02 18:04:48] INFO  WEBrick 1.4.2
    -[2019-04-02 18:04:48] INFO  ruby 2.5.1 (2018-03-29) [x86_64-linux]
    -== Sinatra (v2.0.5) has taken the stage on 8080 for development with backup from WEBrick
    -[2019-04-02 18:04:48] INFO  WEBrick::HTTPServer#start: pid=1 port=8080
    +
    Server running at http://0.0.0.0:8080/
     

    Test it out by navigating to localhost:8080 in your favorite browser!

    We can add multiple process types to a single app. We’ll do that in the next section.

    diff --git a/docs/buildpack-author-guide/create-buildpack/make-buildpack-configurable/index.html b/docs/buildpack-author-guide/create-buildpack/make-buildpack-configurable/index.html index 005ca2431..5c9cf3ca2 100644 --- a/docs/buildpack-author-guide/create-buildpack/make-buildpack-configurable/index.html +++ b/docs/buildpack-author-guide/create-buildpack/make-buildpack-configurable/index.html @@ -8,8 +8,8 @@ - + @@ -23,8 +23,8 @@ - + @@ -741,15 +741,15 @@

    Making your buildpack configurable

    -

    It’s likely that not all Ruby apps will want to use the same version of Ruby. Let’s make the Ruby version configurable.

    -

    Select Ruby version

    -

    We’ll allow buildpack users to define the desired Ruby version via a .ruby-version file in their app. We’ll first update the detect script to check for this file. We will then record the dependency we can provide (Ruby), as well as the specific dependency the application will require, in the Build Plan, a document the lifecycle uses to determine if the buildpack will provide everything the application needs.

    -

    Update ruby-buildpack/bin/detect to look like this:

    - +

    It’s likely that not all NodeJS apps will want to use the same version of NodeJS. Let’s make the NodeJS version configurable.

    +

    Select NodeJS version

    +

    We’ll allow buildpack users to define the desired NodeJS version via a .node-js-version file in their app. We’ll first update the detect script to check for this file. We will then record the dependency we can provide (NodeJS), as well as the specific dependency the application will require, in the Build Plan, a document the lifecycle uses to determine if the buildpack will provide everything the application needs.

    +

    Update node-js-buildpack/bin/detect to look like this:

    +
    #!/usr/bin/env bash
     set -eo pipefail
     
    -if [[ ! -f Gemfile ]]; then
    +if [[ ! -f package.json ]]; then
        exit 100
     fi
     
    @@ -757,20 +757,20 @@ 

    Select Ruby version

    plan=$2 version=3.1.3 -if [[ -f .ruby-version ]]; then - version=$(cat .ruby-version | tr -d '[:space:]') +if [[ -f .node-js-version ]]; then + version=$(cat .node-js-version | tr -d '[:space:]') fi -echo "provides = [{ name = \"ruby\" }]" > "$plan" -echo "requires = [{ name = \"ruby\", metadata = { version = \"$version\" } }]" >> "$plan" +echo "provides = [{ name = \"node-js\" }]" > "$plan" +echo "requires = [{ name = \"node-js\", metadata = { version = \"$version\" } }]" >> "$plan" # ======= /ADDED ======= -

    Then you will need to update your build script to look for the recorded Ruby version in the build plan:

    -

    Your ruby-buildpack/bin/build script should look like the following:

    - +

    Then you will need to update your build script to look for the recorded NodeJS version in the build plan:

    +

    Your node-js-buildpack/bin/build script should look like the following:

    +
    #!/usr/bin/env bash
     set -eo pipefail
     
    -echo "---> Ruby Buildpack"
    +echo "---> NodeJS Buildpack"
     
     # ======= MODIFIED =======
     # 1. GET ARGS
    @@ -778,74 +778,52 @@ 

    Select Ruby version

    plan=$3 # 2. CREATE THE LAYER DIRECTORY -rubylayer="$layersdir"/ruby -mkdir -p "$rubylayer" +node_js_layer="${layersdir}"/node-js +mkdir -p "${node_js_layer}" # ======= MODIFIED ======= -# 3. DOWNLOAD RUBY -ruby_version=$(cat "$plan" | yj -t | jq -r '.entries[] | select(.name == "ruby") | .metadata.version') -echo "---> Downloading and extracting Ruby $ruby_version" -ruby_url=https://s3-external-1.amazonaws.com/heroku-buildpack-ruby/heroku-22/ruby-$ruby_version.tgz -wget -q -O - "$ruby_url" | tar -xzf - -C "$rubylayer" - -# 4. MAKE RUBY AVAILABLE DURING LAUNCH -echo -e '[types]\nlaunch = true' > "$layersdir/ruby.toml" - -# 5. MAKE RUBY AVAILABLE TO THIS SCRIPT -export PATH="$rubylayer"/bin:$PATH -export LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}"$rubylayer/lib" - -# 6. INSTALL GEMS -# Compares previous Gemfile.lock checksum to the current Gemfile.lock -bundlerlayer="$layersdir/bundler" -local_bundler_checksum=$((sha256sum Gemfile.lock || echo 'DOES_NOT_EXIST') | cut -d ' ' -f 1) -remote_bundler_checksum=$(cat "$layersdir/bundler.toml" | yj -t | jq -r .metadata.checksum 2>/dev/null || echo 'DOES_NOT_EXIST') -# Always set the types table so that we re-use the appropriate layers -echo -e '[types]\ncache = true\nlaunch = true' >> "$layersdir/bundler.toml" - -if [[ -f Gemfile.lock && $local_bundler_checksum == $remote_bundler_checksum ]] ; then - # Determine if no gem dependencies have changed, so it can reuse existing gems without running bundle install - echo "---> Reusing gems" - bundle config --local path "$bundlerlayer" >/dev/null - bundle config --local bin "$bundlerlayer/bin" >/dev/null +# 3. DOWNLOAD node-js +default_node_js_version="18.18.1" +node_js_version=$(cat "$plan" | yj -t | jq -r '.entries[] | select(.name == "node-js") | .metadata.version' || echo ${default_node_js_version}) +node_js_url=https://nodejs.org/dist/v${node_js_version}/node-v${node_js_version}-linux-x64.tar.xz +remote_nodejs_version=$(cat "${layersdir}/node-js.toml" 2> /dev/null | yj -t | jq -r .metadata.nodejs_version 2>/dev/null || echo 'NOT FOUND') +if [[ "${node_js_url}" != *"${remote_nodejs_version}"* ]] ; then + echo "-----> Downloading and extracting NodeJS" ${node_js_version} + wget -q -O - "${node_js_url}" | tar -xJf - --strip-components 1 -C "${node_js_layer}" else - # Determine if there has been a gem dependency change and install new gems to the bundler layer; re-using existing and un-changed gems - echo "---> Installing gems" - mkdir -p "$bundlerlayer" - cat >> "$layersdir/bundler.toml" << EOL + echo "-----> Reusing NodeJS" +fi + +# 4. MAKE node-js AVAILABLE DURING LAUNCH and CACHE the LAYER + cat > "${layersdir}/node-js.toml" << EOL +[types] +cache = true +launch = true [metadata] -checksum = "$local_bundler_checksum" +nodejs_version = "${node_js_version}" EOL - bundle config set --local path "$bundlerlayer" && bundle install && bundle binstubs --all --path "$bundlerlayer/bin" - -fi -# 7. SET DEFAULT START COMMAND -cat > "$layersdir/launch.toml" << EOL -# our web process +# ========== ADDED =========== +# 5. SET DEFAULT START COMMAND +cat >> "${layersdir}/launch.toml" << EOL [[processes]] type = "web" -command = "bundle exec ruby app.rb" +command = "node app.js" default = true - -# our worker process -[[processes]] -type = "worker" -command = "bundle exec ruby worker.rb" EOL -

    Finally, create a file ruby-sample-app/.ruby-version with the following contents:

    - -
    3.1.0
    -

    Now when you run:

    +

    Finally, create a file node-js-sample-app/.node-js-version with the following contents:

    + +
    18.18.1
    +

    In the following pack invocation we choose to --clear-cache so that we explicitly do not re-use cached layers. This helps us demonstrate that the NodeJS runtime layer does not get restored from a cache.

    -
    pack build test-ruby-app --path ./ruby-sample-app --buildpack ./ruby-buildpack
    +
    pack build test-node-js-app --clear-cache --path ./node-js-sample-app --buildpack ./node-js-buildpack
     
    -

    You will notice that version of Ruby specified in the app’s .ruby-version file is downloaded.

    +

    You will notice that version of NodeJS specified in the app’s .node-js-version file is downloaded.

    ===> BUILDING
     ...
    ----> Ruby Buildpack
    ----> Downloading and extracting Ruby 3.1.0
    +---> NodeJS Buildpack
    +-----> Downloading and extracting NodeJS 18.18.1
     

    Next, let’s see how buildpacks can store information about the dependencies provided in the output app image for introspection.


    diff --git a/docs/buildpack-author-guide/create-buildpack/setup-local-environment/index.html b/docs/buildpack-author-guide/create-buildpack/setup-local-environment/index.html index 2910e5c9d..82090a04d 100644 --- a/docs/buildpack-author-guide/create-buildpack/setup-local-environment/index.html +++ b/docs/buildpack-author-guide/create-buildpack/setup-local-environment/index.html @@ -9,7 +9,7 @@ +mkdir node-js-sample-app Create a file in the current directory called node-js-sample-app/app.js with the…"/> @@ -24,7 +24,7 @@ +mkdir node-js-sample-app Create a file in the current directory called node-js-sample-app/app.js with the…"> @@ -744,38 +744,45 @@

    Set up your local environment

    First, we’ll create a sample Ruby app that you can use when developing your buildpack:

    -
    mkdir ruby-sample-app
    +
    mkdir node-js-sample-app
     
    -

    Create a file in the current directory called ruby-sample-app/app.rb with the following contents:

    - -
    require 'sinatra'
    -
    -set :bind, '0.0.0.0'
    -set :port, 8080
    -
    -get '/' do
    -  'Hello World!'
    -end
    -

    Then, create a file called ruby-sample-app/Gemfile with the following contents:

    - -
    source 'http://rubygems.org'
    -
    -git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
    -
    -gem 'sinatra'
    -gem 'webrick'
    +

    Create a file in the current directory called node-js-sample-app/app.js with the following contents:

    + +
    const http = require('http');
    + 
    +const hostname = '0.0.0.0';
    +const port = 8080;
    + 
    +const server = http.createServer((req, res) => {
    +  res.statusCode = 200;
    +  res.setHeader('Content-Type', 'text/plain');
    +  res.end('Hello World!');
    +});
    + 
    +// For demo purposes we do not actually start the server.  This
    +// allows us pretend to start the server and check if the output
    +// message is correct.
    +//server.listen(port, hostname, () => {
    +//  console.log(`Server running at http://${hostname}:${port}/`);
    +//});
    +console.log(`Server running at http://${hostname}:${port}/`)
    +

    We also create a package.json file with the following contents:

    + +
    {
    +  name = "example-application"
    +}
     

    Finally, make sure your local Docker daemon is running by executing:

    docker version
    diff --git a/docs/buildpack-author-guide/create-buildpack/specify-multiple-process-types/index.html b/docs/buildpack-author-guide/create-buildpack/specify-multiple-process-types/index.html
    index da09165b2..6fcf19db4 100644
    --- a/docs/buildpack-author-guide/create-buildpack/specify-multiple-process-types/index.html
    +++ b/docs/buildpack-author-guide/create-buildpack/specify-multiple-process-types/index.html
    @@ -8,7 +8,7 @@
     
     
     
    -
    +
     
     
     
    @@ -22,7 +22,7 @@
     
     
     
    -
    +
     
     
     
    @@ -739,90 +739,74 @@ 

    Specify multiple process types

    -

    One of the benefits of buildpacks is that they are multi-process - an image can have multiple entrypoints for each operational mode. Let’s see how this works. We will extend our app to have a worker process.

    -

    Let’s create a worker file, ruby-sample-app/worker.rb, with the following contents:

    - -
    for i in 0..5
    -    puts "Running a worker task..."
    -end
    -

    After building our app, we could run the resulting image with the web process (currently the default) or our new worker process.

    -

    To enable running the worker process, we’ll need to have our buildpack define a “process type” for the worker. Modify the section where processes are defined to:

    +

    One of the benefits of buildpacks is that they are multi-process - an image can have multiple entrypoints for each operational mode. Let’s see how this works. We will extend our app to have an entrypoint that allows a debugger to attach to it.

    +

    To enable running the debug process, we’ll need to have our buildpack define a “process type” for the worker. Modify the section where processes are defined to:

    # ...
     
    -cat > "$layersdir/launch.toml" << EOL
    +cat > "${layersdir}/launch.toml" << EOL
     # our web process
     [[processes]]
     type = "web"
    -command = "bundle exec ruby app.rb"
    +command = "node app.js"
     default = true
     
    -# our worker process
    +# our debug process
     [[processes]]
    -type = "worker"
    -command = "bundle exec ruby worker.rb"
    +type = "debug"
    +command = "node --inspect app.js"
     EOL
     
     # ...
    -

    Your full ruby-buildpack/bin/build script should now look like the following:

    - +

    Your full node-js-buildpack/bin/build script should now look like the following:

    +
    #!/usr/bin/env bash
     set -eo pipefail
     
    -echo "---> Ruby Buildpack"
    +echo "---> NodeJS Buildpack"
     
     # 1. GET ARGS
     layersdir=$1
     
     # 2. CREATE THE LAYER DIRECTORY
    -rubylayer="$layersdir"/ruby
    -mkdir -p "$rubylayer"
    +node_js_layer="${layersdir}"/node-js
    +mkdir -p "${node_js_layer}"
     
    -# 3. DOWNLOAD RUBY
    -echo "---> Downloading and extracting Ruby"
    -ruby_url=https://s3-external-1.amazonaws.com/heroku-buildpack-ruby/heroku-22/ruby-3.1.3.tgz
    -wget -q -O - "$ruby_url" | tar -xzf - -C "$rubylayer"
    +# 3. DOWNLOAD node-js
    +echo "---> Downloading and extracting NodeJS"
    +node_js_url=https://nodejs.org/dist/v18.18.1/node-v18.18.1-linux-x64.tar.xz
    +wget -q -O - "$node_js_url" | tar -xJf - --strip-components 1 -C "${node_js_layer}"
     
    -# 4. MAKE RUBY AVAILABLE DURING LAUNCH
    -echo -e '[types]\nlaunch = true' > "$layersdir/ruby.toml"
    -
    -# 5. MAKE RUBY AVAILABLE TO THIS SCRIPT
    -export PATH="$rubylayer"/bin:$PATH
    -export LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}"$rubylayer/lib"
    -
    -# 6. INSTALL GEMS
    -echo "---> Installing gems"
    -bundle install
    +# 4. MAKE node-js AVAILABLE DURING LAUNCH
    +    cat > "${layersdir}/node-js.toml" << EOL
    +[types]
    +launch = true
    +EOL
     
     # ========== MODIFIED ===========
    -# 7. SET DEFAULT START COMMAND
    -cat > "$layersdir/launch.toml" << EOL
    +# 5. SET DEFAULT START COMMAND
    +cat > "${layersdir}/launch.toml" << EOL
     # our web process
     [[processes]]
     type = "web"
    -command = "bundle exec ruby app.rb"
    +command = "node app.js"
     default = true
     
    -# our worker process
    +# our debug process
     [[processes]]
    -type = "worker"
    -command = "bundle exec ruby worker.rb"
    +type = "debug"
    +command = "node --inspect app.js"
     EOL
     

    Now if you rebuild your app using the updated buildpack:

    -
    pack build test-ruby-app --path ./ruby-sample-app --buildpack ./ruby-buildpack
    +
    pack build test-node-js-app --path ./node-js-sample-app --buildpack ./node-js-buildpack
     
    -

    You should then be able to run your new Ruby worker process:

    +

    You should then be able to run your new NodeJS debug process:

    -
    docker run --rm --entrypoint worker test-ruby-app
    +
    docker run --rm --entrypoint debug test-node-js-app
     
    -

    and see the worker log output:

    +

    and see the debug log output:

    -
    Running a worker task...
    -Running a worker task...
    -Running a worker task...
    -Running a worker task...
    -Running a worker task...
    -Running a worker task...
    +
    Debugger listening on ws://127.0.0.1:9229/
     

    Next, we’ll look at how to improve our buildpack by leveraging cache.


    diff --git a/index.xml b/index.xml index c63f7af47..27afd1464 100644 --- a/index.xml +++ b/index.xml @@ -347,9 +347,8 @@ Operating system name (e.g., &ldquo;linux&rdquo;) Architecture (e.g., &a Mon, 01 Jan 0001 00:00:00 +0000 https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/setup-local-environment/ First, we&rsquo;ll create a sample Ruby app that you can use when developing your buildpack: -mkdir ruby-sample-app Create a file in the current directory called ruby-sample-app/app.rb with the following contents: -require &#39;sinatra&#39; set :bind, &#39;0.0.0.0&#39; set :port, 8080 get &#39;/&#39; do &#39;Hello World!&#39; end Then, create a file called ruby-sample-app/Gemfile with the following contents: -source &#39;http://rubygems.org&#39; git_source(:github) {|repo_name| &#34;https://github.com/#{repo_name}&#34; } gem &#39;sinatra&#39; gem &#39;webrick&#39; Finally, make sure your local Docker daemon is running by executing: +mkdir node-js-sample-app Create a file in the current directory called node-js-sample-app/app.js with the following contents: +const http = require(&#39;http&#39;); const hostname = &#39;0.0.0.0&#39;; const port = 8080; const server = http.createServer((req, res) =&gt; { res.statusCode = 200; res.setHeader(&#39;Content-Type&#39;, &#39;text/plain&#39;); res.end(&#39;Hello World!&#39;); }); // For demo purposes we do not actually start the server. This // allows us pretend to start the server and check if the output // message is correct. Set up your local environment @@ -369,7 +368,7 @@ Client: Docker Engine - Community Version: 20.10.9 API version: 1. Let&rsquo;s create the directory where your buildpack will live: Using the Pack CLI The buildpack new &lt;id&gt; command will create a directory named for the buildpack ID. Example: -pack buildpack new examples/ruby \ --api 0.8 \ --path ruby-buildpack \ --version 0.0.1 \ --stacks io.buildpacks.samples.stacks.jammy This command will create ruby-buildpack directory which contains buildpack.toml, bin/build, bin/detect files. +pack buildpack new examples/node-js \ --api 0.8 \ --path node-js-buildpack \ --version 0.0.1 \ --stacks io.buildpacks.samples.stacks.jammy This command will create node-js-buildpack directory which contains buildpack.toml, bin/build, bin/detect files. Additional Parameters -a, --api Buildpack API compatibility of the generated buildpack -h, --help Help for &rsquo;new' --path the location on the filesystem to generate the artifacts --stacks Stacks (deprecated) the buildpack will work with -V, --version the version of the buildpack in buildpack. @@ -395,10 +394,10 @@ build cat $PWD/samples/buildpacks/hello-extensions/bin/build The buildpack tries https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/detection/ Mon, 01 Jan 0001 00:00:00 +0000 https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/detection/ - Next, you will want to actually detect that the app you are building is a Ruby app. In order to do this, you will need to check for a Gemfile. + Next, you will want to actually detect that the app you are building is a node-js app. In order to do this, you will need to check for a package.json. Replace exit 1 in the detect script with the following check: -if [[ ! -f Gemfile ]]; then exit 100 fi Your ruby-buildpack/bin/detect script should look like this: -#!/usr/bin/env bash set -eo pipefail if [[ ! -f Gemfile ]]; then exit 100 fi Next, rebuild your app with the updated buildpack: +if [[ ! -f package.json ]]; then exit 100 fi Your node-js-buildpack/bin/detect script should look like this: +#!/usr/bin/env bash set -eo pipefail if [[ ! -f package.json ]]; then exit 100 fi Next, rebuild your app with the updated buildpack: Building your application @@ -406,7 +405,7 @@ if [[ ! -f Gemfile ]]; then exit 100 fi Your ruby-buildpack/bin/detect script sh Mon, 01 Jan 0001 00:00:00 +0000 https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/build-app/ Now we&rsquo;ll change the build step you created to install application dependencies. This will require updates to the build script such that it performs the following steps: -Creates a layer for the Ruby runtime Downloads the Ruby runtime and installs it to the layer Installs Bundler (the Ruby dependency manager) Uses Bundler to install dependencies By doing this, you&rsquo;ll learn how to create arbitrary layers with your buildpack, and how to read the contents of the app in order to perform actions like downloading dependencies. +Create a layer for the NodeJS runtime Download the NodeJS runtime and installs it to the layer By doing this, you&rsquo;ll learn how to create arbitrary layers with your buildpack, and how to read the contents of the app in order to perform actions like downloading dependencies. Generating a build.Dockerfile @@ -432,8 +431,8 @@ Examine curl extension detect cat $PWD/samples/extensions/curl/bin/detect The ex Mon, 01 Jan 0001 00:00:00 +0000 https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/make-app-runnable/ To make your app runnable, a default start command must be set. You&rsquo;ll need to add the following to the end of your build script: -# ... # Set default start command cat &gt; &#34;$layersdir/launch.toml&#34; &lt;&lt; EOL [[processes]] type = &#34;web&#34; command = &#34;bundle exec ruby app.rb&#34; default = true EOL # ... Your full ruby-buildpack/bin/build script should now look like the following: -#!/usr/bin/env bash set -eo pipefail echo &#34;---&gt; Ruby Buildpack&#34; # 1. +# ... # Set default start command cat &gt; &#34;${layersdir}/launch.toml&#34; &lt;&lt; EOL [[processes]] type = &#34;web&#34; command = &#34;node app.js&#34; default = true EOL # ... Your full node-js-buildpack/bin/build script should now look like the following: +#!/usr/bin/env bash set -eo pipefail echo &#34;---&gt; NodeJS Buildpack&#34; # 1. Specifying an Image Extension at Build Time @@ -466,9 +465,8 @@ Configure the hello-extensions buildpack to require cowsay Set the BP_REQUIRES b https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/specify-multiple-process-types/ Mon, 01 Jan 0001 00:00:00 +0000 https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/specify-multiple-process-types/ - One of the benefits of buildpacks is that they are multi-process - an image can have multiple entrypoints for each operational mode. Let&rsquo;s see how this works. We will extend our app to have a worker process. -Let&rsquo;s create a worker file, ruby-sample-app/worker.rb, with the following contents: -for i in 0..5 puts &#34;Running a worker task...&#34; end After building our app, we could run the resulting image with the web process (currently the default) or our new worker process. + One of the benefits of buildpacks is that they are multi-process - an image can have multiple entrypoints for each operational mode. Let&rsquo;s see how this works. We will extend our app to have an entrypoint that allows a debugger to attach to it. +To enable running the debug process, we&rsquo;ll need to have our buildpack define a &ldquo;process type&rdquo; for the worker. Modify the section where processes are defined to: The finer points of image extensions @@ -482,18 +480,18 @@ for i in 0..5 puts &#34;Running a worker task...&#34; end After building https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/caching/ Mon, 01 Jan 0001 00:00:00 +0000 https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/caching/ - We can improve performance by caching dependencies between builds, only re-downloading when necessary. To begin, let&rsquo;s create a cacheable bundler layer. -Creating the bundler layer To do this, replace the following lines in the build script: -echo &#34;---&gt; Installing gems&#34; bundle install with the following: -echo &#34;---&gt; Installing gems&#34; bundlerlayer=&#34;$layersdir/bundler&#34; mkdir -p &#34;$bundlerlayer&#34; echo -e &#39;[types]\ncache = true\nlaunch = true&#39; &gt; &#34;$layersdir/bundler.toml&#34; bundle config set --local path &#34;$bundlerlayer&#34; &amp;&amp; bundle install &amp;&amp; bundle binstubs --all --path &#34;$bundlerlayer/bin&#34; Your full ruby-buildpack/bin/build script should now look like the following: + We can improve performance by caching the runtime between builds, only re-downloading when necessary. To begin, let&rsquo;s cache the runtime layer. +Cache the runtime layer To do this, replace the following lines in the build script: +# 4. MAKE node-js AVAILABLE DURING LAUNCH echo -e &#39;[types]\nlaunch = true&#39; &gt; &#34;${layersdir}/node-js.toml&#34; with the following: +# 4. MAKE node-js AVAILABLE DURING LAUNCH and CACHE it echo -e &#39;[types]\ncache = true\nlaunch = true&#39; &gt; &#34;${layersdir}/node-js. Making your buildpack configurable https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/make-buildpack-configurable/ Mon, 01 Jan 0001 00:00:00 +0000 https://buildpacks.io/docs/buildpack-author-guide/create-buildpack/make-buildpack-configurable/ - It&rsquo;s likely that not all Ruby apps will want to use the same version of Ruby. Let&rsquo;s make the Ruby version configurable. -Select Ruby version We&rsquo;ll allow buildpack users to define the desired Ruby version via a .ruby-version file in their app. We&rsquo;ll first update the detect script to check for this file. We will then record the dependency we can provide (Ruby), as well as the specific dependency the application will require, in the Build Plan, a document the lifecycle uses to determine if the buildpack will provide everything the application needs. + It&rsquo;s likely that not all NodeJS apps will want to use the same version of NodeJS. Let&rsquo;s make the NodeJS version configurable. +Select NodeJS version We&rsquo;ll allow buildpack users to define the desired NodeJS version via a .node-js-version file in their app. We&rsquo;ll first update the detect script to check for this file. We will then record the dependency we can provide (NodeJS), as well as the specific dependency the application will require, in the Build Plan, a document the lifecycle uses to determine if the buildpack will provide everything the application needs. Adding Bill-of-Materials