github.com/paketoio/libpak@v1.3.1/build.go (about)

     1  /*
     2   * Copyright 2018-2020 the original author or authors.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *      https://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package libpak
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"path/filepath"
    23  	"strings"
    24  
    25  	"github.com/buildpacks/libcnb"
    26  	"github.com/heroku/color"
    27  	"github.com/imdario/mergo"
    28  	"github.com/paketoio/libpak/bard"
    29  	"github.com/paketoio/libpak/internal"
    30  )
    31  
    32  // Build is called by the main function of a buildpack, for build.
    33  func Build(f libcnb.BuildFunc, options ...libcnb.Option) {
    34  	logger := bard.NewLogger(os.Stdout)
    35  
    36  	libcnb.Build(
    37  		func(context libcnb.BuildContext) (libcnb.BuildResult, error) {
    38  
    39  			// TODO: Remove once pack supports bindings natively
    40  			bindings, err := libcnb.NewBindingsFromEnvironment("CNB_BINDINGS")
    41  			if err != nil {
    42  				return libcnb.BuildResult{}, fmt.Errorf("unable to get bindings from $CNB_BINDINGS: %w", err)
    43  			}
    44  			if err = mergo.Merge(&context.Platform.Bindings, bindings); err != nil {
    45  				return libcnb.BuildResult{}, fmt.Errorf("unable to merge bindings %+v and %+v: %w", context.Platform.Bindings, bindings, err)
    46  			}
    47  
    48  			result, err := f(context)
    49  			if err != nil {
    50  				return result, bard.IdentifiableError{
    51  					Name:        context.Buildpack.Info.Name,
    52  					Description: context.Buildpack.Info.Version,
    53  					Err:         err,
    54  				}
    55  			}
    56  
    57  			file := filepath.Join(context.Layers.Path, "*.toml")
    58  			existing, err := filepath.Glob(file)
    59  			if err != nil {
    60  				return libcnb.BuildResult{}, fmt.Errorf("unable to list files in %s: %w", file, err)
    61  			}
    62  
    63  			var contrib []string
    64  			for _, l := range result.Layers {
    65  				contrib = append(contrib, l.Name())
    66  			}
    67  
    68  			var remove []string
    69  			for _, e := range existing {
    70  				if !strings.HasSuffix(e, "store.toml") && !contains(contrib, strings.TrimSuffix(filepath.Base(e), ".toml")) {
    71  					remove = append(remove, e)
    72  				}
    73  			}
    74  
    75  			if len(remove) > 0 {
    76  				logger.Header("%s unused layers", color.YellowString("Removing"))
    77  
    78  				for _, r := range remove {
    79  					logger.Body(strings.TrimSuffix(filepath.Base(r), ".toml"))
    80  					if err = os.RemoveAll(r); err != nil {
    81  						return libcnb.BuildResult{}, fmt.Errorf("unable to remove file %s: %w", r, err)
    82  					}
    83  				}
    84  			}
    85  
    86  			return result, nil
    87  		},
    88  		append([]libcnb.Option{
    89  			libcnb.WithEnvironmentWriter(internal.NewEnvironmentWriter()),
    90  			libcnb.WithExitHandler(internal.NewExitHandler()),
    91  			libcnb.WithTOMLWriter(internal.NewTOMLWriter()),
    92  		}, options...)...,
    93  	)
    94  }
    95  
    96  func contains(candidates []string, value string) bool {
    97  	for _, c := range candidates {
    98  		if c == value {
    99  			return true
   100  		}
   101  	}
   102  
   103  	return false
   104  }