1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT License. See License.txt in the project root for license information.
3
4package common
5
6import (
7	"fmt"
8	"log"
9	"os"
10	"path/filepath"
11	"strings"
12
13	"github.com/Azure/azure-sdk-for-go/tools/generator/autorest"
14	"github.com/Azure/azure-sdk-for-go/tools/generator/autorest/model"
15	"github.com/Azure/azure-sdk-for-go/tools/generator/cmd/template"
16	"github.com/Azure/azure-sdk-for-go/tools/generator/common"
17	"github.com/Azure/azure-sdk-for-go/tools/internal/exports"
18	"github.com/Masterminds/semver"
19)
20
21type GenerateContext struct {
22	SdkPath    string
23	SpecPath   string
24	CommitHash string
25}
26
27type GenerateResult struct {
28	Version        string
29	RpName         string
30	PackageName    string
31	PackageAbsPath string
32	Changelog      model.Changelog
33	ChangelogMd    string
34}
35
36func (ctx GenerateContext) SDKRoot() string {
37	return ctx.SdkPath
38}
39
40func (ctx GenerateContext) SpecRoot() string {
41	return ctx.SpecPath
42}
43
44func (ctx GenerateContext) GenerateForAutomation(readme string, repo string) ([]GenerateResult, []error) {
45	absReadme := filepath.Join(ctx.SpecPath, readme)
46	absReadmeGo := filepath.Join(filepath.Dir(absReadme), "readme.go.md")
47
48	var result []GenerateResult
49	var errors []error
50
51	log.Printf("Get all namespaces from readme file")
52	rpMap, err := ReadV2ModuleNameToGetNamespace(absReadmeGo)
53	if err != nil {
54		return nil, []error{
55			fmt.Errorf("cannot get rp and namespaces from readme '%s': %+v", readme, err),
56		}
57	}
58
59	for rpName, namespaceNames := range rpMap {
60		for _, namespaceName := range namespaceNames {
61			log.Printf("Process rp: %s, namespace: %s", rpName, namespaceName)
62			singleResult, err := ctx.GenerateForSingleRpNamespace(rpName, namespaceName, "", "", repo)
63			if err != nil {
64				errors = append(errors, err)
65				continue
66			}
67			result = append(result, *singleResult)
68		}
69	}
70	return result, errors
71}
72
73func (ctx GenerateContext) GenerateForSingleRpNamespace(rpName, namespaceName, specficPackageTitle, specficVersion, specficRepoURL string) (*GenerateResult, error) {
74	packagePath := filepath.Join(ctx.SdkPath, "sdk", rpName, namespaceName)
75	changelogPath := filepath.Join(packagePath, common.ChangelogFilename)
76	if _, err := os.Stat(changelogPath); os.IsNotExist(err) {
77		log.Printf("Package '%s' changelog not exist, do onboard process", packagePath)
78
79		if specficPackageTitle == "" {
80			specficPackageTitle = strings.Title(rpName)
81		}
82
83		log.Printf("Use template to generate new rp folder and basic package files...")
84		if err = template.GeneratePackageByTemplate(rpName, namespaceName, template.Flags{
85			SDKRoot:      ctx.SdkPath,
86			TemplatePath: "tools/generator/template/rpName/packageName",
87			PackageTitle: specficPackageTitle,
88			Commit:       ctx.CommitHash,
89		}); err != nil {
90			return nil, err
91		}
92
93		if specficRepoURL != "" {
94			log.Printf("Change the repo url in `autorest.md`...")
95			autorestMdPath := filepath.Join(packagePath, "autorest.md")
96			if err = ReplaceRepoURL(autorestMdPath, specficRepoURL); err != nil {
97				return nil, err
98			}
99		}
100
101		log.Printf("Run `go generate` to regenerate the code...")
102		if err = ExecuteGoGenerate(packagePath); err != nil {
103			return nil, err
104		}
105
106		log.Printf("Generate changelog for package...")
107		newExports, err := exports.Get(packagePath)
108		if err != nil {
109			return nil, err
110		}
111		changelog, err := autorest.GetChangelogForPackage(nil, &newExports)
112		if err != nil {
113			return nil, err
114		}
115
116		log.Printf("Replace {{NewClientMethod}} placeholder in the README.md ")
117		if err = ReplaceNewClientMethodPlaceholder(packagePath, newExports); err != nil {
118			return nil, err
119		}
120
121		return &GenerateResult{
122			Version:        "0.1.0",
123			RpName:         rpName,
124			PackageName:    namespaceName,
125			PackageAbsPath: packagePath,
126			Changelog:      *changelog,
127			ChangelogMd:    changelog.ToCompactMarkdown(),
128		}, nil
129	} else {
130		log.Printf("Package '%s' existed, do update process", packagePath)
131
132		log.Printf("Get ori exports for changelog generation...")
133		oriExports, err := exports.Get(packagePath)
134		if err != nil {
135			return nil, err
136		}
137
138		log.Printf("Remove all the files that start with `zz_generated_`...")
139		if err = CleanSDKGeneratedFiles(packagePath); err != nil {
140			return nil, err
141		}
142
143		log.Printf("Change the commit hash in `autorest.md` to a new commit that corresponds to the new release...")
144		autorestMdPath := filepath.Join(packagePath, "autorest.md")
145		if err = ReplaceCommitID(autorestMdPath, ctx.CommitHash); err != nil {
146			return nil, err
147		}
148
149		if specficRepoURL != "" {
150			log.Printf("Change the repo url in `autorest.md`...")
151			if err = ReplaceRepoURL(autorestMdPath, specficRepoURL); err != nil {
152				return nil, err
153			}
154		}
155
156		log.Printf("Run `go generate` to regenerate the code...")
157		if err = ExecuteGoGenerate(packagePath); err != nil {
158			return nil, err
159		}
160
161		log.Printf("Generate changelog for package...")
162		newExports, err := exports.Get(packagePath)
163		if err != nil {
164			return nil, err
165		}
166		changelog, err := autorest.GetChangelogForPackage(&oriExports, &newExports)
167		if err != nil {
168			return nil, err
169		}
170
171		log.Printf("Calculate new version...")
172		var version *semver.Version
173		if specficVersion == "" {
174			version, err = CalculateNewVersion(changelog, packagePath)
175			if err != nil {
176				return nil, err
177			}
178		} else {
179			log.Printf("Use specfic version: %s", specficVersion)
180			version, err = semver.NewVersion(specficVersion)
181			if err != nil {
182				return nil, err
183			}
184		}
185
186		log.Printf("Add changelog to file...")
187		changelogMd, err := AddChangelogToFile(changelog, version, packagePath)
188		if err != nil {
189			return nil, err
190		}
191
192		log.Printf("Remove all the files that start with `zz_generated_`...")
193		if err = CleanSDKGeneratedFiles(packagePath); err != nil {
194			return nil, err
195		}
196
197		log.Printf("Replace version in autorest.md...")
198		if err = ReplaceVersion(packagePath, version.String()); err != nil {
199			return nil, err
200		}
201
202		log.Printf("Run `go generate` to regenerate the code for new version...")
203		if err = ExecuteGoGenerate(packagePath); err != nil {
204			return nil, err
205		}
206
207		return &GenerateResult{
208			Version:        version.String(),
209			RpName:         rpName,
210			PackageName:    namespaceName,
211			PackageAbsPath: packagePath,
212			Changelog:      *changelog,
213			ChangelogMd:    changelogMd,
214		}, nil
215	}
216}
217