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 automation
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/automation/validate"
16	"github.com/Azure/azure-sdk-for-go/tools/internal/exports"
17	"github.com/Azure/azure-sdk-for-go/tools/internal/utils"
18)
19
20type generateContext struct {
21	sdkRoot    string
22	specRoot   string
23	commitHash string
24
25	repoContent map[string]exports.Content
26
27	existingPackages  packagesForReadme
28	defaultOptions    model.Options
29	additionalOptions []model.Option
30}
31
32func (ctx generateContext) SDKRoot() string {
33	return ctx.sdkRoot
34}
35
36func (ctx generateContext) SpecRoot() string {
37	return ctx.specRoot
38}
39
40func (ctx generateContext) RepoContent() map[string]exports.Content {
41	return ctx.repoContent
42}
43
44var _ autorest.GenerateContext = (*generateContext)(nil)
45
46func (ctx generateContext) generate(readme string) ([]autorest.GenerateResult, []error) {
47	absReadme := filepath.Join(ctx.specRoot, readme)
48	absReadmeGo := filepath.Join(filepath.Dir(absReadme), "readme.go.md")
49	log.Printf("Reading tags from readme.go.md '%s'...", absReadmeGo)
50	reader, err := os.Open(absReadmeGo)
51	if err != nil {
52		return nil, []error{
53			fmt.Errorf("cannot read from readme.go.md: %+v", err),
54		}
55	}
56	log.Printf("Parsing tags from readme.go.md '%s'...", absReadmeGo)
57	tags, err := autorest.ReadBatchTags(reader)
58	if err != nil {
59		return nil, []error{
60			fmt.Errorf("cannot read batch tags in readme.go.md '%s': %+v", absReadmeGo, err),
61		}
62	}
63
64	log.Printf("Cleaning all the packages from readme '%s'...", readme)
65	removedPackages, err := clean(ctx.sdkRoot, ctx.existingPackages)
66	if err != nil {
67		return nil, []error{
68			fmt.Errorf("cannot clean packages from readme '%s': %+v", readme, err),
69		}
70	}
71
72	log.Printf("Generating the following tags: \n[%s]", strings.Join(tags, ", "))
73	var packageResults []autorest.GenerateResult
74	var errors []error
75	for _, tag := range tags {
76		result, err := ctx.generateForTag(readme, tag)
77		if err != nil {
78			errors = append(errors, err)
79			continue
80		}
81		packageResults = append(packageResults, *result)
82	}
83
84	// also add the removed packages in the results if it is not regenerated
85	for _, removedPackage := range removedPackages {
86		if !contains(packageResults, removedPackage.packageName) {
87			// this package is not regenerated, therefore it is removed
88			packageResults = append(packageResults, autorest.GenerateResult{
89				Package: autorest.ChangelogResult{
90					Tag:             removedPackage.Tag,
91					PackageName:     removedPackage.packageName,
92					PackageFullPath: utils.NormalizePath(filepath.Join(ctx.sdkRoot, removedPackage.packageName)),
93					Changelog: model.Changelog{
94						RemovedPackage: true,
95					},
96				},
97			})
98		}
99	}
100
101	return packageResults, errors
102}
103
104func (ctx generateContext) generateForTag(readme, tag string) (*autorest.GenerateResult, error) {
105	var options model.Options
106	// Get the proper options to use depending on whether this tag has been already generated in the SDK or not
107	if metadata, ok := ctx.existingPackages[tag]; ok {
108		// this tag has been generated, use the existing parameters in its metadata
109		additionalOptions, err := model.ParseOptions(strings.Split(metadata.AdditionalProperties.AdditionalOptions, " "))
110		if err != nil {
111			return nil, fmt.Errorf("cannot parse existing defaultOptions for readme '%s'/tag '%s': %+v", readme, tag, err)
112		}
113		options = ctx.defaultOptions.MergeOptions(additionalOptions.Arguments()...)
114	} else {
115		// this is a new tag
116		options = ctx.defaultOptions.MergeOptions(ctx.additionalOptions...)
117	}
118
119	input := autorest.GenerateInput{
120		Readme:     readme,
121		Tag:        tag,
122		CommitHash: ctx.commitHash,
123		Options:    options,
124	}
125	validateCtx := validate.MetadataValidateContext{
126		Readme:  readme,
127		SDKRoot: ctx.sdkRoot,
128	}
129	return autorest.GeneratePackage(ctx, input, autorest.GenerateOptions{
130		Stderr:            os.Stderr,
131		Stdout:            os.Stdout,
132		AutoRestLogPrefix: "[AUTOREST] ",
133		ChangelogTitle:    "Unreleased",
134		Validators: []autorest.MetadataValidateFunc{
135			validateCtx.PreviewCheck,
136			validateCtx.MgmtCheck,
137			validateCtx.NamespaceCheck,
138		},
139	})
140}
141