Skip to content

Commit c80d73e

Browse files
authored
Merge branch 'main' into patch-1
2 parents b85fb97 + 6b2d630 commit c80d73e

File tree

33 files changed

+1103
-3506
lines changed

33 files changed

+1103
-3506
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ jobs:
2424
- name: Install Dependencies
2525
run: sudo apt-get install make curl jq
2626
- name: Install pack
27-
uses: buildpacks/github-actions/setup-pack@v5.4.0
27+
uses: buildpacks/github-actions/setup-pack@v5.5.1
2828
with:
29-
pack-version: '0.30.0-rc1' # FIXME: update to 0.30.0 when available
29+
pack-version: '0.31.0'
3030
- name: Test
3131
run: make test
3232
env:

content/docs/app-developer-guide/run-an-app.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Base Image:
4848
Top Layer: sha256:700c764e7c5d5c75e6a0fc7d272b7e1c70ab327c03fbdf4abd9313e5ec3217f7
4949
5050
Run Images:
51-
cnbs/sample-stack-run:alpine
51+
cnbs/sample-base-run:alpine
5252
5353
Rebasable: true
5454

content/docs/buildpack-author-guide/create-buildpack/adding-bill-of-materials.md

Lines changed: 63 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -15,229 +15,184 @@ One of the benefits of buildpacks is they can also populate the app image with m
1515

1616
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`.
1717

18-
<!-- test:exec -->
1918
```bash
20-
pack inspect-image test-ruby-app
19+
pack inspect-image test-node-js-app
2120
```
22-
<!--+- "{{execute}}"+-->
2321
You should see the following:
2422

25-
<!-- test:assert=contains;ignore-lines=... -->
2623
```text
2724
Run Images:
28-
cnbs/sample-stack-run:jammy
25+
cnbs/sample-base-run:jammy
2926
...
3027
3128
Buildpacks:
3229
ID VERSION HOMEPAGE
33-
examples/ruby 0.0.1 -
30+
examples/node-js 0.0.1 -
3431
3532
Processes:
3633
TYPE SHELL COMMAND ARGS WORK DIR
37-
web (default) bash bundle exec ruby app.rb /workspace
38-
worker bash bundle exec ruby worker.rb /workspace
34+
web (default) bash node-js app.js /workspace
3935
```
4036

41-
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.
37+
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.
4238

43-
To add the `ruby` version to the output of `pack download sbom`, we will have to provide a [Software `Bill-of-Materials`](https://en.wikipedia.org/wiki/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](https://cyclonedx.org/), [SPDX](https://spdx.dev/) or [Syft](https://github.com/anchore/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`](https://github.com/anchore/syft)) you might choose to use. In this example we will use the CycloneDX json format.
39+
To add the `node-js` version to the output of `pack download sbom`, we will have to provide a [Software `Bill-of-Materials`](https://en.wikipedia.org/wiki/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](https://cyclonedx.org/), [SPDX](https://spdx.dev/) or [Syft](https://github.com/anchore/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`](https://github.com/anchore/syft)) you might choose to use. In this example we will use the CycloneDX json format.
4440

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

47-
<!-- test:file=ruby-buildpack/buildpack.toml -->
43+
<!-- test:file=node-js-buildpack/buildpack.toml -->
4844
```toml
4945
# Buildpack API version
5046
api = "0.8"
5147

5248
# Buildpack ID and metadata
5349
[buildpack]
54-
id = "examples/ruby"
50+
id = "examples/node-js"
5551
version = "0.0.1"
5652
sbom-formats = [ "application/vnd.cyclonedx+json" ]
5753

58-
# Stacks that the buildpack will work with
54+
# Targets the buildpack will work with
55+
[[targets]]
56+
os = "linux"
57+
58+
# Stacks (deprecated) the buildpack will work with
5959
[[stacks]]
60-
id = "io.buildpacks.samples.stacks.jammy"
60+
id = "*"
6161
```
6262

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

6565
```bash
6666
# ...
6767

68-
# Append a Bill-of-Materials containing metadata about the provided ruby version
69-
cat >> "$layersdir/ruby.sbom.cdx.json" << EOL
68+
# Append a Bill-of-Materials containing metadata about the provided node-js version
69+
cat >> "${layersdir}/node-js.sbom.cdx.json" << EOL
7070
{
7171
"bomFormat": "CycloneDX",
7272
"specVersion": "1.4",
7373
"version": 1,
7474
"components": [
7575
{
7676
"type": "library",
77-
"name": "ruby",
78-
"version": "$ruby_version"
77+
"name": "node-js",
78+
"version": "${node_js_version}"
7979
}
8080
]
8181
}
8282
EOL
8383
```
8484

85-
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`:
85+
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`:
8686

8787
```bash
88-
crubybom="${layersdir}/ruby.sbom.cdx.json"
89-
cat >> ${rubybom} << EOL
88+
node-jsbom="${layersdir}/node-js.sbom.cdx.json"
89+
cat >> ${node-jsbom} << EOL
9090
{
9191
"bomFormat": "CycloneDX",
9292
"specVersion": "1.4",
9393
"version": 1,
9494
"components": [
9595
{
9696
"type": "library",
97-
"name": "ruby",
98-
"version": "$ruby_version"
97+
"name": "node-js",
98+
"version": "${node_js_version}"
9999
}
100100
]
101101
}
102102
EOL
103-
if [[ -f Gemfile.lock ]] ; then
104-
for gem in $(gem dep -q | grep ^Gem | sed 's/^Gem //')
105-
do
106-
version=${gem##*-}
107-
name=${gem%-${version}}
108-
DEP=$(jq --arg name "${name}" --arg version "${version}" \
109-
'.components[.components| length] |= . + {"type": "library", "name": $name, "version": $version}' \
110-
"${rubybom}")
111-
echo ${DEP} > "${rubybom}"
112-
done
113-
fi
114103
```
115104

116-
Your `ruby-buildpack/bin/build`<!--+"{{open}}"+--> script should look like the following:
105+
Your `node-js-buildpack/bin/build`<!--+"{{open}}"+--> script should look like the following:
117106

118-
<!-- test:file=ruby-buildpack/bin/build -->
107+
<!-- test:file=node-js-buildpack/bin/build -->
119108
```bash
120109
#!/usr/bin/env bash
121110
set -eo pipefail
122111

123-
echo "---> Ruby Buildpack"
112+
echo "---> NodeJS Buildpack"
124113

114+
# ======= MODIFIED =======
125115
# 1. GET ARGS
126116
layersdir=$1
127117
plan=$3
128118

129119
# 2. CREATE THE LAYER DIRECTORY
130-
rubylayer="$layersdir"/ruby
131-
mkdir -p "$rubylayer"
132-
133-
# 3. DOWNLOAD RUBY
134-
ruby_version=$(cat "$plan" | yj -t | jq -r '.entries[] | select(.name == "ruby") | .metadata.version')
135-
echo "---> Downloading and extracting Ruby $ruby_version"
136-
ruby_url=https://s3-external-1.amazonaws.com/heroku-buildpack-ruby/heroku-22/ruby-$ruby_version.tgz
137-
wget -q -O - "$ruby_url" | tar -xzf - -C "$rubylayer"
138-
139-
# 4. MAKE RUBY AVAILABLE DURING LAUNCH
140-
echo -e '[types]\nlaunch = true' > "$layersdir/ruby.toml"
141-
142-
# 5. MAKE RUBY AVAILABLE TO THIS SCRIPT
143-
export PATH="$rubylayer"/bin:$PATH
144-
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}"$rubylayer/lib"
145-
146-
# 6. INSTALL GEMS
147-
# Compares previous Gemfile.lock checksum to the current Gemfile.lock
148-
bundlerlayer="$layersdir/bundler"
149-
local_bundler_checksum=$((sha256sum Gemfile.lock || echo 'DOES_NOT_EXIST') | cut -d ' ' -f 1)
150-
remote_bundler_checksum=$(cat "$layersdir/bundler.toml" | yj -t | jq -r .metadata.checksum 2>/dev/null || echo 'DOES_NOT_EXIST')
151-
# Always set the types table so that we re-use the appropriate layers
152-
echo -e '[types]\ncache = true\nlaunch = true' >> "$layersdir/bundler.toml"
153-
154-
if [[ -f Gemfile.lock && $local_bundler_checksum == $remote_bundler_checksum ]] ; then
155-
# Determine if no gem dependencies have changed, so it can reuse existing gems without running bundle install
156-
echo "---> Reusing gems"
157-
bundle config --local path "$bundlerlayer" >/dev/null
158-
bundle config --local bin "$bundlerlayer/bin" >/dev/null
120+
node_js_layer="${layersdir}"/node-js
121+
mkdir -p "${node_js_layer}"
122+
123+
# 3. DOWNLOAD node-js
124+
default_node_js_version="18.18.1"
125+
node_js_version=$(cat "$plan" | yj -t | jq -r '.entries[] | select(.name == "node-js") | .metadata.version' || echo ${default_node_js_version})
126+
node_js_url=https://nodejs.org/dist/v${node_js_version}/node-v${node_js_version}-linux-x64.tar.xz
127+
remote_nodejs_version=$(cat "${layersdir}/node-js.toml" 2> /dev/null | yj -t | jq -r .metadata.nodejs_version 2>/dev/null || echo 'NOT FOUND')
128+
if [[ "${node_js_url}" != *"${remote_nodejs_version}"* ]] ; then
129+
echo "-----> Downloading and extracting NodeJS"
130+
wget -q -O - "${node_js_url}" | tar -xJf - --strip-components 1 -C "${node_js_layer}"
159131
else
160-
# Determine if there has been a gem dependency change and install new gems to the bundler layer; re-using existing and un-changed gems
161-
echo "---> Installing gems"
162-
mkdir -p "$bundlerlayer"
163-
cat >> "$layersdir/bundler.toml" << EOL
132+
echo "-----> Reusing NodeJS"
133+
fi
134+
135+
# 4. MAKE node-js AVAILABLE DURING LAUNCH and CACHE the LAYER
136+
cat > "${layersdir}/node-js.toml" << EOL
137+
[types]
138+
cache = true
139+
launch = true
164140
[metadata]
165-
checksum = "$local_bundler_checksum"
141+
nodejs_version = "${node_js_version}"
166142
EOL
167-
bundle config set --local path "$bundlerlayer" && bundle install && bundle binstubs --all --path "$bundlerlayer/bin"
168143

169-
fi
170-
171-
# 7. SET DEFAULT START COMMAND
172-
cat > "$layersdir/launch.toml" << EOL
173-
# our web process
144+
# 5. SET DEFAULT START COMMAND
145+
cat >> "${layersdir}/launch.toml" << EOL
174146
[[processes]]
175147
type = "web"
176-
command = "bundle exec ruby app.rb"
148+
command = "node app.js"
177149
default = true
178-
179-
# our worker process
180-
[[processes]]
181-
type = "worker"
182-
command = "bundle exec ruby worker.rb"
183150
EOL
184151

185152
# ========== ADDED ===========
186-
# 8. ADD A SBOM
187-
rubybom="${layersdir}/ruby.sbom.cdx.json"
188-
cat >> ${rubybom} << EOL
153+
# 6. ADD A SBOM
154+
node_jsbom="${layersdir}/node-js.sbom.cdx.json"
155+
cat >> ${node_jsbom} << EOL
189156
{
190157
"bomFormat": "CycloneDX",
191158
"specVersion": "1.4",
192159
"version": 1,
193160
"components": [
194161
{
195162
"type": "library",
196-
"name": "ruby",
197-
"version": "$ruby_version"
163+
"name": "node-js",
164+
"version": "${node_js_version}"
198165
}
199166
]
200167
}
201168
EOL
202-
if [[ -f Gemfile.lock ]] ; then
203-
for gem in $(gem dep -q | grep ^Gem | sed 's/^Gem //')
204-
do
205-
version=${gem##*-}
206-
name=${gem%-${version}}
207-
DEP=$(jq --arg name "${name}" --arg version "${version}" \
208-
'.components[.components| length] |= . + {"type": "library", "name": $name, "version": $version}' \
209-
"${rubybom}")
210-
echo ${DEP} > "${rubybom}"
211-
done
212-
fi
213169
```
214170

215171
Then rebuild your app using the updated buildpack:
216172

217173
<!-- test:exec -->
218174
```bash
219-
pack build test-ruby-app --path ./ruby-sample-app --buildpack ./ruby-buildpack
175+
pack build test-node-js-app --path ./node-js-sample-app --buildpack ./node-js-buildpack
220176
```
221177
<!--+- "{{execute}}"+-->
222178

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

225181
<!-- test:exec -->
226182
```bash
227-
pack sbom download test-ruby-app
183+
pack sbom download test-node-js-app
228184
```
229185
<!--+- "{{execute}}"+-->
230186

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

233189
<!-- test:exec -->
234190
```bash
235-
cat layers/sbom/launch/examples_ruby/ruby/sbom.cdx.json | jq -M
191+
cat layers/sbom/launch/examples_node-js/node-js/sbom.cdx.json | jq -M
236192
```
237193

238-
You should find that the included `ruby` version is `3.1.0` as expected.
194+
You should find that the included `node-js` version is `18.18.1` as expected.
239195

240-
<!-- test:assert=contains;ignore-lines=... -->
241196
```text
242197
{
243198
"bomFormat": "CycloneDX",
@@ -246,9 +201,9 @@ You should find that the included `ruby` version is `3.1.0` as expected.
246201
"components": [
247202
{
248203
"type": "library",
249-
"name": "ruby",
250-
"version": "3.1.0"
251-
},
204+
"name": "node-js",
205+
"version": "18.18.1"
206+
}
252207
...
253208
]
254209
}
@@ -260,7 +215,7 @@ Congratulations! You’ve created your first configurable Cloud Native Buildpack
260215

261216
Now that you've finished your buildpack, how about extending it? Try:
262217

263-
- Caching the downloaded Ruby version
218+
- Caching the downloaded NodeJS version
264219
- [Packaging your buildpack for distribution][package-a-buildpack]
265220

266221
[package-a-buildpack]: /docs/buildpack-author-guide/package-a-buildpack/

0 commit comments

Comments
 (0)