Skip to content

Commit 52155ad

Browse files
committed
Update docs to API version 0.7
Use `pack sbom download` and API 0.7 to demonstrate a CycloneDX sbom in a buildpack. Signed-off-by: Aidan Delaney <[email protected]>
1 parent 7a49a3d commit 52155ad

File tree

6 files changed

+373
-54
lines changed

6 files changed

+373
-54
lines changed

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

Lines changed: 115 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ One of the benefits of buildpacks is they can also populate the app image with m
1212
* The buildpacks were used to create the app image
1313
* And more...!
1414

15-
You can find all of this information using `pack` via its `inspect-image` command.
15+
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`.
1616

1717
<!-- test:exec -->
1818
```bash
@@ -38,18 +38,77 @@ Processes:
3838

3939
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.
4040

41-
To add the `ruby` version to the output of `pack inspect-image`, we will have to provide a `Bill-of-Materials` (`BOM`) containing this information. You'll need to update the `launch.toml` created at the end of your `build` script:
41+
To add the `ruby` version to the output of `pack download sbom`, we will have to provide a `Bill-of-Materials` (`BOM`) containing this information. There are three "standard" ways to report SBOM data. You'll need to choose to use on of CycloneDX, SPDX or Syft update the `ruby.sbom.<ext>` (where `<ext>` is the extension appropriate for your BOM standard, one of `cdx.json`, `spdx.json` or `syft.json`) at the end of your `build` script. Discussion of which BOM 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 BOM scanner (eg: [`syft cli`](https://github.com/anchore/syft)) you might choose to use. In this example we will use the CycloneDX json format.
42+
43+
First, annotate the `buildpack.toml` to specify that it emits CycloneDX:
44+
45+
<!-- test:file=ruby-buildpack/buildpack.toml -->
46+
```toml
47+
# Buildpack API version
48+
api = "0.7"
49+
50+
# Buildpack ID and metadata
51+
[buildpack]
52+
id = "examples/ruby"
53+
version = "0.0.1"
54+
sbom-formats = [ "application/vnd.cyclonedx+json" ]
55+
56+
# Stacks that the buildpack will work with
57+
[[stacks]]
58+
id = "io.buildpacks.samples.stacks.bionic"
59+
```
60+
61+
Then, in our buildpack implemetnation we will generate the necessary BOM metadata:
4262

4363
```bash
4464
# ...
4565

4666
# Append a Bill-of-Materials containing metadata about the provided ruby version
47-
cat >> "$layersdir/launch.toml" << EOL
48-
[[bom]]
49-
name = "ruby"
50-
[bom.metadata]
51-
version = "$ruby_version"
67+
cat >> "$layersdir/ruby.sbom.cdx.json" << EOL
68+
{
69+
"bomFormat": "CycloneDX",
70+
"specVersion": "1.3",
71+
"version": 1,
72+
"components": [
73+
{
74+
"type": "library",
75+
"name": "ruby",
76+
"version": "$ruby_version"
77+
}
78+
]
79+
}
80+
EOL
81+
```
82+
83+
We can also add an BOM 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`:
84+
85+
```bash
86+
crubybom="${layersdir}/ruby.sbom.cdx.json"
87+
cat >> ${rubybom} << EOL
88+
{
89+
"bomFormat": "CycloneDX",
90+
"specVersion": "1.3",
91+
"version": 1,
92+
"components": [
93+
{
94+
"type": "library",
95+
"name": "ruby",
96+
"version": "$ruby_version"
97+
}
98+
]
99+
}
52100
EOL
101+
if [[ -f Gemfile.lock ]] ; then
102+
for gem in $(gem dep -q | grep ^Gem | sed 's/^Gem //')
103+
do
104+
version=${gem##*-}
105+
name=${gem%-${version}}
106+
DEP=$(jq --arg name "${name}" --arg version "${version}" \
107+
'.components[.components| length] |= . + {"type": "library", "name": $name, "version": $version}' \
108+
"${rubybom}")
109+
echo ${DEP} > "${rubybom}"
110+
done
111+
fi
53112
```
54113

55114
Your `ruby-buildpack/bin/build`<!--+"{{open}}"+--> script should look like the following:
@@ -129,13 +188,32 @@ EOL
129188
130189
# ========== ADDED ===========
131190
# 9. ADD A BOM
132-
cat >> "$layersdir/launch.toml" << EOL
133-
[[bom]]
134-
name = "ruby"
135-
[bom.metadata]
136-
version = "$ruby_version"
191+
rubybom="${layersdir}/ruby.sbom.cdx.json"
192+
cat >> ${rubybom} << EOL
193+
{
194+
"bomFormat": "CycloneDX",
195+
"specVersion": "1.3",
196+
"version": 1,
197+
"components": [
198+
{
199+
"type": "library",
200+
"name": "ruby",
201+
"version": "$ruby_version"
202+
}
203+
]
204+
}
137205
EOL
138-
206+
if [[ -f Gemfile.lock ]] ; then
207+
for gem in $(gem dep -q | grep ^Gem | sed 's/^Gem //')
208+
do
209+
version=${gem##*-}
210+
name=${gem%-${version}}
211+
DEP=$(jq --arg name "${name}" --arg version "${version}" \
212+
'.components[.components| length] |= . + {"type": "library", "name": $name, "version": $version}' \
213+
"${rubybom}")
214+
echo ${DEP} > "${rubybom}"
215+
done
216+
fi
139217
```
140218
141219
Then rebuild your app using the updated buildpack:
@@ -146,28 +224,41 @@ pack build test-ruby-app --path ./ruby-sample-app --buildpack ./ruby-buildpack
146224
```
147225
<!--+- "{{execute}}"+-->
148226
149-
You should then be able to inspect your Ruby app for its Bill-of-Materials via:
227+
Viewing your bill-of-materials requires first pushing the built image to a container registry and then querying the bill-of-materials. If you are a `docker` user, we can experiment with an OCI container registry by running an instance of Docker's `registry:2` (as an aside, the standard Docker daemon is a container registry, but is not fully [OCI compliant](https://opencontainers.org/)).
150228

151229
<!-- test:exec -->
152230
```bash
153-
pack inspect-image test-ruby-app --bom
231+
docker run -d -p 5000:5000 registry:2
232+
docker image tag test-ruby-app localhost:5000/test-ruby-app
233+
docker push localhost:5000/test-ruby-app
234+
pack sbom download localhost:5000/test-ruby-app
154235
```
155236
<!--+- "{{execute}}"+-->
156237

238+
The SBOM information is now downloaded to the local file system:
239+
240+
<!-- test:exec -->
241+
```bash
242+
cat layers/sbom/launch/examples_ruby/ruby/sbom.cdx.json | jq -M
243+
```
244+
157245
You should find that the included `ruby` version is `2.5.0` as expected.
158246

159-
<!-- test:assert=contains -->
247+
<!-- test:assert=contains;ignore-lines=... -->
160248
```text
249+
{
250+
"bomFormat": "CycloneDX",
251+
"specVersion": "1.3",
252+
"version": 1,
253+
"components": [
161254
{
255+
"type": "library",
162256
"name": "ruby",
163-
"metadata": {
164-
"version": "2.5.0"
165-
},
166-
"buildpacks": {
167-
"id": "examples/ruby",
168-
"version": "0.0.1"
169-
}
170-
}
257+
"version": "2.5.0"
258+
},
259+
...
260+
]
261+
}
171262
```
172263

173264
Congratulations! You’ve created your first configurable Cloud Native Buildpack that uses detection, image layers, and caching to create an introspectable and runnable OCI image.

content/docs/buildpack-author-guide/create-buildpack/building-blocks-cnb.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Example:
1717
<!-- test:exec -->
1818
```bash
1919
pack buildpack new examples/ruby \
20-
--api 0.6 \
20+
--api 0.7 \
2121
--path ruby-buildpack \
2222
--version 0.0.1 \
2323
--stacks io.buildpacks.samples.stacks.bionic
@@ -41,7 +41,7 @@ You will have `ruby-buildpack/buildpack.toml`<!--+"{{open}}"+--> in your buildpa
4141
<!-- test:file=ruby-buildpack/buildpack.toml -->
4242
```toml
4343
# Buildpack API version
44-
api = "0.6"
44+
api = "0.7"
4545

4646
# Buildpack ID and metadata
4747
[buildpack]

katacoda/scenarios/buildpack-author-guide/adding-bill-of-materials.md

Lines changed: 115 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ One of the benefits of buildpacks is they can also populate the app image with m
99
* The buildpacks were used to create the app image
1010
* And more...!
1111

12-
You can find all of this information using `pack` via its `inspect-image` command.
12+
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`.
1313

1414
<!-- test:exec -->
1515
```bash
@@ -34,18 +34,77 @@ Processes:
3434

3535
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.
3636

37-
To add the `ruby` version to the output of `pack inspect-image`, we will have to provide a `Bill-of-Materials` (`BOM`) containing this information. You'll need to update the `launch.toml` created at the end of your `build` script:
37+
To add the `ruby` version to the output of `pack download sbom`, we will have to provide a `Bill-of-Materials` (`BOM`) containing this information. There are three "standard" ways to report SBOM data. You'll need to choose to use on of CycloneDX, SPDX or Syft update the `ruby.sbom.<ext>` (where `<ext>` is the extension appropriate for your BOM standard, one of `cdx.json`, `spdx.json` or `syft.json`) at the end of your `build` script. Discussion of which BOM 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 BOM scanner (eg: [`syft cli`](https://github.com/anchore/syft)) you might choose to use. In this example we will use the CycloneDX json format.
38+
39+
First, annotate the `buildpack.toml` to specify that it emits CycloneDX:
40+
41+
<!-- test:file=ruby-buildpack/buildpack.toml -->
42+
<pre class="file" data-filename="ruby-buildpack/buildpack.toml" data-target="replace">
43+
# Buildpack API version
44+
api = "0.7"
45+
46+
# Buildpack ID and metadata
47+
[buildpack]
48+
id = "examples/ruby"
49+
version = "0.0.1"
50+
sbom-formats = [ "application/vnd.cyclonedx+json" ]
51+
52+
# Stacks that the buildpack will work with
53+
[[stacks]]
54+
id = "io.buildpacks.samples.stacks.bionic"
55+
</pre>
56+
57+
Then, in our buildpack implemetnation we will generate the necessary BOM metadata:
3858

3959
```bash
4060
# ...
4161

4262
# Append a Bill-of-Materials containing metadata about the provided ruby version
43-
cat >> "$layersdir/launch.toml" << EOL
44-
[[bom]]
45-
name = "ruby"
46-
[bom.metadata]
47-
version = "$ruby_version"
63+
cat >> "$layersdir/ruby.sbom.cdx.json" << EOL
64+
{
65+
"bomFormat": "CycloneDX",
66+
"specVersion": "1.3",
67+
"version": 1,
68+
"components": [
69+
{
70+
"type": "library",
71+
"name": "ruby",
72+
"version": "$ruby_version"
73+
}
74+
]
75+
}
76+
EOL
77+
```
78+
79+
We can also add an BOM 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`:
80+
81+
```bash
82+
crubybom="${layersdir}/ruby.sbom.cdx.json"
83+
cat >> ${rubybom} << EOL
84+
{
85+
"bomFormat": "CycloneDX",
86+
"specVersion": "1.3",
87+
"version": 1,
88+
"components": [
89+
{
90+
"type": "library",
91+
"name": "ruby",
92+
"version": "$ruby_version"
93+
}
94+
]
95+
}
4896
EOL
97+
if [[ -f Gemfile.lock ]] ; then
98+
for gem in $(gem dep -q | grep ^Gem | sed 's/^Gem //')
99+
do
100+
version=${gem##*-}
101+
name=${gem%-${version}}
102+
DEP=$(jq --arg name "${name}" --arg version "${version}" \
103+
'.components[.components| length] |= . + {"type": "library", "name": $name, "version": $version}' \
104+
"${rubybom}")
105+
echo ${DEP} > "${rubybom}"
106+
done
107+
fi
49108
```
50109

51110
Your `ruby-buildpack/bin/build`{{open}} script should look like the following:
@@ -125,13 +184,32 @@ EOL
125184

126185
# ========== ADDED ===========
127186
# 9. ADD A BOM
128-
cat >> "$layersdir/launch.toml" << EOL
129-
[[bom]]
130-
name = "ruby"
131-
[bom.metadata]
132-
version = "$ruby_version"
187+
rubybom="${layersdir}/ruby.sbom.cdx.json"
188+
cat >> ${rubybom} << EOL
189+
{
190+
"bomFormat": "CycloneDX",
191+
"specVersion": "1.3",
192+
"version": 1,
193+
"components": [
194+
{
195+
"type": "library",
196+
"name": "ruby",
197+
"version": "$ruby_version"
198+
}
199+
]
200+
}
133201
EOL
134-
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
135213
</pre>
136214

137215
Then rebuild your app using the updated buildpack:
@@ -141,27 +219,40 @@ Then rebuild your app using the updated buildpack:
141219
pack build test-ruby-app --path ./ruby-sample-app --buildpack ./ruby-buildpack
142220
```{{execute}}
143221
144-
You should then be able to inspect your Ruby app for its Bill-of-Materials via:
222+
Viewing your bill-of-materials requires first pushing the built image to a container registry and then querying the bill-of-materials. If you are a `docker` user, we can experiment with an OCI container registry by running an instance of Docker's `registry:2` (as an aside, the standard Docker daemon is a container registry, but is not fully [OCI compliant](https://opencontainers.org/)).
145223
146224
<!-- test:exec -->
147225
```bash
148-
pack inspect-image test-ruby-app --bom
226+
docker run -d -p 5000:5000 registry:2
227+
docker image tag test-ruby-app localhost:5000/test-ruby-app
228+
docker push localhost:5000/test-ruby-app
229+
pack sbom download localhost:5000/test-ruby-app
149230
```{{execute}}
150231
232+
The SBOM information is now downloaded to the local file system:
233+
234+
<!-- test:exec -->
235+
```bash
236+
cat layers/sbom/launch/examples_ruby/ruby/sbom.cdx.json | jq -M
237+
```
238+
151239
You should find that the included `ruby` version is `2.5.0` as expected.
152240
153-
<!-- test:assert=contains -->
241+
<!-- test:assert=contains;ignore-lines=... -->
154242
```text
243+
{
244+
"bomFormat": "CycloneDX",
245+
"specVersion": "1.3",
246+
"version": 1,
247+
"components": [
155248
{
249+
"type": "library",
156250
"name": "ruby",
157-
"metadata": {
158-
"version": "2.5.0"
159-
},
160-
"buildpacks": {
161-
"id": "examples/ruby",
162-
"version": "0.0.1"
163-
}
164-
}
251+
"version": "2.5.0"
252+
},
253+
...
254+
]
255+
}
165256
```
166257
167258
Congratulations! You’ve created your first configurable Cloud Native Buildpack that uses detection, image layers, and caching to create an introspectable and runnable OCI image.

0 commit comments

Comments
 (0)