Skip to content

Commit cccc7be

Browse files
Frankie G-JSophie Wigmore
Frankie G-J
and
Sophie Wigmore
authored
Restructure (#341)
* Restructure and modernize buildpack Signed-off-by: Frankie Gallina-Jones <frankieg@vmware.com> * Apply suggestions from code review Co-authored-by: Sophie Wigmore <swigmore@vmware.com>
1 parent f217e27 commit cccc7be

36 files changed

+3736
-949
lines changed

build.go

+128-33
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,75 @@
11
package phpdist
22

33
import (
4+
"os"
45
"path/filepath"
6+
"strings"
57
"time"
68

79
"github.com/Masterminds/semver"
8-
"github.com/paketo-buildpacks/packit"
9-
"github.com/paketo-buildpacks/packit/chronos"
10-
"github.com/paketo-buildpacks/packit/postal"
10+
"github.com/paketo-buildpacks/packit/v2"
11+
"github.com/paketo-buildpacks/packit/v2/chronos"
12+
"github.com/paketo-buildpacks/packit/v2/postal"
13+
"github.com/paketo-buildpacks/packit/v2/scribe"
1114
)
1215

16+
//go:generate faux --interface FileManager --output fakes/file_manager.go
17+
18+
// FileManager defines the interface for manipulating files in the PHP installation
19+
// in the build container.
20+
type FileManager interface {
21+
FindExtensions(layerRoot string) (string, error)
22+
WriteConfig(layerRoot, cnbPath string, data PhpIniConfig) (defaultConfig string, buildpackConfig string, err error)
23+
}
24+
1325
//go:generate faux --interface EntryResolver --output fakes/entry_resolver.go
26+
27+
// EntryResolver defines the interface for picking the most relevant entry from
28+
// the Buildpack Plan entries.
1429
type EntryResolver interface {
15-
Resolve([]packit.BuildpackPlanEntry) packit.BuildpackPlanEntry
30+
Resolve(name string, entries []packit.BuildpackPlanEntry, priorities []interface{}) (packit.BuildpackPlanEntry, []packit.BuildpackPlanEntry)
31+
MergeLayerTypes(name string, entries []packit.BuildpackPlanEntry) (launch, build bool)
1632
}
1733

1834
//go:generate faux --interface DependencyManager --output fakes/dependency_manager.go
35+
36+
// DependencyManager defines the interface for picking the best matching
37+
// dependency, installing it, and generating a BOM.
1938
type DependencyManager interface {
2039
Resolve(path, id, version, stack string) (postal.Dependency, error)
21-
Install(dependency postal.Dependency, cnbPath, layerPath string) error
40+
Deliver(dependency postal.Dependency, cnbPath, layerPath, platformPath string) error
41+
GenerateBillOfMaterials(dependencies ...postal.Dependency) []packit.BOMEntry
2242
}
2343

2444
//go:generate faux --interface EnvironmentConfiguration --output fakes/environment_configuration.go
25-
type EnvironmentConfiguration interface {
26-
Configure(layer packit.Layer) error
27-
}
2845

29-
//go:generate faux --interface BuildPlanRefinery --output fakes/build_plan_refinery.go
30-
type BuildPlanRefinery interface {
31-
BillOfMaterials(dependency postal.Dependency) packit.BuildpackPlan
46+
// EnvironmentConfiguration defines the interface for setting build- and launch-time
47+
// environment variables on the layer.
48+
type EnvironmentConfiguration interface {
49+
Configure(layer packit.Layer, extensionsDir, defaultIni string, scanDirs []string) error
3250
}
3351

34-
func Build(entries EntryResolver,
52+
// Build will return a packit.BuildFunc that will be invoked during the build
53+
// phase of the buildpack lifecycle.
54+
//
55+
// Build will find the right php dependency to install, install it in a layer,
56+
// and generate a Bill-of-Materials. On rebuilds, it reuses the cached
57+
// dependency if the SHA256 of the requested version matches the SHA256 of the
58+
// cached version. Build also sets up a default php.ini configuration.
59+
func Build(entryResolver EntryResolver,
3560
dependencies DependencyManager,
61+
files FileManager,
3662
environment EnvironmentConfiguration,
37-
planRefinery BuildPlanRefinery,
38-
logger LogEmitter,
63+
logger scribe.Emitter,
3964
clock chronos.Clock) packit.BuildFunc {
4065
return func(context packit.BuildContext) (packit.BuildResult, error) {
4166
var err error
4267

43-
logger.Title(context.BuildpackInfo)
68+
logger.Title("%s %s", context.BuildpackInfo.Name, context.BuildpackInfo.Version)
4469
logger.Process("Resolving PHP version")
4570

46-
entry := entries.Resolve(context.Plan.Entries)
71+
entry, entries := entryResolver.Resolve(PHPDependency, context.Plan.Entries, EntryPriorities)
72+
logger.Candidates(entries)
4773

4874
version, _ := entry.Metadata["version"].(string)
4975
dependency, err := dependencies.Resolve(filepath.Join(context.CNBPath, "buildpack.toml"), entry.Name, version, context.Stack)
@@ -52,7 +78,7 @@ func Build(entries EntryResolver,
5278
return packit.BuildResult{}, err
5379
}
5480

55-
logger.SelectedDependency(entry, dependency.Version)
81+
logger.SelectedDependency(entry, dependency, clock.Now())
5682

5783
source, _ := entry.Metadata["version-source"].(string)
5884
if source == "buildpack.yml" {
@@ -62,28 +88,51 @@ func Build(entries EntryResolver,
6288
logger.Break()
6389
}
6490

65-
phpLayer, err := context.Layers.Get("php")
91+
logger.Debug.Process("Getting the layer associated with PHP:")
92+
phpLayer, err := context.Layers.Get(PHPDependency)
6693
if err != nil {
6794
return packit.BuildResult{}, err
6895
}
96+
logger.Debug.Subprocess(phpLayer.Path)
97+
logger.Debug.Break()
6998

70-
bom := planRefinery.BillOfMaterials(postal.Dependency{
71-
ID: dependency.ID,
72-
Name: dependency.Name,
73-
SHA256: dependency.SHA256,
74-
Stacks: dependency.Stacks,
75-
URI: dependency.URI,
76-
Version: dependency.Version,
77-
})
99+
logger.Debug.Process("Generating the SBOM")
100+
logger.Debug.Break()
101+
bom := dependencies.GenerateBillOfMaterials(dependency)
102+
launch, build := entryResolver.MergeLayerTypes(PHPDependency, context.Plan.Entries)
103+
104+
phpLayer.Launch, phpLayer.Build, phpLayer.Cache = launch, build, build
105+
106+
var buildMetadata packit.BuildMetadata
107+
if build {
108+
buildMetadata.BOM = bom
109+
}
110+
111+
var launchMetadata packit.LaunchMetadata
112+
if launch {
113+
launchMetadata.BOM = bom
114+
}
78115

79116
cachedSHA, ok := phpLayer.Metadata[DepKey].(string)
80117
if ok && cachedSHA == dependency.SHA256 {
81118
logger.Process("Reusing cached layer %s", phpLayer.Path)
119+
logger.Debug.Subprocess("SHA256 of cached PHP dependency matches SHA256 of resolved dependency")
82120
logger.Break()
83121

122+
if phpLayer.Build {
123+
logger.Debug.Process("PHP layer will be available to other buildpacks during build")
124+
}
125+
if phpLayer.Launch {
126+
logger.Debug.Process("PHP layer will be available at runtime")
127+
}
128+
if phpLayer.Cache {
129+
logger.Debug.Process("PHP layer will be cached")
130+
}
131+
84132
return packit.BuildResult{
85-
Plan: bom,
86133
Layers: []packit.Layer{phpLayer},
134+
Build: buildMetadata,
135+
Launch: launchMetadata,
87136
}, nil
88137
}
89138

@@ -94,9 +143,7 @@ func Build(entries EntryResolver,
94143
return packit.BuildResult{}, err
95144
}
96145

97-
phpLayer.Launch = entry.Metadata["launch"] == true
98-
phpLayer.Build = entry.Metadata["build"] == true
99-
phpLayer.Cache = entry.Metadata["build"] == true
146+
phpLayer.Launch, phpLayer.Build, phpLayer.Cache = launch, build, build
100147

101148
phpLayer.Metadata = map[string]interface{}{
102149
DepKey: dependency.SHA256,
@@ -105,7 +152,9 @@ func Build(entries EntryResolver,
105152

106153
logger.Subprocess("Installing PHP %s", dependency.Version)
107154
duration, err := clock.Measure(func() error {
108-
return dependencies.Install(dependency, context.CNBPath, phpLayer.Path)
155+
logger.Debug.Subprocess("Installation path: %s", phpLayer.Path)
156+
logger.Debug.Subprocess("Dependency URI: %s", dependency.URI)
157+
return dependencies.Deliver(dependency, context.CNBPath, phpLayer.Path, context.Platform.Path)
109158
})
110159
if err != nil {
111160
return packit.BuildResult{}, err
@@ -114,14 +163,60 @@ func Build(entries EntryResolver,
114163
logger.Action("Completed in %s", duration.Round(time.Millisecond))
115164
logger.Break()
116165

117-
err = environment.Configure(phpLayer)
166+
logger.Debug.Subprocess("Finding PHP extensions directory")
167+
extensionsDir, err := files.FindExtensions(phpLayer.Path)
168+
if err != nil {
169+
return packit.BuildResult{}, err
170+
}
171+
logger.Debug.Break()
172+
173+
libDir := "lib"
174+
if userLibDir := os.Getenv("BP_PHP_LIB_DIR"); userLibDir != "" {
175+
libDir = userLibDir
176+
logger.Debug.Subprocess("$BP_PHP_LIB_DIR = %s", libDir)
177+
logger.Debug.Break()
178+
}
179+
180+
logger.Subprocess("Generating default PHP configuration")
181+
defaultConfig, buildpackConfig, err := files.WriteConfig(phpLayer.Path, context.CNBPath, PhpIniConfig{
182+
IncludePath: strings.Join([]string{
183+
filepath.Join(phpLayer.Path, "lib", "php"),
184+
filepath.Join(context.WorkingDir, libDir),
185+
}, string(os.PathListSeparator)),
186+
ExtensionDir: extensionsDir,
187+
// TODO: figure out where extensions and zendextensions arrays come from
188+
// Do we even need to load extensions in the default INI file? Maybe better to simply require that folks add additional INI files?
189+
})
190+
if err != nil {
191+
return packit.BuildResult{}, err
192+
}
193+
logger.Debug.Action("Generated %s and %s", defaultConfig, buildpackConfig)
194+
logger.Break()
195+
196+
err = environment.Configure(phpLayer, extensionsDir, defaultConfig, []string{
197+
filepath.Dir(defaultConfig),
198+
filepath.Dir(buildpackConfig),
199+
filepath.Join(context.WorkingDir, "php.ini.d"),
200+
})
118201
if err != nil {
119202
return packit.BuildResult{}, err
120203
}
204+
logger.EnvironmentVariables(phpLayer)
205+
206+
if phpLayer.Build {
207+
logger.Debug.Process("PHP layer will be available to other buildpacks during build")
208+
}
209+
if phpLayer.Launch {
210+
logger.Debug.Process("PHP layer will be available at runtime")
211+
}
212+
if phpLayer.Cache {
213+
logger.Debug.Process("PHP layer will be cached")
214+
}
121215

122216
return packit.BuildResult{
123-
Plan: bom,
124217
Layers: []packit.Layer{phpLayer},
218+
Build: buildMetadata,
219+
Launch: launchMetadata,
125220
}, nil
126221
}
127222
}

0 commit comments

Comments
 (0)