Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic implementation of the detect command #2072

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ func NewPackCommand(logger ConfigurableLogger) (*cobra.Command, error) {
commands.AddHelpFlag(rootCmd, "pack")

rootCmd.AddCommand(commands.Build(logger, cfg, packClient))
rootCmd.AddCommand(commands.Detect(logger, cfg, packClient))
rootCmd.AddCommand(commands.NewBuilderCommand(logger, cfg, packClient))
rootCmd.AddCommand(commands.NewBuildpackCommand(logger, cfg, packClient, buildpackage.NewConfigReader()))
rootCmd.AddCommand(commands.NewExtensionCommand(logger, cfg, packClient, buildpackage.NewConfigReader()))
Expand Down
2 changes: 1 addition & 1 deletion internal/build/container_ops_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ drwxrwxrwx 2 123 456 (.*) some-vol
h.AssertNil(t, err)
defer cleanupContainer(ctx, ctr.ID)

writeOp := build.WriteRunToml(containerPath, []builder.RunImageMetadata{builder.RunImageMetadata{
writeOp := build.WriteRunToml(containerPath, []builder.RunImageMetadata{{
Image: "image-1",
Mirrors: []string{
"mirror-1",
Expand Down
12 changes: 12 additions & 0 deletions internal/build/lifecycle_execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,18 @@ func (l *LifecycleExecution) Run(ctx context.Context, phaseFactoryCreator PhaseF
return l.Create(ctx, buildCache, launchCache, phaseFactory)
}

func (l *LifecycleExecution) RunDetect(ctx context.Context, phaseFactoryCreator PhaseFactoryCreator) error {
phaseFactory := phaseFactoryCreator(l)

l.logger.Info(style.Step("DETECTING"))
if err := l.Detect(ctx, phaseFactory); err != nil {
return err
}

return nil
// return l.Create(ctx, buildCache, launchCache, phaseFactory)
}

func (l *LifecycleExecution) Cleanup() error {
var reterr error
if err := l.docker.VolumeRemove(context.Background(), l.layersVolume, true); err != nil {
Expand Down
15 changes: 15 additions & 0 deletions internal/build/lifecycle_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,18 @@ func (l *LifecycleExecutor) Execute(ctx context.Context, opts LifecycleOptions)
lifecycleExec.Run(ctx, NewDefaultPhaseFactory)
})
}

func (l *LifecycleExecutor) Detect(ctx context.Context, opts LifecycleOptions) error {
tmpDir, err := os.MkdirTemp("", "pack.tmp")
if err != nil {
return err
}

lifecycleExec, err := NewLifecycleExecution(l.logger, l.docker, tmpDir, opts)
if err != nil {
return err
}

defer lifecycleExec.Cleanup()
return lifecycleExec.RunDetect(ctx, NewDefaultPhaseFactory)
}
1 change: 1 addition & 0 deletions internal/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type PackClient interface {
PackageBuildpack(ctx context.Context, opts client.PackageBuildpackOptions) error
PackageExtension(ctx context.Context, opts client.PackageBuildpackOptions) error
Build(context.Context, client.BuildOptions) error
Detect(context.Context, client.BuildOptions) error
RegisterBuildpack(context.Context, client.RegisterBuildpackOptions) error
YankBuildpack(client.YankBuildpackOptions) error
InspectBuildpack(client.InspectBuildpackOptions) (*client.BuildpackInfo, error)
Expand Down
128 changes: 128 additions & 0 deletions internal/commands/detect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package commands

import (
"github.com/google/go-containerregistry/pkg/name"
"github.com/pkg/errors"
"github.com/spf13/cobra"

"github.com/buildpacks/pack/internal/config"
"github.com/buildpacks/pack/pkg/client"
"github.com/buildpacks/pack/pkg/image"
"github.com/buildpacks/pack/pkg/logging"
)

type DetectFlags struct {
AppPath string
Builder string
Registry string
DescriptorPath string
LifecycleImage string
Volumes []string
PreBuildpacks []string
PostBuildpacks []string
Policy string
Network string
Env []string
EnvFiles []string
Buildpacks []string
Extensions []string
}

// Run Detect phase of lifecycle against a source code
func Detect(logger logging.Logger, cfg config.Config, packClient PackClient) *cobra.Command {
var flags DetectFlags

cmd := &cobra.Command{
Use: "detect",
Args: cobra.ExactArgs(0),
Short: "Run the detect phase of buildpacks against your source code",
Example: "pack detect --path apps/test-app --builder cnbs/sample-builder:bionic",
Long: "Pack Detect uses Cloud Native Buildpacks to run the detect phase of buildpack groups against the source code.\n" +
"You can use `--path` to specify a different source code directory, else it defaults to the current directory. Detect requires a `builder`, which can either " +
"be provided directly to build using `--builder`, or can be set using the `set-default-builder` command.",
RunE: logError(logger, func(cmd *cobra.Command, args []string) error {
if err := validateDetectFlags(&flags, cfg, logger); err != nil {
return err
}

builder := flags.Builder

if builder == "" {
suggestSettingBuilder(logger, packClient)
return client.NewSoftError()
}

buildpacks := flags.Buildpacks
extensions := flags.Extensions

env, err := parseEnv(flags.EnvFiles, flags.Env)
if err != nil {
return err
}

stringPolicy := flags.Policy
if stringPolicy == "" {
stringPolicy = cfg.PullPolicy
}
pullPolicy, err := image.ParsePullPolicy(stringPolicy)
if err != nil {
return errors.Wrapf(err, "parsing pull policy %s", flags.Policy)
}
var lifecycleImage string
if flags.LifecycleImage != "" {
ref, err := name.ParseReference(flags.LifecycleImage)
if err != nil {
return errors.Wrapf(err, "parsing lifecycle image %s", flags.LifecycleImage)
}
lifecycleImage = ref.Name()
}

if err := packClient.Detect(cmd.Context(), client.BuildOptions{
AppPath: flags.AppPath,
Builder: builder,
Env: env,

PullPolicy: pullPolicy,
ContainerConfig: client.ContainerConfig{
Network: flags.Network,
Volumes: flags.Volumes,
},
LifecycleImage: lifecycleImage,
PreBuildpacks: flags.PreBuildpacks,
PostBuildpacks: flags.PostBuildpacks,

Buildpacks: buildpacks,
Extensions: extensions,
}); err != nil {
return errors.Wrap(err, "failed to detect")
}
return nil
}),
}
detectCommandFlags(cmd, &flags, cfg)
AddHelpFlag(cmd, "detect")
return cmd
}

// TODO: Incomplete
func validateDetectFlags(flags *DetectFlags, cfg config.Config, logger logging.Logger) error {
// Have to implement
return nil
}

// TODO: Incomplete
func detectCommandFlags(cmd *cobra.Command, detectFlags *DetectFlags, cfg config.Config) {
cmd.Flags().StringVarP(&detectFlags.AppPath, "path", "p", "", "Path to app dir or zip-formatted file (defaults to current working directory)")
cmd.Flags().StringSliceVarP(&detectFlags.Buildpacks, "buildpack", "b", nil, "Buildpack to use. One of:\n a buildpack by id and version in the form of '<buildpack>@<version>',\n path to a buildpack directory (not supported on Windows),\n path/URL to a buildpack .tar or .tgz file, or\n a packaged buildpack image name in the form of '<hostname>/<repo>[:<tag>]'"+stringSliceHelp("buildpack"))
cmd.Flags().StringSliceVarP(&detectFlags.Extensions, "extension", "", nil, "Extension to use. One of:\n an extension by id and version in the form of '<extension>@<version>',\n path to an extension directory (not supported on Windows),\n path/URL to an extension .tar or .tgz file, or\n a packaged extension image name in the form of '<hostname>/<repo>[:<tag>]'"+stringSliceHelp("extension"))
cmd.Flags().StringVarP(&detectFlags.Builder, "builder", "B", cfg.DefaultBuilder, "Builder image")
cmd.Flags().StringArrayVarP(&detectFlags.Env, "env", "e", []string{}, "Build-time environment variable, in the form 'VAR=VALUE' or 'VAR'.\nWhen using latter value-less form, value will be taken from current\n environment at the time this command is executed.\nThis flag may be specified multiple times and will override\n individual values defined by --env-file."+stringArrayHelp("env")+"\nNOTE: These are NOT available at image runtime.")
cmd.Flags().StringArrayVar(&detectFlags.EnvFiles, "env-file", []string{}, "Build-time environment variables file\nOne variable per line, of the form 'VAR=VALUE' or 'VAR'\nWhen using latter value-less form, value will be taken from current\n environment at the time this command is executed\nNOTE: These are NOT available at image runtime.\"")
cmd.Flags().StringVar(&detectFlags.Network, "network", "", "Connect detect and build containers to network")
cmd.Flags().StringVar(&detectFlags.Policy, "pull-policy", "", `Pull policy to use. Accepted values are always, never, and if-not-present. (default "always")`)
// cmd.Flags().StringVarP(&detectFlags.DescriptorPath, "descriptor", "d", "", "Path to the project descriptor file")
cmd.Flags().StringVar(&detectFlags.LifecycleImage, "lifecycle-image", cfg.LifecycleImage, `Custom lifecycle image to use for analysis, restore, and export when builder is untrusted.`)
cmd.Flags().StringArrayVar(&detectFlags.Volumes, "volume", nil, "Mount host volume into the build container, in the form '<host path>:<target path>[:<options>]'.\n- 'host path': Name of the volume or absolute directory path to mount.\n- 'target path': The path where the file or directory is available in the container.\n- 'options' (default \"ro\"): An optional comma separated list of mount options.\n - \"ro\", volume contents are read-only.\n - \"rw\", volume contents are readable and writeable.\n - \"volume-opt=<key>=<value>\", can be specified more than once, takes a key-value pair consisting of the option name and its value."+stringArrayHelp("volume"))
cmd.Flags().StringArrayVar(&detectFlags.PreBuildpacks, "pre-buildpack", []string{}, "Buildpacks to prepend to the groups in the builder's order")
cmd.Flags().StringArrayVar(&detectFlags.PostBuildpacks, "post-buildpack", []string{}, "Buildpacks to append to the groups in the builder's order")
}
14 changes: 14 additions & 0 deletions internal/commands/testmocks/mock_pack_client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions internal/fakes/fake_lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ func (f *FakeLifecycle) Execute(ctx context.Context, opts build.LifecycleOptions
f.Opts = opts
return nil
}

func (f *FakeLifecycle) Detect(ctx context.Context, opts build.LifecycleOptions) error {
f.Opts = opts
return nil
}
1 change: 1 addition & 0 deletions pkg/client/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ type LifecycleExecutor interface {
// Execute is responsible for invoking each of these binaries
// with the desired configuration.
Execute(ctx context.Context, opts build.LifecycleOptions) error
Detect(ctx context.Context, opts build.LifecycleOptions) error
}

type IsTrustedBuilder func(string) bool
Expand Down
Loading
Loading