File: list.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 (219 lines) | stat: -rw-r--r-- 6,442 bytes parent folder | download | duplicates (3)
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
// +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
}