File: automationCmd.go

package info (click to toggle)
golang-github-azure-azure-sdk-for-go 68.0.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 556,256 kB
  • sloc: javascript: 196; sh: 96; makefile: 7
file content (181 lines) | stat: -rw-r--r-- 5,335 bytes parent folder | download
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)
		}
	}
}