cli/bpmetadata/repo.go (114 lines of code) (raw):
package bpmetadata
import (
"errors"
"os"
"strings"
"github.com/GoogleCloudPlatform/cloud-foundation-toolkit/cli/util"
"github.com/iancoleman/strcase"
)
type repoDetail struct {
RepoName string
ModuleName string
Source *repoSource
}
type repoSource struct {
URL string
BlueprintRootPath string
RepoRootPath string
SourceType string
}
const (
nestedBpPath = "/modules"
)
// getRepoDetailsByPath takes a local path for a blueprint and tries
// to get repo details that include its name, path and type
func getRepoDetailsByPath(bpPath string, r *repoDetail, readme []byte) {
// For a submodule, we'll try to get repo details from the
// root blueprint or just return the current repoDetail object
// if it's still in memory.
if strings.Contains(bpPath, nestedBpPath) && r.Source != nil {
// try to parse the module name from MD which will get
// overridden with "["repoName-submoduleName" if repoName is available
r.ModuleName = parseRepoNameFromMd(readme)
if r.RepoName != "" {
r.ModuleName = r.RepoName + "-" + getBpSubmoduleNameInKebabCase(bpPath)
}
return
}
s := "git"
bpRootPath := getBlueprintRootPath(bpPath)
currentRootRepoDetails := getRepoDetailsFromRootBp(bpRootPath)
bpPath = strings.TrimSuffix(bpPath, "/")
repoUrl, repoRoot, err := util.GetRepoUrlAndRootPath(bpPath)
if err != nil {
repoUrl = ""
s = ""
}
if currentRootRepoDetails.Source.URL != "" {
repoUrl = currentRootRepoDetails.Source.URL
}
n, err := util.GetRepoName(repoUrl)
if err != nil {
n = parseRepoNameFromMd(readme)
}
*r = repoDetail{
RepoName: n,
ModuleName: n,
Source: &repoSource{
URL: repoUrl,
SourceType: s,
BlueprintRootPath: bpRootPath,
RepoRootPath: repoRoot,
},
}
}
// getRepoDetailsFromRootBp tries to parse repo details from the
// root blueprint metadata.yaml. This is specially useful when
// metadata is generated for a submodule that
func getRepoDetailsFromRootBp(bpPath string) repoDetail {
rootBp := getBlueprintRootPath(bpPath)
b, err := UnmarshalMetadata(rootBp, metadataFileName)
if errors.Is(err, os.ErrNotExist) {
return repoDetail{
Source: &repoSource{
BlueprintRootPath: rootBp,
},
}
}
if err != nil && strings.Contains(err.Error(), "proto:") {
return repoDetail{
Source: &repoSource{
BlueprintRootPath: rootBp,
},
}
}
// There is metadata for root but does not have source info
// which means this is a non-git hosted blueprint
if b.Spec.Info.Source == nil {
return repoDetail{
RepoName: b.Metadata.Name,
Source: &repoSource{
BlueprintRootPath: rootBp,
},
}
}
// If we get here, root metadata exists and has git info
return repoDetail{
RepoName: b.Metadata.Name,
Source: &repoSource{
URL: b.Spec.Info.Source.Repo,
SourceType: "git",
BlueprintRootPath: rootBp,
RepoRootPath: strings.Replace(rootBp, b.Spec.Info.Source.Dir, "", 1),
},
}
}
func parseRepoNameFromMd(readme []byte) string {
n := ""
title, err := getMdContent(readme, 1, 1, "", false)
if err == nil {
n = strcase.ToKebab(title.literal)
}
return n
}
// getBpRootPath determines if the provided bpPath is for a submodule
// and resolves it to the root module path if necessary
func getBlueprintRootPath(bpPath string) string {
if strings.Contains(bpPath, nestedBpPath) {
i := strings.Index(bpPath, nestedBpPath)
bpPath = bpPath[0:i]
}
return bpPath
}
// getBpSubmoduleNameInKebabCase gets the submodule name from the blueprint path
// if it lives under the /modules directory
func getBpSubmoduleNameInKebabCase(bpPath string) string {
i := strings.Index(bpPath, nestedBpPath)
if i == -1 {
return ""
}
// 9 is the length for "/modules" after which the submodule name starts
return strcase.ToKebab(bpPath[i+9:])
}