Skip to content

Commit 5cbbf33

Browse files
authored
pack builder create supports system buildpacks (#2349)
This PR implements [RFC 101 - System Buildpacks](https://github.com/buildpacks/rfcs/blob/main/text/0101-system-buildpacks.md ) support in pack. Systems buildpacks are special buildpacks that are automatically included before (pre) and after (post) the regular buildpacks during the build process, providing platform-level functionality like shell profile scripts, service binding, and other platform-specific capabilities. Signed-off-by: Juan Bustamante <[email protected]>
1 parent d5a88c7 commit 5cbbf33

29 files changed

+1022
-43
lines changed

acceptance/acceptance_test.go

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1273,6 +1273,225 @@ func testAcceptance(
12731273
})
12741274
})
12751275

1276+
when("system buildpacks", func() {
1277+
var (
1278+
builderWithSystemBP string
1279+
builderWithFailingSystemBP string
1280+
builderWithOptionalFailingSystemBP string
1281+
regularBuilder string
1282+
)
1283+
1284+
it.Before(func() {
1285+
// Create builder with system buildpacks
1286+
builderWithSystemBP = fmt.Sprintf("pack.local/builder-with-system-bps/%s", h.RandString(10))
1287+
h.SkipIf(t, !createBuilderPack.Supports("builder create"), "pack builder create not supported")
1288+
1289+
createBuilderPack.JustRunSuccessfully(
1290+
"builder", "create", builderWithSystemBP,
1291+
"--config", createBuilderPack.FixtureManager().FixtureLocation("builder_with_system_buildpacks.toml"),
1292+
)
1293+
1294+
// Create builder with failing system buildpack
1295+
builderWithFailingSystemBP = fmt.Sprintf("pack.local/builder-fail-system/%s", h.RandString(10))
1296+
createBuilderPack.JustRunSuccessfully(
1297+
"builder", "create", builderWithFailingSystemBP,
1298+
"--config", createBuilderPack.FixtureManager().FixtureLocation("builder_with_failing_system_buildpack.toml"),
1299+
)
1300+
1301+
// Create builder with optional failing system buildpack
1302+
builderWithOptionalFailingSystemBP = fmt.Sprintf("pack.local/builder-optional-fail/%s", h.RandString(10))
1303+
createBuilderPack.JustRunSuccessfully(
1304+
"builder", "create", builderWithOptionalFailingSystemBP,
1305+
"--config", createBuilderPack.FixtureManager().FixtureLocation("builder_with_optional_failing_system_buildpack.toml"),
1306+
)
1307+
1308+
// Create regular builder for comparison
1309+
regularBuilder = fmt.Sprintf("pack.local/regular-builder/%s", h.RandString(10))
1310+
createBuilderPack.JustRunSuccessfully(
1311+
"builder", "create", regularBuilder,
1312+
"--config", createBuilderPack.FixtureManager().FixtureLocation("builder.toml"),
1313+
)
1314+
})
1315+
1316+
it.After(func() {
1317+
imageManager.CleanupImages(builderWithSystemBP)
1318+
imageManager.CleanupImages(builderWithFailingSystemBP)
1319+
imageManager.CleanupImages(builderWithOptionalFailingSystemBP)
1320+
imageManager.CleanupImages(regularBuilder)
1321+
})
1322+
1323+
when("inspecting builder with system buildpacks", func() {
1324+
it("shows system buildpacks in builder info", func() {
1325+
output := createBuilderPack.RunSuccessfully("builder", "inspect", builderWithSystemBP)
1326+
1327+
// Verify system buildpacks are shown in the output
1328+
h.AssertContains(t, output, "system/pre")
1329+
h.AssertContains(t, output, "system/post")
1330+
})
1331+
})
1332+
1333+
when("building with system buildpacks", func() {
1334+
var (
1335+
appImage string
1336+
appPath string
1337+
)
1338+
1339+
it.Before(func() {
1340+
appPath = filepath.Join("testdata", "mock_app")
1341+
appImage = fmt.Sprintf("pack.local/app/%s", h.RandString(10))
1342+
})
1343+
1344+
it.After(func() {
1345+
imageManager.CleanupImages(appImage)
1346+
})
1347+
1348+
when("system buildpacks are enabled (default)", func() {
1349+
it("runs pre-system buildpacks before regular buildpacks", func() {
1350+
output := pack.RunSuccessfully(
1351+
"build", appImage,
1352+
"--path", appPath,
1353+
"--builder", builderWithSystemBP,
1354+
"--no-color",
1355+
)
1356+
1357+
// Verify pre-system buildpack ran before the main buildpack
1358+
h.AssertContains(t, output, "DETECT: System Pre buildpack")
1359+
h.AssertContains(t, output, "BUILD: System Pre buildpack")
1360+
h.AssertContains(t, output, "Simple Layers Buildpack")
1361+
1362+
// Verify order: system pre should come before main buildpack
1363+
systemPreIndex := strings.Index(output, "BUILD: System Pre buildpack")
1364+
mainBuildpackIndex := strings.Index(output, "Simple Layers Buildpack")
1365+
if systemPreIndex == -1 || mainBuildpackIndex == -1 || systemPreIndex >= mainBuildpackIndex {
1366+
t.Fatalf("Expected system pre buildpack to run before main buildpack")
1367+
}
1368+
})
1369+
1370+
it("runs post-system buildpacks after regular buildpacks", func() {
1371+
output := pack.RunSuccessfully(
1372+
"build", appImage,
1373+
"--path", appPath,
1374+
"--builder", builderWithSystemBP,
1375+
"--no-color",
1376+
)
1377+
1378+
// Verify post-system buildpack ran after the main buildpack
1379+
h.AssertContains(t, output, "BUILD: System Post buildpack")
1380+
1381+
// Verify order: system post should come after main buildpack
1382+
mainBuildpackIndex := strings.Index(output, "Simple Layers Buildpack")
1383+
systemPostIndex := strings.Index(output, "BUILD: System Post buildpack")
1384+
if mainBuildpackIndex == -1 || systemPostIndex == -1 || mainBuildpackIndex >= systemPostIndex {
1385+
t.Fatalf("Expected system post buildpack to run after main buildpack")
1386+
}
1387+
})
1388+
1389+
it("builds successfully with system buildpacks", func() {
1390+
output := pack.RunSuccessfully(
1391+
"build", appImage,
1392+
"--path", appPath,
1393+
"--builder", builderWithSystemBP,
1394+
"--verbose",
1395+
)
1396+
1397+
// Verify system buildpack contributed during build
1398+
h.AssertContains(t, output, "BUILD: System Pre buildpack")
1399+
h.AssertContains(t, output, "BUILD: System Post buildpack")
1400+
1401+
// Verify the image was successfully built
1402+
h.AssertContains(t, output, "Successfully built image")
1403+
assertImage.ExistsLocally(appImage)
1404+
})
1405+
})
1406+
1407+
when("--disable-system-buildpacks flag is used", func() {
1408+
it("does not run system buildpacks", func() {
1409+
output := pack.RunSuccessfully(
1410+
"build", appImage,
1411+
"--path", appPath,
1412+
"--builder", builderWithSystemBP,
1413+
"--disable-system-buildpacks",
1414+
"--no-color",
1415+
)
1416+
1417+
// Verify system buildpacks did not run
1418+
h.AssertNotContains(t, output, "DETECT: System Pre buildpack")
1419+
h.AssertNotContains(t, output, "BUILD: System Pre buildpack")
1420+
h.AssertNotContains(t, output, "BUILD: System Post buildpack")
1421+
1422+
// Verify main buildpack still runs
1423+
h.AssertContains(t, output, "Simple Layers Buildpack")
1424+
1425+
// Verify the image was successfully built
1426+
h.AssertContains(t, output, "Successfully built image")
1427+
assertImage.ExistsLocally(appImage)
1428+
})
1429+
})
1430+
1431+
when("builder has no system buildpacks", func() {
1432+
it("builds normally without system buildpacks", func() {
1433+
output := pack.RunSuccessfully(
1434+
"build", appImage,
1435+
"--path", appPath,
1436+
"--builder", regularBuilder,
1437+
"--no-color",
1438+
)
1439+
1440+
// Verify no system buildpacks ran
1441+
h.AssertNotContains(t, output, "System Pre buildpack")
1442+
h.AssertNotContains(t, output, "System Post buildpack")
1443+
1444+
// Verify main buildpack runs
1445+
h.AssertContains(t, output, "Simple Layers Buildpack")
1446+
1447+
// Verify the image was successfully built
1448+
h.AssertContains(t, output, "Successfully built image")
1449+
assertImage.ExistsLocally(appImage)
1450+
})
1451+
})
1452+
1453+
when("required system buildpack fails detection", func() {
1454+
it("fails the build", func() {
1455+
output, err := pack.Run(
1456+
"build", appImage,
1457+
"--path", appPath,
1458+
"--builder", builderWithFailingSystemBP,
1459+
"--no-color",
1460+
)
1461+
1462+
// Build should fail
1463+
h.AssertNotNil(t, err)
1464+
h.AssertContains(t, output, "DETECT: System Fail Detect buildpack (will fail)")
1465+
h.AssertContains(t, output, "No buildpack groups passed detection")
1466+
})
1467+
})
1468+
1469+
when("optional system buildpack fails detection", func() {
1470+
it("continues with the build", func() {
1471+
output := pack.RunSuccessfully(
1472+
"build", appImage,
1473+
"--path", appPath,
1474+
"--builder", builderWithOptionalFailingSystemBP,
1475+
"--no-color",
1476+
)
1477+
1478+
// Build should succeed despite optional system buildpack failing
1479+
h.AssertContains(t, output, "DETECT: System Fail Detect buildpack (will fail)")
1480+
h.AssertContains(t, output, "DETECT: System Pre buildpack")
1481+
h.AssertContains(t, output, "BUILD: System Pre buildpack")
1482+
h.AssertContains(t, output, "Simple Layers Buildpack")
1483+
1484+
// Verify the failed optional buildpack didn't run build
1485+
h.AssertNotContains(t, output, "BUILD: System Fail Detect buildpack")
1486+
1487+
// Verify the image was successfully built
1488+
h.AssertContains(t, output, "Successfully built image")
1489+
assertImage.ExistsLocally(appImage)
1490+
})
1491+
})
1492+
})
1493+
})
1494+
12761495
when("build", func() {
12771496
var repo, repoName string
12781497

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env bash
2+
3+
echo "---> BUILD: System Fail Detect buildpack (should never run)"
4+
5+
# This should never be reached
6+
exit 1
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env bash
2+
3+
echo "---> DETECT: System Fail Detect buildpack (will fail)"
4+
5+
# Always fail detection
6+
exit 1
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
api = "0.7"
2+
3+
[buildpack]
4+
id = "system/fail-detect"
5+
version = "system-fail-detect-version"
6+
name = "System Fail Detect Buildpack"
7+
8+
[[stacks]]
9+
id = "pack.test.stack"
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/usr/bin/env bash
2+
3+
echo "---> BUILD: System Post buildpack"
4+
5+
set -o errexit
6+
set -o pipefail
7+
8+
layers_dir=$1
9+
platform_dir=$2
10+
11+
# Create a layer to verify it ran
12+
mkdir -p "${layers_dir}/system-post"
13+
cat > "${layers_dir}/system-post.toml" <<EOF
14+
launch = true
15+
cache = true
16+
EOF
17+
18+
echo "System Post Buildpack was here" > "${layers_dir}/system-post/marker"
19+
20+
exit 0
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env bash
2+
3+
echo "---> DETECT: System Post buildpack"
4+
5+
# Always pass detection for testing
6+
exit 0
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
api = "0.7"
2+
3+
[buildpack]
4+
id = "system/post"
5+
version = "system-post-version"
6+
name = "System Post Buildpack"
7+
8+
[[stacks]]
9+
id = "pack.test.stack"
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/usr/bin/env bash
2+
3+
echo "---> BUILD: System Pre buildpack"
4+
5+
set -o errexit
6+
set -o pipefail
7+
8+
layers_dir=$1
9+
platform_dir=$2
10+
11+
# Create a layer to verify it ran
12+
mkdir -p "${layers_dir}/system-pre"
13+
cat > "${layers_dir}/system-pre.toml" <<EOF
14+
launch = true
15+
cache = true
16+
EOF
17+
18+
echo "System Pre Buildpack was here" > "${layers_dir}/system-pre/marker"
19+
20+
exit 0
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env bash
2+
3+
echo "---> DETECT: System Pre buildpack"
4+
5+
# Always pass detection for testing
6+
exit 0
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
api = "0.7"
2+
3+
[buildpack]
4+
id = "system/pre"
5+
version = "system-pre-version"
6+
name = "System Pre Buildpack"
7+
8+
[[stacks]]
9+
id = "pack.test.stack"
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
[[buildpacks]]
2+
id = "simple-layers-buildpack"
3+
uri = "file://{{.Fixtures}}/simple-layers-buildpack"
4+
5+
[[buildpacks]]
6+
id = "system/fail-detect"
7+
uri = "file://{{.Fixtures}}/system-fail-detect"
8+
9+
[[buildpacks]]
10+
id = "system/post"
11+
uri = "file://{{.Fixtures}}/system-post-buildpack"
12+
13+
# System buildpacks configuration
14+
[system]
15+
[system.pre]
16+
buildpacks = [
17+
{ id = "system/fail-detect", version = "system-fail-detect-version", optional = false }
18+
]
19+
20+
[system.post]
21+
buildpacks = [
22+
{ id = "system/post", version = "system-post-version", optional = true }
23+
]
24+
25+
[[order]]
26+
[[order.group]]
27+
id = "simple-layers-buildpack"
28+
version = "simple-layers-buildpack-version"
29+
30+
[stack]
31+
id = "pack.test.stack"
32+
build-image = "pack-test/build"
33+
run-image = "pack-test/run"

0 commit comments

Comments
 (0)