diff --git a/dev-tools/mage/checksums.go b/dev-tools/mage/checksums.go index 9cd4cef0ce2..d4fbfc55808 100644 --- a/dev-tools/mage/checksums.go +++ b/dev-tools/mage/checksums.go @@ -15,6 +15,7 @@ import ( "github.com/otiai10/copy" "github.com/elastic/elastic-agent/dev-tools/mage/manifest" + "github.com/elastic/elastic-agent/dev-tools/packaging" ) const ComponentSpecFileSuffix = ".spec.yml" @@ -94,157 +95,76 @@ func ChecksumsWithoutManifest(versionedFlatPath string, versionedDropPath string } // This is a helper function for flattenDependencies that's used when building from a manifest -func ChecksumsWithManifest(requiredPackage string, versionedFlatPath string, versionedDropPath string, manifestResponse *manifest.Build) map[string]string { +func ChecksumsWithManifest(platform, dependenciesVersion string, versionedFlatPath string, versionedDropPath string, manifestResponse *manifest.Build) map[string]string { checksums := make(map[string]string) if manifestResponse == nil { return checksums } - // Iterate over the component projects in the manifest - projects := manifestResponse.Projects - for componentName := range projects { - // Iterate over the individual package files within each component project - for pkgName := range projects[componentName].Packages { - // Only care about packages that match the required package constraint (os/arch) - if strings.Contains(pkgName, requiredPackage) { - // Iterate over the external binaries that we care about for packaging agent - for _, spec := range manifest.ExpectedBinaries { - // If the individual package doesn't match the expected prefix, then continue - // FIXME temporarily skip fips packages until elastic-agent FIPS is in place - if !strings.HasPrefix(pkgName, spec.BinaryName) || strings.Contains(pkgName, "-fips-") { - if mg.Verbose() { - log.Printf(">>>>>>> Package [%s] skipped", pkgName) - } - continue - } - - if mg.Verbose() { - log.Printf(">>>>>>> Package [%s] matches requiredPackage [%s]", pkgName, requiredPackage) - } - - // Get the version from the component based on the version in the package name - // This is useful in the case where it's an Independent Agent Release, where - // the opted-in projects will be one patch version ahead of the rest of the - // opted-out/previously-released projects - componentVersion := getComponentVersion(componentName, requiredPackage, projects[componentName]) - if mg.Verbose() { - log.Printf(">>>>>>> Component [%s]/[%s] version is [%s]", componentName, requiredPackage, componentVersion) - } - - // Combine the package name w/ the versioned flat path - fullPath := filepath.Join(versionedFlatPath, pkgName) - - // Eliminate the file extensions to get the proper directory - // name that we need to copy - var dirToCopy string - if strings.HasSuffix(fullPath, ".tar.gz") { - dirToCopy = fullPath[:strings.LastIndex(fullPath, ".tar.gz")] - } else if strings.HasSuffix(fullPath, ".zip") { - dirToCopy = fullPath[:strings.LastIndex(fullPath, ".zip")] - } else { - dirToCopy = fullPath - } - if mg.Verbose() { - log.Printf(">>>>>>> Calculated directory to copy: [%s]", dirToCopy) - } - - // Set copy options - options := copy.Options{ - OnSymlink: func(_ string) copy.SymlinkAction { - return copy.Shallow - }, - Sync: true, - } - if mg.Verbose() { - log.Printf("> prepare to copy %s into %s ", dirToCopy, versionedDropPath) - } - - // Do the copy - err := copy.Copy(dirToCopy, versionedDropPath, options) - if err != nil { - panic(err) - } - - // copy spec file for match - specName := filepath.Base(dirToCopy) - idx := strings.Index(specName, "-"+componentVersion) - if idx != -1 { - specName = specName[:idx] - } - if mg.Verbose() { - log.Printf(">>>> Looking to copy spec file: [%s]", specName) - } - - checksum, err := CopyComponentSpecs(specName, versionedDropPath) - if err != nil { - panic(err) - } - - checksums[specName+ComponentSpecFileSuffix] = checksum - } + // Iterate over the external binaries that we care about for packaging agent + for _, spec := range packaging.ExpectedBinaries { + + if spec.PythonWheel { + if mg.Verbose() { + log.Printf(">>>>>>> Component %s/%s is a Python wheel, skipping", spec.ProjectName, spec.BinaryName) } + continue } - } - return checksums -} + if !spec.SupportsPlatform(platform) { + log.Printf(">>>>>>> Component %s/%s does not support platform %s, skipping", spec.ProjectName, spec.BinaryName, platform) + continue + } -// This function is used when building with a Manifest. In that manifest, it's possible -// for projects in an Independent Agent Release to have different versions since the opted-in -// ones will be one patch version higher than the opted-out/previously released projects. -// This function tries to find the versions from the package name -func getComponentVersion(componentName string, requiredPackage string, componentProject manifest.Project) string { - var componentVersion string - var foundIt bool - // Iterate over all the packages in the component project - for pkgName := range componentProject.Packages { - // Only care about the external binaries that we want to package - for _, spec := range manifest.ExpectedBinaries { - // If the given component name doesn't match the external binary component, skip - // FIXME temporarily skip fips packages until elastic-agent FIPS is in place - if componentName != spec.ProjectName || strings.Contains(pkgName, "-fips-") { - continue + manifestPackage, err := manifest.ResolveManifestPackage(manifestResponse.Projects[spec.ProjectName], spec, dependenciesVersion, platform) + if err != nil { + if mg.Verbose() { + log.Printf(">>>>>>> Error resolving package for [%s/%s]", spec.BinaryName, platform) } + continue + } - // Split the package name on the binary name prefix plus a dash - firstSplit := strings.Split(pkgName, spec.BinaryName+"-") - if len(firstSplit) < 2 { - continue - } + // Combine the package name w/ the versioned flat path + fullPath := filepath.Join(versionedFlatPath, manifestPackage.Name) + + // Eliminate the file extensions to get the proper directory + // name that we need to copy + var dirToCopy string + if strings.HasSuffix(fullPath, ".tar.gz") { + dirToCopy = fullPath[:strings.LastIndex(fullPath, ".tar.gz")] + } else if strings.HasSuffix(fullPath, ".zip") { + dirToCopy = fullPath[:strings.LastIndex(fullPath, ".zip")] + } else { + dirToCopy = fullPath + } + if mg.Verbose() { + log.Printf(">>>>>>> Calculated directory to copy: [%s]", dirToCopy) + } - // Get the second part of the first split - secondHalf := firstSplit[1] - if len(secondHalf) < 2 { - continue - } + // Set copy options + options := copy.Options{ + OnSymlink: func(_ string) copy.SymlinkAction { + return copy.Shallow + }, + Sync: true, + } + if mg.Verbose() { + log.Printf("> prepare to copy %s into %s ", dirToCopy, versionedDropPath) + } - // Make sure the second half matches the required package - if strings.Contains(secondHalf, requiredPackage) { - // ignore packages with names where this splitting doesn't results in proper version - if strings.Contains(secondHalf, "docker-image") { - continue - } - if strings.Contains(secondHalf, "oss-") { - continue - } - - // The component version should be the first entry after splitting w/ the requiredPackage - componentVersion = strings.Split(secondHalf, "-"+requiredPackage)[0] - foundIt = true - // break out of inner loop - break - } + // Do the copy + err = copy.Copy(dirToCopy, versionedDropPath, options) + if err != nil { + panic(err) } - if foundIt { - // break out of outer loop - break + + checksum, err := CopyComponentSpecs(spec.BinaryName, versionedDropPath) + if err != nil { + panic(err) } - } - if componentVersion == "" { - errMsg := fmt.Sprintf("Unable to determine component version for [%s]", componentName) - panic(errMsg) + checksums[spec.BinaryName+ComponentSpecFileSuffix] = checksum } - return componentVersion + return checksums } diff --git a/dev-tools/mage/manifest/manifest.go b/dev-tools/mage/manifest/manifest.go index 11659614f67..8557e7bd2bc 100644 --- a/dev-tools/mage/manifest/manifest.go +++ b/dev-tools/mage/manifest/manifest.go @@ -13,13 +13,14 @@ import ( "os" "path" "path/filepath" + "regexp" "strings" "time" "github.com/magefile/mage/mg" "golang.org/x/sync/errgroup" - "github.com/elastic/elastic-agent/dev-tools/mage/pkgcommon" + "github.com/elastic/elastic-agent/dev-tools/packaging" "github.com/elastic/elastic-agent/pkg/version" ) @@ -94,72 +95,6 @@ var PlatformPackages = map[string]string{ "windows/amd64": "windows-x86_64.zip", } -// ExpectedBinaries is a map of binaries agent needs to their project in the unified-release manager. -// The project names are those used in the "projects" list in the unified release manifest. -// See the sample manifests in the testdata directory. -var ExpectedBinaries = []BinarySpec{ - {BinaryName: "agentbeat", ProjectName: "beats", Platforms: AllPlatforms, PackageTypes: pkgcommon.AllPackageTypes}, - {BinaryName: "apm-server", ProjectName: "apm-server", Platforms: []Platform{{"linux", "x86_64"}, {"linux", "arm64"}, {"windows", "x86_64"}, {"darwin", "x86_64"}}, PackageTypes: pkgcommon.AllPackageTypes}, - {BinaryName: "cloudbeat", ProjectName: "cloudbeat", Platforms: []Platform{{"linux", "x86_64"}, {"linux", "arm64"}}, PackageTypes: pkgcommon.AllPackageTypes}, - {BinaryName: "connectors", ProjectName: "connectors", Platforms: []Platform{{"linux", "x86_64"}, {"linux", "arm64"}}, PythonWheel: true, PackageTypes: pkgcommon.AllPackageTypes}, - {BinaryName: "endpoint-security", ProjectName: "endpoint-dev", Platforms: AllPlatforms, PackageTypes: []pkgcommon.PackageType{pkgcommon.RPM, pkgcommon.Deb, pkgcommon.Zip, pkgcommon.TarGz}}, - {BinaryName: "fleet-server", ProjectName: "fleet-server", Platforms: AllPlatforms, PackageTypes: pkgcommon.AllPackageTypes}, - {BinaryName: "pf-elastic-collector", ProjectName: "prodfiler", Platforms: []Platform{{"linux", "x86_64"}, {"linux", "arm64"}}, PackageTypes: pkgcommon.AllPackageTypes}, - {BinaryName: "pf-elastic-symbolizer", ProjectName: "prodfiler", Platforms: []Platform{{"linux", "x86_64"}, {"linux", "arm64"}}, PackageTypes: pkgcommon.AllPackageTypes}, - {BinaryName: "pf-host-agent", ProjectName: "prodfiler", Platforms: []Platform{{"linux", "x86_64"}, {"linux", "arm64"}}, PackageTypes: pkgcommon.AllPackageTypes}, -} - -type BinarySpec struct { - BinaryName string - ProjectName string - Platforms []Platform - PythonWheel bool - PackageTypes []pkgcommon.PackageType -} - -func (proj BinarySpec) SupportsPlatform(platform string) bool { - for _, p := range proj.Platforms { - if p.Platform() == platform { - return true - } - } - return false -} - -func (proj BinarySpec) SupportsPackageType(pkgType pkgcommon.PackageType) bool { - for _, p := range proj.PackageTypes { - if p == pkgType { - return true - } - } - return false -} - -func (proj BinarySpec) GetPackageName(version string, platform string) string { - if proj.PythonWheel { - return fmt.Sprintf("%s-%s.zip", proj.BinaryName, version) - } - return fmt.Sprintf("%s-%s-%s", proj.BinaryName, version, PlatformPackages[platform]) -} - -type Platform struct { - OS string - Arch string -} - -// Converts to the format expected on the mage command line "linux", "x86_64" = "linux/amd64" -func (p Platform) Platform() string { - if p.Arch == "x86_64" { - p.Arch = "amd64" - } - if p.Arch == "aarch64" { - p.Arch = "arm64" - } - return p.OS + "/" + p.Arch -} - -var AllPlatforms = []Platform{{"linux", "x86_64"}, {"linux", "arm64"}, {"windows", "x86_64"}, {"darwin", "x86_64"}, {"darwin", "aarch64"}} - // DownloadManifest is going to download the given manifest file and return the ManifestResponse func DownloadManifest(ctx context.Context, manifest string) (Build, error) { manifestUrl, urlError := url.Parse(manifest) @@ -210,7 +145,7 @@ func DownloadComponents(ctx context.Context, manifest string, platforms []string errGrp, downloadsCtx := errgroup.WithContext(ctx) // for project, pkgs := range expectedProjectPkgs() { - for _, spec := range ExpectedBinaries { + for _, spec := range packaging.ExpectedBinaries { for _, platform := range platforms { targetPath := filepath.Join(dropPath) err := os.MkdirAll(targetPath, 0755) @@ -224,12 +159,12 @@ func DownloadComponents(ctx context.Context, manifest string, platforms []string continue } - pkgURL, err := resolveManifestPackage(projects[spec.ProjectName], spec, majorMinorPatchVersion, platform) + resolvedPackage, err := ResolveManifestPackage(projects[spec.ProjectName], spec, majorMinorPatchVersion, platform) if err != nil { return err } - for _, p := range pkgURL { + for _, p := range resolvedPackage.URLs { log.Printf(">>>>>>>>> Downloading [%s] [%s] ", spec.BinaryName, p) pkgFilename := path.Base(p) downloadTarget := filepath.Join(targetPath, pkgFilename) @@ -251,76 +186,114 @@ func DownloadComponents(ctx context.Context, manifest string, platforms []string return nil } -func resolveManifestPackage(project Project, spec BinarySpec, version string, platform string) ([]string, error) { - var val Package - var ok bool +type ResolvedPackage struct { + Name string + URLs []string +} + +func ResolveManifestPackage(project Project, spec packaging.BinarySpec, dependencyVersion string, platform string) (*ResolvedPackage, error) { // Try the normal/easy case first - packageName := spec.GetPackageName(version, platform) - val, ok = project.Packages[packageName] - if !ok { - // If we didn't find it, it may be an Independent Agent Release, where - // the opted-in projects will have a patch version one higher than - // the rest of the projects, so we need to seek that out + packageName := spec.GetPackageName(dependencyVersion, platform) + if mg.Verbose() { + log.Printf(">>>>>>>>>>> Got packagename [%s], looking for exact match", packageName) + } + + if exactMatch, ok := project.Packages[packageName]; ok { + // We found the exact filename we are looking for if mg.Verbose() { - log.Printf(">>>>>>>>>>> Looking for package [%s] of type [%s]", spec.BinaryName, PlatformPackages[platform]) + log.Printf(">>>>>>>>>>> Found exact match packageName for [%s, %s]: %s", project.Branch, project.CommitHash, exactMatch) } - var foundIt bool - for pkgName := range project.Packages { - if strings.HasPrefix(pkgName, spec.BinaryName) { - firstSplit := strings.Split(pkgName, spec.BinaryName+"-") - if len(firstSplit) < 2 { - continue - } + return &ResolvedPackage{ + Name: packageName, + URLs: []string{exactMatch.URL, exactMatch.ShaURL, exactMatch.AscURL}, + }, nil + } - secondHalf := firstSplit[1] - // Make sure we're finding one w/ the same required package type - if strings.Contains(secondHalf, PlatformPackages[platform]) { - - // Split again after the version with the required package string - secondSplit := strings.Split(secondHalf, "-"+PlatformPackages[platform]) - if len(secondSplit) < 2 { - continue - } - - // The first element after the split should normally be the version - pkgVersion := secondSplit[0] - if mg.Verbose() { - log.Printf(">>>>>>>>>>> Using derived version for package [%s]: %s ", pkgName, pkgVersion) - } - - // Create a project/package key with the package, derived version, and required package - foundPkgKey := fmt.Sprintf("%s-%s-%s", spec.BinaryName, pkgVersion, PlatformPackages[platform]) - if mg.Verbose() { - log.Printf(">>>>>>>>>>> Looking for project package key: [%s]", foundPkgKey) - } - - // Get the package value, if it exists - val, ok = project.Packages[foundPkgKey] - if !ok { - continue - } - - if mg.Verbose() { - log.Printf(">>>>>>>>>>> Found package key [%s]", foundPkgKey) - } - - foundIt = true - } - } - } + // If we didn't find it, it may be an Independent Agent Release, where + // the opted-in projects will have a patch version one higher than + // the rest of the projects, so we "relax" the version constraint - if !foundIt { - return nil, fmt.Errorf("package [%s] not found in project manifest at %s", packageName, project.ExternalArtifactsManifestURL) - } + // Find the original version in the filename + versionIndex := strings.Index(packageName, dependencyVersion) + if versionIndex == -1 { + return nil, fmt.Errorf("no exact match and filename %q does not seem to contain dependencyVersion %q to try a fallback", packageName, dependencyVersion) + } + + // TODO move relaxVersion to the version package so we can rewrite the version like so + //parseVersion, _ := version.ParseVersion(dependencyVersion) + //parseVersion.GetRelaxedPatchRegexp() + relaxedVersion, err := relaxVersion(dependencyVersion) + if err != nil { + return nil, fmt.Errorf("relaxing dependencyVersion %q: %w", dependencyVersion, err) } if mg.Verbose() { - log.Printf(">>>>>>>>>>> Project branch/commit [%s, %s]", project.Branch, project.CommitHash) + log.Printf(">>>>>>>>>>> Couldn't find exact match, relaxing agent dependencyVersion to %s", relaxedVersion) + } + + // locate the original version in the filename and substitute the relaxed version regexp, quoting everything around that + relaxedPackageName := regexp.QuoteMeta(packageName[:versionIndex]) + relaxedPackageName += relaxedVersion + relaxedPackageName += regexp.QuoteMeta(packageName[versionIndex+len(dependencyVersion):]) + + if mg.Verbose() { + log.Printf(">>>>>>>>>>> Attempting to match a filename with %s", relaxedPackageName) + } + + relaxedPackageNameRegexp, err := regexp.Compile(relaxedPackageName) + if err != nil { + return nil, fmt.Errorf("compiling relaxed package name regex %q: %w", relaxedPackageName, err) + } + + for pkgName, pkg := range project.Packages { + if mg.Verbose() { + log.Printf(">>>>>>>>>>> Evaluating filename %s", pkgName) + } + if relaxedPackageNameRegexp.MatchString(pkgName) { + if mg.Verbose() { + log.Printf(">>>>>>>>>>> Found matching packageName for [%s, %s]: %s", project.Branch, project.CommitHash, pkgName) + } + return &ResolvedPackage{ + Name: pkgName, + URLs: []string{pkg.URL, pkg.ShaURL, pkg.AscURL}, + }, nil + } } - return []string{val.URL, val.ShaURL, val.AscURL}, nil + return nil, fmt.Errorf("package [%s] not found in project manifest at %s", packageName, project.ExternalArtifactsManifestURL) +} + +// versionRegexp is taken from https://semver.org/ (see the FAQ section/Is there a suggested regular expression (RegEx) to check a SemVer string?) +const versionRegexp = `^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(0|[1-9]\d*)(?:-(?:(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?:[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$` +const anyPatchVersionRegexp = `(?:0|[1-9]\d*)` + +var versionRegExp = regexp.MustCompile(versionRegexp) + +func relaxVersion(version string) (string, error) { + matchIndices := versionRegExp.FindSubmatchIndex([]byte(version)) + // Matches index pairs are (0,1) for the whole regexp and (2,3) for the patch group + // check that we have matched correctly + if len(matchIndices) < 4 { + return "", fmt.Errorf("failed to match regexp for version [%s]", version) + } + + // take the starting index of the patch version + patchStartIndex := matchIndices[2] + // copy everything before the patch version escaping the regexp + relaxedVersion := regexp.QuoteMeta(version[:patchStartIndex]) + // add the patch regexp + relaxedVersion += anyPatchVersionRegexp + // check if there's more characters after the patch version + remainderIndex := matchIndices[3] + if remainderIndex < len(version) { + // This is a looser regexp that allows anything beyond the major version to change (while still enforcing a valid patch version though) + // see TestResolveManifestPackage/Independent_Agent_Staging_8.14_apm-server and TestResolveManifestPackage/Independent_Agent_Staging_8.14_endpoint-dev + // Be more relaxed and allow for any character sequence after this + relaxedVersion += `.*` + } + return relaxedVersion, nil } func DownloadPackage(ctx context.Context, downloadUrl string, target string) error { diff --git a/dev-tools/mage/manifest/manifest_test.go b/dev-tools/mage/manifest/manifest_test.go index b975149aa27..0d971edf69b 100644 --- a/dev-tools/mage/manifest/manifest_test.go +++ b/dev-tools/mage/manifest/manifest_test.go @@ -7,12 +7,15 @@ package manifest import ( _ "embed" "encoding/json" + "fmt" "log" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/elastic/elastic-agent/dev-tools/packaging" ) var ( @@ -143,22 +146,77 @@ func TestResolveManifestPackage(t *testing.T) { return } - urlList, err := resolveManifestPackage(projects[tc.projectName], spec, manifestJson.Version, tc.platform) + resolvedPackage, err := ResolveManifestPackage(projects[tc.projectName], spec, manifestJson.Version, tc.platform) require.NoError(t, err) + require.NotNil(t, resolvedPackage) - assert.Len(t, urlList, 3) - for _, url := range urlList { + assert.Len(t, resolvedPackage.URLs, 3) + for _, url := range resolvedPackage.URLs { assert.Contains(t, tc.expectedUrlList, url) } }) } } -func findBinarySpec(name string) (BinarySpec, bool) { - for _, spec := range ExpectedBinaries { +func findBinarySpec(name string) (packaging.BinarySpec, bool) { + for _, spec := range packaging.ExpectedBinaries { if spec.BinaryName == name { return spec, true } } - return BinarySpec{}, false + return packaging.BinarySpec{}, false +} + +func TestRelaxVersion(t *testing.T) { + type args struct { + version string + } + tests := []struct { + name string + args args + want string + wantErr assert.ErrorAssertionFunc + }{ + { + name: "major-minor-patch", + args: args{ + version: "1.2.3", + }, + want: `1\.2\.(?:0|[1-9]\d*)`, + wantErr: assert.NoError, + }, + { + name: "major-minor-patch-snapshot", + args: args{ + version: "1.2.3-SNAPSHOT", + }, + want: `1\.2\.(?:0|[1-9]\d*).*`, + wantErr: assert.NoError, + }, + { + name: "major-minor-patch-snapshot-buildmeta", + args: args{ + version: "1.2.3-SNAPSHOT+build20250328112233", + }, + want: `1\.2\.(?:0|[1-9]\d*).*`, + wantErr: assert.NoError, + }, + { + name: "not a semver", + args: args{ + version: "foobar", + }, + want: "", + wantErr: assert.Error, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := relaxVersion(tt.args.version) + if !tt.wantErr(t, err, fmt.Sprintf("relaxVersion(%v)", tt.args.version)) { + return + } + assert.Equalf(t, tt.want, got, "relaxVersion(%v)", tt.args.version) + }) + } } diff --git a/dev-tools/mage/pkg.go b/dev-tools/mage/pkg.go index 6ec09f2e598..ed93e86344e 100644 --- a/dev-tools/mage/pkg.go +++ b/dev-tools/mage/pkg.go @@ -222,7 +222,7 @@ func TestPackages(options ...TestPackagesOption) error { args = append(args, "-v") } - args = append(args, MustExpand("{{ elastic_beats_dir }}/dev-tools/packaging/package_test.go")) + args = append(args, MustExpand("{{ elastic_beats_dir }}/dev-tools/packaging/testing/package_test.go")) if params.HasModules { args = append(args, "--modules") diff --git a/dev-tools/packaging/packages.yml b/dev-tools/packaging/packages.yml index 5254991306e..ab77bb2c3ab 100644 --- a/dev-tools/packaging/packages.yml +++ b/dev-tools/packaging/packages.yml @@ -584,6 +584,115 @@ shared: source: '{{ repo.RootDir }}/dev-tools/licenses/ELASTIC-LICENSE-2.0.txt' mode: 0644 +platforms: &all-platforms + - &linux-amd64 + os: linux + arch: x86_64 + - &linux-arm64 + os: linux + arch: arm64 + - &windows-amd64 + os: windows + arch: x86_64 + - &darwin-amd64 + os: darwin + arch: x86_64 + - &darwin-arm64 + os: darwin + arch: aarch64 + +packageTypes: &all-package-types + - &pkg-type-rpm + 1 # RPM + - &pkg-type-deb + 2 # Deb + - &pkg-type-zip + 3 # zip + - &pkg-type-targz + 4 # tar.gz + - &pkg-type-docker + 5 # docker + +components: + # general template for new components +# - &comp- +# projectName: +# packageName: