1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
|
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
package automation
import (
"fmt"
"log"
"os"
"strings"
"github.com/Azure/azure-sdk-for-go/eng/tools/generator/cmd/automation/pipeline"
"github.com/Azure/azure-sdk-for-go/eng/tools/generator/cmd/v2/common"
"github.com/Azure/azure-sdk-for-go/eng/tools/generator/repo"
"github.com/Azure/azure-sdk-for-go/eng/tools/internal/utils"
"github.com/spf13/cobra"
)
// Command returns the automation v2 command. Note that this command is designed to run in the root directory of
// azure-sdk-for-go. It does not work if you are running this tool in somewhere else
func Command() *cobra.Command {
cmd := &cobra.Command{
Use: "automation-v2 <generate input filepath> <generate output filepath> [goVersion]",
Args: cobra.RangeArgs(2, 3),
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
log.SetFlags(0) // remove the time stamp prefix
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
goVersion := "1.18"
if len(args) == 3 {
goVersion = args[2]
}
if err := execute(args[0], args[1], goVersion); err != nil {
logError(err)
return err
}
return nil
},
SilenceUsage: true, // this command is used for a pipeline, the usage should never show
}
return cmd
}
func execute(inputPath, outputPath, goVersion string) error {
log.Printf("Reading generate input file from '%s'...", inputPath)
input, err := pipeline.ReadInput(inputPath)
if err != nil {
return fmt.Errorf("cannot read generate input: %+v", err)
}
log.Printf("Generating using the following GenerateInput...\n%s", input.String())
cwd, err := os.Getwd()
if err != nil {
return err
}
log.Printf("Using current directory as SDK root: %s", cwd)
ctx := automationContext{
sdkRoot: utils.NormalizePath(cwd),
specRoot: input.SpecFolder,
commitHash: input.HeadSha,
goVersion: goVersion,
}
output, err := ctx.generate(input)
if err != nil {
return err
}
log.Printf("Output generated: \n%s", output.String())
log.Printf("Writing output to file '%s'...", outputPath)
if err := pipeline.WriteOutput(outputPath, output); err != nil {
return fmt.Errorf("cannot write generate output: %+v", err)
}
return nil
}
type automationContext struct {
sdkRoot string
specRoot string
commitHash string
goVersion string
}
// TODO -- support dry run
func (ctx *automationContext) generate(input *pipeline.GenerateInput) (*pipeline.GenerateOutput, error) {
if input.DryRun {
return nil, fmt.Errorf("dry run not supported yet")
}
// iterate over all the readme
results := make([]pipeline.PackageResult, 0)
errorBuilder := generateErrorBuilder{}
// create sdk repo ref
sdkRepo, err := repo.OpenSDKRepository(ctx.sdkRoot)
if err != nil {
return nil, fmt.Errorf("failed to get sdk repo: %+v", err)
}
if input.RelatedReadmeMdFile != "" {
input.RelatedReadmeMdFiles = append(input.RelatedReadmeMdFiles, input.RelatedReadmeMdFile)
}
for _, readme := range input.RelatedReadmeMdFiles {
log.Printf("Start to process readme file: %s", readme)
sepStrs := strings.Split(readme, "/")
for i, sepStr := range sepStrs {
if sepStr == "resource-manager" {
readme = strings.Join(sepStrs[i-1:], "/")
if i > 1 {
ctx.specRoot = input.SpecFolder + "/" + strings.Join(sepStrs[:i-1], "/")
}
break
}
}
generateCtx := common.GenerateContext{
SDKPath: sdkRepo.Root(),
SDKRepo: &sdkRepo,
SpecPath: ctx.specRoot,
}
namespaceResults, errors := generateCtx.GenerateForAutomation(readme, input.RepoHTTPSURL, ctx.goVersion)
if len(errors) != 0 {
errorBuilder.add(errors...)
continue
}
for _, namespaceResult := range namespaceResults {
content := namespaceResult.ChangelogMD
breaking := namespaceResult.Changelog.HasBreakingChanges()
breakingChangeItems := namespaceResult.Changelog.GetBreakingChangeItems()
results = append(results, pipeline.PackageResult{
Version: namespaceResult.Version,
PackageName: namespaceResult.PackageName,
Path: []string{fmt.Sprintf("sdk/resourcemanager/%s/%s", namespaceResult.RPName, namespaceResult.PackageName)},
PackageFolder: fmt.Sprintf("sdk/resourcemanager/%s/%s", namespaceResult.RPName, namespaceResult.PackageName),
ReadmeMd: []string{readme},
Changelog: &pipeline.Changelog{
Content: &content,
HasBreakingChange: &breaking,
BreakingChangeItems: &breakingChangeItems,
},
})
}
log.Printf("Finish to process readme file: %s", readme)
}
return &pipeline.GenerateOutput{
Packages: results,
}, errorBuilder.build()
}
type generateErrorBuilder struct {
errors []error
}
func (b *generateErrorBuilder) add(err ...error) {
b.errors = append(b.errors, err...)
}
func (b *generateErrorBuilder) build() error {
if len(b.errors) == 0 {
return nil
}
var messages []string
for _, err := range b.errors {
messages = append(messages, err.Error())
}
return fmt.Errorf("total %d error(s): \n%s", len(b.errors), strings.Join(messages, "\n"))
}
func logError(err error) {
for _, line := range strings.Split(err.Error(), "\n") {
if l := strings.TrimSpace(line); l != "" {
log.Printf("[ERROR] %s", l)
}
}
}
|