1package main 2 3import ( 4 "encoding/json" 5 "errors" 6 "flag" 7 "fmt" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "strconv" 12 13 "github.com/aws/aws-sdk-go-v2/internal/repotools/changes" 14) 15 16var changeParams = struct { 17 module string 18 changeType changes.ChangeType 19 description string 20 compareTo string 21 similar bool 22}{} 23 24var addFlags *flag.FlagSet 25var lsFlags *flag.FlagSet 26var modifyFlags *flag.FlagSet 27var rmFlags *flag.FlagSet 28 29func changeUsage() { 30 sets := []*flag.FlagSet{addFlags, lsFlags, modifyFlags, rmFlags} 31 32 for _, f := range sets { 33 f.Usage() 34 } 35} 36 37func init() { 38 addFlags = flag.NewFlagSet("add", flag.ExitOnError) 39 addFlags.StringVar(&changeParams.module, "module", "", "sets the change's module") 40 addFlags.Var(&changeParams.changeType, "type", "sets the change's type") 41 addFlags.StringVar(&changeParams.description, "description", "", "sets the change's description") 42 addFlags.StringVar(&changeParams.compareTo, "compare-to", "", "specifies a path to a version enclosure to compare current module hashes to in order to resolve a wildcard.") 43 addFlags.Usage = func() { 44 fmt.Printf("%s change add [-module=<module>] [-type=<type>] [-description=<description>]\n", os.Args[0]) 45 addFlags.PrintDefaults() 46 } 47 48 lsFlags = flag.NewFlagSet("ls", flag.ExitOnError) 49 lsFlags.StringVar(&changeParams.module, "module", "", "filters changes by module") 50 lsFlags.Usage = func() { 51 fmt.Printf("%s change ls [-module=<module>]\n", os.Args[0]) 52 lsFlags.PrintDefaults() 53 } 54 55 modifyFlags = flag.NewFlagSet("modify", flag.ExitOnError) 56 modifyFlags.Usage = func() { 57 fmt.Printf("%s change modify <change id>\n <change id>: the index (as found in the ls subcommand) or the ID of the change to modify\n", os.Args[0]) 58 modifyFlags.PrintDefaults() 59 } 60 61 rmFlags = flag.NewFlagSet("rm", flag.ExitOnError) 62 rmFlags.Usage = func() { 63 fmt.Printf("%s change rm <change id>\n <change id>: the index (as found in the ls subcommand) or the ID of the change to remove\n", os.Args[0]) 64 rmFlags.PrintDefaults() 65 } 66} 67 68func changeSubcmd(args []string) error { 69 if len(args) == 0 { 70 changeUsage() 71 return errors.New("invalid usage") 72 } 73 74 subCommand := args[0] 75 76 changesPath, err := changes.GetChangesPath() 77 if err != nil { 78 return fmt.Errorf("failed to load .changes directory: %v", err) 79 } 80 81 metadata, err := changes.LoadMetadata(changesPath) 82 if err != nil { 83 return fmt.Errorf("failed to load .changes directory: %v", err) 84 } 85 86 switch subCommand { 87 case "add", "new": 88 err = addFlags.Parse(args[1:]) 89 if err != nil { 90 return err 91 } 92 93 if changes.ModIsWildcard(changeParams.module) { 94 return addCmdWildcard(metadata, changeParams.module, changeParams.changeType, changeParams.description, changeParams.compareTo) 95 } 96 97 return addCmd(metadata, changeParams.module, changeParams.changeType, changeParams.description) 98 case "ls", "list": 99 err = lsFlags.Parse(args[1:]) 100 if err != nil { 101 return err 102 } 103 104 return lsCmd(metadata, changeParams.module) 105 case "modify", "edit": 106 err = modifyFlags.Parse(args[1:]) 107 if err != nil { 108 return err 109 } 110 111 if len(args) < 2 { 112 changeUsage() 113 return errors.New("invalid usage") 114 } 115 116 id := args[1] 117 118 return modifyCmd(metadata, id) 119 case "rm", "delete": 120 err = rmFlags.Parse(args[1:]) 121 if err != nil { 122 return err 123 } 124 125 if len(args) < 2 { 126 changeUsage() 127 return errors.New("invalid usage") 128 } 129 130 id := args[1] 131 132 return rmCmd(metadata, id) 133 default: 134 changeUsage() 135 return errors.New("invalid usage") 136 } 137} 138 139func addCmdWildcard(metadata *changes.Metadata, module string, changeType changes.ChangeType, description string, compareTo string) error { 140 if module == "" { 141 return errors.New("couldn't add wildcard change: a module must be provided with --module") 142 } 143 144 repo, err := changes.NewRepository(filepath.Join(metadata.ChangePath, "..")) 145 if err != nil { 146 return fmt.Errorf("couldn't add wildcard change: %v", err) 147 } 148 149 mods, err := repo.Modules() 150 if err != nil { 151 return err 152 } 153 154 var affectedModules []string 155 if compareTo != "" { 156 data, err := ioutil.ReadFile(compareTo) 157 if err != nil { 158 return err 159 } 160 161 var enc changes.VersionEnclosure 162 err = json.Unmarshal(data, &enc) 163 if err != nil { 164 return err 165 } 166 167 hashes, err := repo.ModuleHashes(enc) 168 if err != nil { 169 return err 170 } 171 172 affectedModules = enc.HashDiff(hashes) 173 } else { 174 affectedModules, err = changes.MatchWildcardModules(mods, module) 175 if err != nil { 176 return err 177 } 178 } 179 180 template, err := changes.ChangeToTemplate(changes.Change{ 181 Module: module, 182 Type: changeType, 183 Description: description, 184 AffectedModules: affectedModules, 185 }) 186 if err != nil { 187 return fmt.Errorf("failed to create change: %v", err) 188 } 189 190 filledTemplate, err := editTemplate(template) 191 if err != nil { 192 return fmt.Errorf("failed to create change: %v", err) 193 } 194 195 changes, err := changes.TemplateToChanges(filledTemplate) 196 if err != nil { 197 return fmt.Errorf("failed to create change: %v", err) 198 } 199 200 if len(changes) != 1 { 201 return fmt.Errorf("failed to create change: expected template to create 1 change, got %d changes", len(changes)) 202 } 203 204 change := changes[0] 205 206 // TODO: move some logic into TemplateToChanges 207 208 return metadata.AddChange(change) 209} 210 211func addCmd(metadata *changes.Metadata, module string, changeType changes.ChangeType, description string) error { 212 if module == "" { 213 currentModule, err := changes.GetCurrentModule() 214 if err != nil { 215 return fmt.Errorf("failed to create change: the module flag was not provided and the tool could not detect a module") 216 } 217 218 module = currentModule 219 } 220 221 var newChanges []changes.Change 222 var err error 223 224 if changeType != "" && description != "" { 225 newChanges, err = changes.NewChanges([]string{module}, changeType, description, "") 226 if err != nil { 227 return fmt.Errorf("failed to create change: %v", err) 228 } 229 230 err = metadata.AddChanges(newChanges) 231 if err != nil { 232 return fmt.Errorf("failed to create change: %v", err) 233 } 234 } else { 235 template, err := changes.ChangeToTemplate(changes.Change{ 236 Module: module, 237 }) 238 if err != nil { 239 return fmt.Errorf("failed to create change: %v", err) 240 } 241 242 filledTemplate, err := editTemplate(template) 243 if err != nil { 244 return fmt.Errorf("failed to create change: %v", err) 245 } 246 247 newChanges, err = metadata.AddChangesFromTemplate(filledTemplate) 248 if err != nil { 249 return fmt.Errorf("failed to create change: %v", err) 250 } 251 } 252 253 for _, c := range newChanges { 254 fmt.Println("added change with id " + c.ID) 255 } 256 257 return nil 258} 259 260func lsCmd(metadata *changes.Metadata, module string) error { 261 for i, c := range metadata.Changes { 262 if c.Module == module || module == "" { 263 fmt.Printf("[%d] %s\n", i, c.ID) 264 fmt.Println("\t", c.Type) 265 fmt.Println("\t", c.Description) 266 fmt.Println() 267 } 268 } 269 270 return nil 271} 272 273func modifyCmd(metadata *changes.Metadata, id string) error { 274 change, err := selectChange(metadata, id) 275 if err != nil { 276 return fmt.Errorf("failed to modify change: %v", err) 277 } 278 279 template, err := changes.ChangeToTemplate(change) 280 if err != nil { 281 return fmt.Errorf("failed to modify change: %v", err) 282 } 283 284 filledTemplate, err := editTemplate(template) 285 if err != nil { 286 return fmt.Errorf("failed to modify change: %v", err) 287 } 288 289 newChanges, err := metadata.UpdateChangeFromTemplate(change, filledTemplate) 290 if err != nil { 291 return fmt.Errorf("couldn't modify change: %v", err) 292 } 293 294 fmt.Printf("successfully modified %s, new change(s):\n", change.ID) 295 for _, c := range newChanges { 296 fmt.Printf("\t%s\n", c.ID) 297 } 298 return nil 299} 300 301func rmCmd(metadata *changes.Metadata, id string) error { 302 change, err := selectChange(metadata, id) 303 if err != nil { 304 return fmt.Errorf("failed to remove change: %v", err) 305 } 306 307 err = metadata.RemoveChangeByID(change.ID) 308 if err != nil { 309 return fmt.Errorf("failed to remove change: %v", err) 310 } 311 312 fmt.Println("successfully removed " + change.ID) 313 return nil 314} 315 316// selectChange will return the change identified by the given id, which can be either the index of one of metadata's 317// Changes or the Change's ID. 318func selectChange(metadata *changes.Metadata, id string) (changes.Change, error) { 319 // try selecting by index first 320 index, err := strconv.Atoi(id) 321 if err == nil { 322 if index < 0 || index >= len(metadata.Changes) { 323 return changes.Change{}, fmt.Errorf("failed to get change with index %d: index out of range", index) 324 } 325 return metadata.Changes[index], nil 326 } 327 328 return metadata.GetChangeByID(id) 329} 330