diff --git a/cmd/cnbBuild.go b/cmd/cnbBuild.go index 46580ae7b..ce2cad984 100644 --- a/cmd/cnbBuild.go +++ b/cmd/cnbBuild.go @@ -1,6 +1,7 @@ package cmd import ( + "archive/zip" "encoding/json" "fmt" "os" @@ -88,6 +89,70 @@ func isBuilder(utils cnbutils.BuildUtils) (bool, error) { return true, nil } +func isZip(path string) bool { + r, err := zip.OpenReader(path) + + switch { + case err == nil: + r.Close() + return true + case err == zip.ErrFormat: + return false + default: + return false + } +} + +func copyProject(source, target string, utils cnbutils.BuildUtils) error { + sourceFiles, _ := utils.Glob(path.Join(source, "**")) + for _, sourceFile := range sourceFiles { + if !isIgnored(sourceFile) { + target := path.Join(target, strings.ReplaceAll(sourceFile, source, "")) + dir, err := isDir(sourceFile) + if err != nil { + log.SetErrorCategory(log.ErrorBuild) + return errors.Wrapf(err, "Checking file info '%s' failed", target) + } + + if dir { + err = utils.MkdirAll(target, os.ModePerm) + if err != nil { + log.SetErrorCategory(log.ErrorBuild) + return errors.Wrapf(err, "Creating directory '%s' failed", target) + } + } else { + log.Entry().Debugf("Copying '%s' to '%s'", sourceFile, target) + _, err = utils.Copy(sourceFile, target) + if err != nil { + log.SetErrorCategory(log.ErrorBuild) + return errors.Wrapf(err, "Copying '%s' to '%s' failed", sourceFile, target) + } + } + + } else { + log.Entry().Debugf("Filtered out '%s'", sourceFile) + } + } + return nil +} + +func copyFile(source, target string, utils cnbutils.BuildUtils) error { + + if isZip(source) { + log.Entry().Infof("Extracting archive '%s' to '%s'", source, target) + _, err := piperutils.Unzip(source, target) + if err != nil { + log.SetErrorCategory(log.ErrorBuild) + return errors.Wrapf(err, "Extracting archive '%s' to '%s' failed", source, target) + } + } else { + log.SetErrorCategory(log.ErrorBuild) + return errors.New("application path must be a directory or zip") + } + + return nil +} + func runCnbBuild(config *cnbBuildOptions, telemetryData *telemetry.CustomData, utils cnbutils.BuildUtils, commonPipelineEnvironment *cnbBuildCommonPipelineEnvironment) error { var err error @@ -140,33 +205,23 @@ func runCnbBuild(config *cnbBuildOptions, telemetryData *telemetry.CustomData, u source = config.Path } - sourceFiles, _ := utils.Glob(path.Join(source, "**")) - for _, sourceFile := range sourceFiles { - if !isIgnored(sourceFile) { - target := path.Join(target, strings.ReplaceAll(sourceFile, source, "")) - dir, err := isDir(sourceFile) - if err != nil { - log.SetErrorCategory(log.ErrorBuild) - return errors.Wrapf(err, "Checking file info '%s' failed", target) - } + dir, err := isDir(source) + if err != nil { + log.SetErrorCategory(log.ErrorBuild) + return errors.Wrapf(err, "Checking file info '%s' failed", target) + } - if dir { - err = utils.MkdirAll(target, os.ModePerm) - if err != nil { - log.SetErrorCategory(log.ErrorBuild) - return errors.Wrapf(err, "Creating directory '%s' failed", target) - } - } else { - log.Entry().Debugf("Copying '%s' to '%s'", sourceFile, target) - _, err = utils.Copy(sourceFile, target) - if err != nil { - log.SetErrorCategory(log.ErrorBuild) - return errors.Wrapf(err, "Copying '%s' to '%s' failed", sourceFile, target) - } - } - - } else { - log.Entry().Debugf("Filtered out '%s'", sourceFile) + if dir { + err = copyProject(source, target, utils) + if err != nil { + log.SetErrorCategory(log.ErrorBuild) + return errors.Wrapf(err, "Copying '%s' into '%s' failed", source, target) + } + } else { + err = copyFile(source, target, utils) + if err != nil { + log.SetErrorCategory(log.ErrorBuild) + return errors.Wrapf(err, "Copying '%s' into '%s' failed", source, target) } } @@ -211,20 +266,20 @@ func runCnbBuild(config *cnbBuildOptions, telemetryData *telemetry.CustomData, u err = utils.RunExecutable(detectorPath, "-buildpacks", buildpacksPath, "-order", orderPath) if err != nil { log.SetErrorCategory(log.ErrorBuild) - return errors.Wrap(err, fmt.Sprintf("execution of '%s' failed", detectorPath)) + return errors.Wrapf(err, "execution of '%s' failed", detectorPath) } err = utils.RunExecutable(builderPath, "-buildpacks", buildpacksPath) if err != nil { log.SetErrorCategory(log.ErrorBuild) - return errors.Wrap(err, fmt.Sprintf("execution of '%s' failed", builderPath)) + return errors.Wrapf(err, "execution of '%s' failed", builderPath) } utils.AppendEnv([]string{fmt.Sprintf("CNB_REGISTRY_AUTH=%s", string(cnbRegistryAuth))}) err = utils.RunExecutable(exporterPath, fmt.Sprintf("%s:%s", containerImage, containerImageTag), fmt.Sprintf("%s:latest", containerImage)) if err != nil { log.SetErrorCategory(log.ErrorBuild) - return errors.Wrap(err, fmt.Sprintf("execution of '%s' failed", exporterPath)) + return errors.Wrapf(err, "execution of '%s' failed", exporterPath) } return nil diff --git a/cmd/cnbBuild_generated.go b/cmd/cnbBuild_generated.go index 7e520d426..e20894b9a 100644 --- a/cmd/cnbBuild_generated.go +++ b/cmd/cnbBuild_generated.go @@ -137,7 +137,7 @@ func addCnbBuildFlags(cmd *cobra.Command, stepConfig *cnbBuildOptions) { cmd.Flags().StringVar(&stepConfig.ContainerImageTag, "containerImageTag", os.Getenv("PIPER_containerImageTag"), "Tag of the container which will be built") cmd.Flags().StringVar(&stepConfig.ContainerRegistryURL, "containerRegistryUrl", os.Getenv("PIPER_containerRegistryUrl"), "Container registry where the image should be pushed to") cmd.Flags().StringSliceVar(&stepConfig.Buildpacks, "buildpacks", []string{}, "List of custom buildpacks to use in the form of '/[:]'.") - cmd.Flags().StringVar(&stepConfig.Path, "path", os.Getenv("PIPER_path"), "The path should either point to your sources or an artifact build before.") + cmd.Flags().StringVar(&stepConfig.Path, "path", os.Getenv("PIPER_path"), "The path should either point to a directory with your sources or an artifact in zip format.") cmd.Flags().StringVar(&stepConfig.DockerConfigJSON, "dockerConfigJSON", os.Getenv("PIPER_dockerConfigJSON"), "Path to the file `.docker/config.json` - this is typically provided by your CI/CD system. You can find more details about the Docker credentials in the [Docker documentation](https://docs.docker.com/engine/reference/commandline/login/).") cmd.MarkFlagRequired("containerImageName") diff --git a/integration/integration_cnb_test.go b/integration/integration_cnb_test.go index 0492edeb8..bc82bfc63 100644 --- a/integration/integration_cnb_test.go +++ b/integration/integration_cnb_test.go @@ -1,4 +1,6 @@ +//go:build integration // +build integration + // can be execute with go test -tags=integration ./integration/... package main @@ -23,6 +25,35 @@ func TestNpmProject(t *testing.T) { container.assertHasOutput(t, "failed to write image to the following tags: [test/not-found:0.0.1") } +func TestZipPath(t *testing.T) { + t.Parallel() + container := givenThisContainer(t, IntegrationTestDockerExecRunnerBundle{ + Image: "paketobuildpacks/builder:full", + User: "cnb", + TestDir: []string{"testdata", "TestCnbIntegration", "zip"}, + }) + + container.whenRunningPiperCommand("cnbBuild", "--containerImageName", "not-found", "--containerImageTag", "0.0.1", "--containerRegistryUrl", "test", "--path", "go.zip") + + container.assertHasOutput(t, "running command: /cnb/lifecycle/detector") + container.assertHasOutput(t, "Installing Go") + container.assertHasOutput(t, "Paketo Go Build Buildpack") + container.assertHasOutput(t, "Saving test/not-found:0.0.1") +} + +func TestNonZipPath(t *testing.T) { + t.Parallel() + container := givenThisContainer(t, IntegrationTestDockerExecRunnerBundle{ + Image: "paketobuildpacks/builder:full", + User: "cnb", + TestDir: []string{"testdata", "TestMtaIntegration", "npm"}, + }) + + container.whenRunningPiperCommand("cnbBuild", "--containerImageName", "not-found", "--containerImageTag", "0.0.1", "--containerRegistryUrl", "test", "--path", "mta.yaml") + + container.assertHasOutput(t, "Copying 'mta.yaml' into '/workspace' failed: application path must be a directory or zip") +} + func TestNpmCustomBuildpacksFullProject(t *testing.T) { t.Parallel() container := givenThisContainer(t, IntegrationTestDockerExecRunnerBundle{ diff --git a/integration/testdata/TestCnbIntegration/zip/go.zip b/integration/testdata/TestCnbIntegration/zip/go.zip new file mode 100644 index 000000000..2748d2e83 Binary files /dev/null and b/integration/testdata/TestCnbIntegration/zip/go.zip differ diff --git a/resources/metadata/cnbBuild.yaml b/resources/metadata/cnbBuild.yaml index f2e191e42..dcad0d706 100644 --- a/resources/metadata/cnbBuild.yaml +++ b/resources/metadata/cnbBuild.yaml @@ -57,7 +57,7 @@ spec: - STEPS - name: path type: string - description: The path should either point to your sources or an artifact build before. + description: The path should either point to a directory with your sources or an artifact in zip format. scope: - PARAMETERS - STAGES