eng/tools/profileBuilder/cmd/list.go (179 lines of code) (raw):

// +build go1.9 // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. package cmd import ( "encoding/json" "fmt" "io/ioutil" "log" "os" "path/filepath" "strings" "github.com/Azure/azure-sdk-for-go/eng/tools/internal/dirs" "github.com/Azure/azure-sdk-for-go/eng/tools/internal/modinfo" "github.com/Azure/azure-sdk-for-go/eng/tools/profileBuilder/model" "github.com/spf13/cobra" ) const ( inputLongName = "input" inputShortName = "i" inputDescription = "Specify the input JSON file to read for the list of packages." ) // listCmd represents the list command var listCmd = &cobra.Command{ Use: "list", Short: "Creates a profile from a set of packages.", Long: `Reads a list of packages from stdin, where each line is treated as a Go package identifier. These packages are then used to create a profile. Often, the easiest way of invoking this command will be using a pipe operator to specify the packages to include. Example: $> ../model/testdata/smallProfile.txt > profileBuilder list --name small_profile `, Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { logWriter := ioutil.Discard if verboseFlag { logWriter = os.Stdout } outputLog := log.New(logWriter, "[STATUS] ", 0) errLog := log.New(os.Stderr, "[ERROR] ", 0) if !filepath.IsAbs(outputRootDir) { abs, err := filepath.Abs(outputRootDir) if err != nil { errLog.Fatalf("failed to convert to absolute path: %v", err) } outputRootDir = abs } inputFile, err := cmd.Flags().GetString(inputLongName) if err != nil { errLog.Fatalf("failed to get %s: %v", inputLongName, err) } data, err := ioutil.ReadFile(inputFile) if err != nil { errLog.Fatalf("failed to read list: %v", err) } var listDef model.ListDefinition err = json.Unmarshal(data, &listDef) if err != nil { errLog.Fatalf("failed to unmarshal JSON: %v", err) } if modulesFlag { // when generating in module-aware mode a few extra things need to happen // 1. find the latest module version for the profile we're working on // 2. update the list of includes package to their latest module versions // 3. if any includes were updated generate a new major version for this profile modver, err := getLatestModVer(outputRootDir) if err != nil { errLog.Fatalf("failed to get module dir: %v", err) } updated, err := updateModuleVersions(&listDef) if err != nil { errLog.Fatalf("failed to update module versions: %v", err) } if updated { // at least one include was updated, write out updated list definition data, err = json.MarshalIndent(listDef, "", " ") if err != nil { errLog.Fatalf("failed to marshal updated list: %v", err) } data = append(data, '\n') err = ioutil.WriteFile(inputFile, data, 0666) if err != nil { errLog.Fatalf("failed to write updated list: %v", err) } // increment profile module major version and generate new go.mod file modver = modinfo.IncrementModuleVersion(modver) outputRootDir = filepath.Join(outputRootDir, modver) err = generateGoMod(outputRootDir) if err != nil { errLog.Fatalf("failed to generate go.mod: %v", err) } } else if modver != "" { outputRootDir = filepath.Join(outputRootDir, modver) } } fmt.Printf("Executes profileBuilder in %s\n", outputRootDir) outputLog.Printf("Output-Location set to: %s", outputRootDir) if clearOutputFlag { if err := clearOutputFolder(outputRootDir, listDef.IgnoredPaths); err != nil { errLog.Fatalf("Unable to clear output-folder: %v", err) } } // use recursive build to include the *api packages model.BuildProfile(listDef, profileName, outputRootDir, outputLog, errLog, true, modulesFlag, semLimit) }, } func init() { rootCmd.AddCommand(listCmd) listCmd.Flags().StringP(inputLongName, inputShortName, "", inputDescription) listCmd.MarkFlagRequired(inputLongName) } // for each included package check if there is a newer module major version then the one specified, // and if there is update the include accordinly. returns true if any packages were updated. func updateModuleVersions(listDef *model.ListDefinition) (bool, error) { dirty := false for i, entry := range listDef.Include { // "../../services/storage/mgmt/2016-01-01/storage" // "../../services/network/mgmt/2015-06-15/network/v2" target := entry if modinfo.HasVersionSuffix(target) { target = filepath.Dir(target) target = strings.Replace(target, "\\", "/", -1) } modDirs, err := modinfo.GetModuleSubdirs(target) if err != nil { return false, err } if len(modDirs) == 0 { continue } latest := target + "/" + modDirs[len(modDirs)-1] if latest == entry { // already using latest major version continue } listDef.Include[i] = latest dirty = true } return dirty, nil } // returns the last major module version for the specified profile directory (e.g. "v2" etc). // if there are no major versions the return value is the empty string. func getLatestModVer(profileDir string) (string, error) { subdirs, err := modinfo.GetModuleSubdirs(profileDir) if err != nil { return "", err } modDir := "" if len(subdirs) > 0 { modDir = subdirs[len(subdirs)-1] } return modDir, nil } // generate a go.mod file in the specified module major version directory (e.g. "profiles/foo/v2") // the provided module directory is used to calculate the module name. func generateGoMod(modDir string) error { const gomodFormat = "module %s\n\ngo 1.12\n" err := os.Mkdir(modDir, os.ModeDir|0644) if err != nil { return err } gomod, err := os.Create(filepath.Join(modDir, "go.mod")) if err != nil { return err } defer gomod.Close() mod, err := modinfo.CreateModuleNameFromPath(modDir) if err != nil { return err } _, err = fmt.Fprintf(gomod, gomodFormat, mod) return err } func clearOutputFolder(root string, excepts []string) error { children, err := dirs.GetSubdirs(root) if err != nil && !os.IsNotExist(err) { return err } for _, child := range children { if contains(excepts, child) { continue } childPath := filepath.Join(root, child) err = os.RemoveAll(childPath) if err != nil { return err } } return nil } func contains(array []string, item string) bool { for _, e := range array { if e == item { return true } } return false }