This is an automated email from the ASF dual-hosted git repository. nferraro pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel-k.git
commit 552e6a8cddd5e4d2a293e6333c0171fbf4f881b0 Author: Doru Bercea <gheorghe-teod.ber...@ibm.com> AuthorDate: Thu Oct 8 18:41:04 2020 -0400 Support computing and outputting of transitive dependencies. --- pkg/builder/builder_steps.go | 47 +++++++----- pkg/builder/runtime/main.go | 65 ++++++++++++---- pkg/cmd/inspect.go | 175 ++++++++++++++++++++++++++++++++++++++++--- pkg/util/camel/catalog.go | 6 +- pkg/util/util.go | 18 +++++ 5 files changed, 265 insertions(+), 46 deletions(-) diff --git a/pkg/builder/builder_steps.go b/pkg/builder/builder_steps.go index a1ce547..0c29790 100644 --- a/pkg/builder/builder_steps.go +++ b/pkg/builder/builder_steps.go @@ -25,6 +25,7 @@ import ( "reflect" "strings" + "github.com/apache/camel-k/pkg/util/camel" "github.com/apache/camel-k/pkg/util/jitpack" "github.com/rs/xid" @@ -140,7 +141,17 @@ func generateProjectSettings(ctx *Context) error { func injectDependencies(ctx *Context) error { // Add dependencies from build - for _, d := range ctx.Build.Dependencies { + return InjectDependenciesCommon(&ctx.Maven.Project, ctx.Build.Dependencies, ctx.Catalog) +} + +// InjectDependenciesCommon -- +func InjectDependenciesCommon( + project *maven.Project, + dependencies []string, + catalog *camel.RuntimeCatalog) error { + + // Add dependencies from build + for _, d := range dependencies { switch { case strings.HasPrefix(d, "bom:"): mid := strings.TrimPrefix(d, "bom:") @@ -151,7 +162,7 @@ func injectDependencies(ctx *Context) error { return err } - ctx.Maven.Project.DependencyManagement.Dependencies = append(ctx.Maven.Project.DependencyManagement.Dependencies, maven.Dependency{ + project.DependencyManagement.Dependencies = append(project.DependencyManagement.Dependencies, maven.Dependency{ GroupID: d.GroupID, ArtifactID: d.ArtifactID, Version: d.Version, @@ -165,7 +176,7 @@ func injectDependencies(ctx *Context) error { artifactID = "camel-" + artifactID } - ctx.Maven.Project.AddDependencyGAV("org.apache.camel", artifactID, "") + project.AddDependencyGAV("org.apache.camel", artifactID, "") case strings.HasPrefix(d, "camel-k:"): artifactID := strings.TrimPrefix(d, "camel-k:") @@ -173,7 +184,7 @@ func injectDependencies(ctx *Context) error { artifactID = "camel-k-" + artifactID } - ctx.Maven.Project.AddDependencyGAV("org.apache.camel.k", artifactID, "") + project.AddDependencyGAV("org.apache.camel.k", artifactID, "") case strings.HasPrefix(d, "camel-quarkus:"): artifactID := strings.TrimPrefix(d, "camel-quarkus:") @@ -181,25 +192,25 @@ func injectDependencies(ctx *Context) error { artifactID = "camel-quarkus-" + artifactID } - ctx.Maven.Project.AddDependencyGAV("org.apache.camel.quarkus", artifactID, "") + project.AddDependencyGAV("org.apache.camel.quarkus", artifactID, "") case strings.HasPrefix(d, "mvn:"): mid := strings.TrimPrefix(d, "mvn:") gav := strings.Replace(mid, "/", ":", -1) - ctx.Maven.Project.AddEncodedDependencyGAV(gav) + project.AddEncodedDependencyGAV(gav) default: if dep := jitpack.ToDependency(d); dep != nil { - ctx.Maven.Project.AddDependency(*dep) + project.AddDependency(*dep) addRepo := true - for _, repo := range ctx.Maven.Project.Repositories { + for _, repo := range project.Repositories { if repo.URL == jitpack.RepoURL { addRepo = false break } } if addRepo { - ctx.Maven.Project.Repositories = append(ctx.Maven.Project.Repositories, maven.Repository{ + project.Repositories = append(project.Repositories, maven.Repository{ ID: "jitpack.io-" + xid.New().String(), URL: jitpack.RepoURL, Releases: maven.RepositoryPolicy{ @@ -219,18 +230,18 @@ func injectDependencies(ctx *Context) error { } // Add dependencies from catalog - deps := make([]maven.Dependency, len(ctx.Maven.Project.Dependencies)) - copy(deps, ctx.Maven.Project.Dependencies) + deps := make([]maven.Dependency, len(project.Dependencies)) + copy(deps, project.Dependencies) for _, d := range deps { - if a, ok := ctx.Catalog.Artifacts[d.ArtifactID]; ok { + if a, ok := catalog.Artifacts[d.ArtifactID]; ok { for _, dep := range a.Dependencies { md := maven.Dependency{ GroupID: dep.GroupID, ArtifactID: dep.ArtifactID, } - ctx.Maven.Project.AddDependency(md) + project.AddDependency(md) for _, e := range dep.Exclusions { me := maven.Exclusion{ @@ -238,18 +249,18 @@ func injectDependencies(ctx *Context) error { ArtifactID: e.ArtifactID, } - ctx.Maven.Project.AddDependencyExclusion(md, me) + project.AddDependencyExclusion(md, me) } } } } // Post process dependencies - deps = make([]maven.Dependency, len(ctx.Maven.Project.Dependencies)) - copy(deps, ctx.Maven.Project.Dependencies) + deps = make([]maven.Dependency, len(project.Dependencies)) + copy(deps, project.Dependencies) for _, d := range deps { - if a, ok := ctx.Catalog.Artifacts[d.ArtifactID]; ok { + if a, ok := catalog.Artifacts[d.ArtifactID]; ok { md := maven.Dependency{ GroupID: a.GroupID, ArtifactID: a.ArtifactID, @@ -261,7 +272,7 @@ func injectDependencies(ctx *Context) error { ArtifactID: e.ArtifactID, } - ctx.Maven.Project.AddDependencyExclusion(md, me) + project.AddDependencyExclusion(md, me) } } } diff --git a/pkg/builder/runtime/main.go b/pkg/builder/runtime/main.go index 991f548..d6f973a 100644 --- a/pkg/builder/runtime/main.go +++ b/pkg/builder/runtime/main.go @@ -60,32 +60,39 @@ func loadCamelCatalog(ctx *builder.Context) error { } func generateProject(ctx *builder.Context) error { - p := maven.NewProjectWithGAV("org.apache.camel.k.integration", "camel-k-integration", defaults.Version) + p := GenerateProjectCommon(ctx.Build.Runtime.Metadata["camel.version"], ctx.Build.Runtime.Version) // Add all the properties from the build configuration p.Properties.AddAll(ctx.Build.Properties) + ctx.Maven.Project = p + + return nil +} + +// GenerateProjectCommon -- +func GenerateProjectCommon(camelVersion string, runtimeVersion string) maven.Project { + p := maven.NewProjectWithGAV("org.apache.camel.k.integration", "camel-k-integration", defaults.Version) + p.Dependencies = make([]maven.Dependency, 0) p.DependencyManagement = &maven.DependencyManagement{Dependencies: make([]maven.Dependency, 0)} p.DependencyManagement.Dependencies = append(p.DependencyManagement.Dependencies, maven.Dependency{ GroupID: "org.apache.camel", ArtifactID: "camel-bom", - Version: ctx.Build.Runtime.Metadata["camel.version"], + Version: camelVersion, Type: "pom", Scope: "import", }) p.DependencyManagement.Dependencies = append(p.DependencyManagement.Dependencies, maven.Dependency{ GroupID: "org.apache.camel.k", ArtifactID: "camel-k-runtime-bom", - Version: ctx.Build.Runtime.Version, + Version: runtimeVersion, Type: "pom", Scope: "import", }) - ctx.Maven.Project = p - - return nil + return p } func computeDependencies(ctx *builder.Context) error { @@ -93,30 +100,56 @@ func computeDependencies(ctx *builder.Context) error { mc.SettingsContent = ctx.Maven.SettingsData mc.LocalRepository = ctx.Build.Maven.LocalRepository mc.Timeout = ctx.Build.Maven.GetTimeout().Duration - mc.AddArgumentf("org.apache.camel.k:camel-k-maven-plugin:%s:generate-dependency-list", ctx.Catalog.Runtime.Version) + + // Compute dependencies. + content, err := ComputeDependenciesCommon(mc, ctx.Catalog.Runtime.Version) + if err != nil { + return err + } + + // Process artifacts list and add it to existing artifacts. + artifacts := []v1.Artifact{} + artifacts, err = ProcessTransitiveDependencies(content, "dependencies") + if err != nil { + return err + } + ctx.Artifacts = append(ctx.Artifacts, artifacts...) + + return nil +} + +// ComputeDependenciesCommon -- +func ComputeDependenciesCommon(mc maven.Context, runtimeVersion string) ([]byte, error) { + mc.AddArgumentf("org.apache.camel.k:camel-k-maven-plugin:%s:generate-dependency-list", runtimeVersion) if err := maven.Run(mc); err != nil { - return errors.Wrap(err, "failure while determining classpath") + return nil, errors.Wrap(err, "failure while determining classpath") } dependencies := path.Join(mc.Path, "target", "dependencies.yaml") content, err := ioutil.ReadFile(dependencies) if err != nil { - return err + return nil, err } + return content, nil +} + +// ProcessTransitiveDependencies -- +func ProcessTransitiveDependencies(content []byte, outputDir string) ([]v1.Artifact, error) { cp := make(map[string][]v1.Artifact) - err = yaml2.Unmarshal(content, &cp) + err := yaml2.Unmarshal(content, &cp) if err != nil { - return err + return nil, err } + artifacts := []v1.Artifact{} for _, e := range cp["dependencies"] { _, fileName := path.Split(e.Location) gav, err := maven.ParseGAV(e.ID) if err != nil { - return nil + return nil, err } // @@ -125,19 +158,19 @@ func computeDependencies(ctx *builder.Context) error { if e.Checksum == "" { chksum, err := digest.ComputeSHA1(e.Location) if err != nil { - return err + return nil, err } e.Checksum = "sha1:" + chksum } - ctx.Artifacts = append(ctx.Artifacts, v1.Artifact{ + artifacts = append(artifacts, v1.Artifact{ ID: e.ID, Location: e.Location, - Target: path.Join("dependencies", gav.GroupID+"."+fileName), + Target: path.Join(outputDir, gav.GroupID+"."+fileName), Checksum: e.Checksum, }) } - return nil + return artifacts, nil } diff --git a/pkg/cmd/inspect.go b/pkg/cmd/inspect.go index f899c95..b10d32a 100644 --- a/pkg/cmd/inspect.go +++ b/pkg/cmd/inspect.go @@ -20,9 +20,13 @@ package cmd import ( "errors" "fmt" + "os" "path" + "strings" v1 "github.com/apache/camel-k/pkg/apis/camel/v1" + "github.com/apache/camel-k/pkg/builder" + "github.com/apache/camel-k/pkg/builder/runtime" "github.com/apache/camel-k/pkg/trait" "github.com/apache/camel-k/pkg/util" "github.com/apache/camel-k/pkg/util/camel" @@ -32,6 +36,14 @@ import ( "github.com/spf13/cobra" ) +var acceptedDependencyTypes = []string{"bom", "camel", "camel-k", "camel-quarkus", "mvn", "github"} + +const ( + defaultRuntimeVersion = "1.3.0" + defaultCamelVersion = "3.3.0" + defaultWorkspaceDirectoryName = "workspace" +) + func newCmdInspect(rootCmdOptions *RootCmdOptions) (*cobra.Command, *inspectCmdOptions) { options := inspectCmdOptions{ RootCmdOptions: rootCmdOptions, @@ -40,7 +52,7 @@ func newCmdInspect(rootCmdOptions *RootCmdOptions) (*cobra.Command, *inspectCmdO cmd := cobra.Command{ Use: "inspect [files to inspect]", Short: "Generate dependencies list the given integration files.", - Long: `Compute and emit the dependencies for a list of integration files.`, + Long: "Output dependencies for a list of integration files. By default this command returns the top level dependencies only.", PreRunE: decode(&options), RunE: func(_ *cobra.Command, args []string) error { if err := options.validate(args); err != nil { @@ -57,14 +69,24 @@ func newCmdInspect(rootCmdOptions *RootCmdOptions) (*cobra.Command, *inspectCmdO }, } + cmd.Flags().Bool("all-dependencies", false, "Compute and output transitive dependencies.") + cmd.Flags().String("additional-dependencies", "", `Comma-separated list of additional top-level dependencies with the format: + <type>:<dependency-name> + where <type> is one of {`+strings.Join(acceptedDependencyTypes, "|")+`}.`) + cmd.Flags().String("workspace", "", "Absolute path to workspace directory. Default: <kamel-invocation-directory>/workspace") cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml") + cmd.Flags().String("dependencies-directory", "", "Absolute path to directory containing all dependencies. Default: <kamel-invocation-directory>/workspace/dependencies") return &cmd, &options } type inspectCmdOptions struct { *RootCmdOptions - OutputFormat string `mapstructure:"output"` + AllDependencies bool `mapstructure:"all-dependencies"` + OutputFormat string `mapstructure:"output"` + AdditionalDependencies string `mapstructure:"additional-dependencies"` + Workspace string `mapstructure:"workspace"` + DependenciesDirectory string `mapstructure:"dependencies-directory"` } func (command *inspectCmdOptions) validate(args []string) error { @@ -89,6 +111,41 @@ func (command *inspectCmdOptions) validate(args []string) error { } } + // Validate list of additional dependencies i.e. make sure that each dependency has + // a valid type. + if command.AdditionalDependencies != "" { + additionalDependencies := strings.Split(command.AdditionalDependencies, ",") + + for _, dependency := range additionalDependencies { + dependencyComponents := strings.Split(dependency, ":") + + TypeIsValid := false + for _, dependencyType := range acceptedDependencyTypes { + if dependencyType == dependencyComponents[0] { + TypeIsValid = true + } + } + + if !TypeIsValid { + return errors.New("Unexpected type for user-provided dependency: " + dependency + ", check command usage for valid format.") + } + } + } + + // If provided, ensure that that the dependencies directory exists. + if command.DependenciesDirectory != "" { + dependenciesDirectoryExists, err := util.DirectoryExists(command.DependenciesDirectory) + // Report any error. + if err != nil { + return err + } + + // Signal file not found. + if !dependenciesDirectoryExists { + return errors.New("input file " + command.DependenciesDirectory + " file does not exist") + } + } + return nil } @@ -107,6 +164,43 @@ func (command *inspectCmdOptions) run(args []string) error { } } + // Get top-level dependencies, this is the default behavior when no other options are provided. + dependencies, err := getTopLevelDependencies(catalog, command.OutputFormat, args) + if err != nil { + return err + } + + // Add additional user-provided dependencies. + if command.AdditionalDependencies != "" { + additionalDependencies := strings.Split(command.AdditionalDependencies, ",") + for _, dependency := range additionalDependencies { + dependencies = append(dependencies, dependency) + } + } + + // If --all-dependencies flag is set, add transitive dependencies. + if command.AllDependencies { + // Create workspace directory to hold all intermediate files. + workspaceDirectory, err := getWorkspaceDirectory(command) + if err != nil { + return err + } + + // The dependencies var will contain both top level and transitive dependencies. + dependencies, err = getTransitiveDependencies(catalog, dependencies, workspaceDirectory) + if err != nil { + return err + } + } + + for _, dep := range dependencies { + fmt.Printf("%v\n", dep) + } + + return nil +} + +func getTopLevelDependencies(catalog *camel.RuntimeCatalog, format string, args []string) ([]string, error) { // List of top-level dependencies. dependencies := strset.New() @@ -114,7 +208,7 @@ func (command *inspectCmdOptions) run(args []string) error { for _, source := range args { data, _, err := loadContent(source, false, false) if err != nil { - return err + return []string{}, err } sourceSpec := v1.SourceSpec{ @@ -129,10 +223,10 @@ func (command *inspectCmdOptions) run(args []string) error { dependencies.Merge(trait.AddSourceDependencies(sourceSpec, catalog)) } - if command.OutputFormat != "" { - err := printDependencies(command.OutputFormat, dependencies) + if format != "" { + err := printDependencies(format, dependencies) if err != nil { - return err + return []string{}, err } } else { // Print output in text form. @@ -140,8 +234,7 @@ func (command *inspectCmdOptions) run(args []string) error { fmt.Printf("%v\n", dep) } } - - return nil + return dependencies.List(), nil } func generateCatalog() (*camel.RuntimeCatalog, error) { @@ -155,7 +248,7 @@ func generateCatalog() (*camel.RuntimeCatalog, error) { Provider: v1.RuntimeProviderMain, } providerDependencies := []maven.Dependency{} - catalog, err := camel.GenerateLocalCatalog(settings, mvn, runtime, providerDependencies) + catalog, err := camel.GenerateCatalogCommon(settings, mvn, runtime, providerDependencies) if err != nil { return nil, err } @@ -182,3 +275,67 @@ func printDependencies(format string, dependecies *strset.Set) error { } return nil } + +func getTransitiveDependencies( + catalog *camel.RuntimeCatalog, + dependencies []string, + workspaceDirectory string) ([]string, error) { + + mvn := v1.MavenSpec{ + LocalRepository: "", + } + + // Create Maven project. + project := runtime.GenerateProjectCommon(defaultCamelVersion, defaultRuntimeVersion) + + // Inject dependencies into Maven project. + err := builder.InjectDependenciesCommon(&project, dependencies, catalog) + if err != nil { + return []string{}, err + } + + // Create local Maven context. + mc := maven.NewContext(path.Join(workspaceDirectory, "maven"), project) + mc.LocalRepository = mvn.LocalRepository + mc.Timeout = mvn.GetTimeout().Duration + + // Compute dependencies. + content, err := runtime.ComputeDependenciesCommon(mc, catalog.Runtime.Version) + if err != nil { + return nil, err + } + + // Compose artifcats list. + artifacts := []v1.Artifact{} + artifacts, err = runtime.ProcessTransitiveDependencies(content, "dependencies") + if err != nil { + return nil, err + } + + allDependencies := strset.New() + for _, entry := range artifacts { + allDependencies.Add(fmt.Sprintf("%s\n", entry.Location)) + } + + return allDependencies.List(), nil +} + +func getWorkspaceDirectory(command *inspectCmdOptions) (string, error) { + // Path to workspace directory. + workspaceDirectory := command.Workspace + if command.Workspace == "" { + currentDirectory, err := os.Getwd() + if err != nil { + return "", err + } + workspaceDirectory = path.Join(currentDirectory, defaultWorkspaceDirectoryName) + } + + // Create the workspace directory if it does not already exist. + err := util.CreateDirectory(workspaceDirectory) + if err != nil { + return "", err + } + + return workspaceDirectory, nil +} diff --git a/pkg/util/camel/catalog.go b/pkg/util/camel/catalog.go index d073251..2d3d5e9 100644 --- a/pkg/util/camel/catalog.go +++ b/pkg/util/camel/catalog.go @@ -85,11 +85,11 @@ func GenerateCatalog( return nil, err } - return GenerateLocalCatalog(settings, mvn, runtime, providerDependencies) + return GenerateCatalogCommon(settings, mvn, runtime, providerDependencies) } -// GenerateLocalCatalog -- -func GenerateLocalCatalog( +// GenerateCatalogCommon -- +func GenerateCatalogCommon( settings string, mvn v1.MavenSpec, runtime v1.RuntimeSpec, diff --git a/pkg/util/util.go b/pkg/util/util.go index 1ecff9b..30ff77b 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -247,6 +247,24 @@ func DirectoryExists(directory string) (bool, error) { return info.IsDir(), nil } +// CreateDirectory -- +func CreateDirectory(directory string) error { + // If directory does not exist, create it. + directoryExists, err := DirectoryExists(directory) + if err != nil { + return err + } + + if !directoryExists { + err := os.MkdirAll(directory, 0777) + if err != nil { + return err + } + } + + return nil +} + // BytesMarshaller -- type BytesMarshaller interface { MarshalBytes() ([]byte, error)