go.fuchsia.dev/jiri@v0.0.0-20240502161911-b66513b29486/cmd/jiri/import.go (about) 1 // Copyright 2015 The Vanadium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "encoding/json" 9 "encoding/xml" 10 "fmt" 11 "os" 12 13 "go.fuchsia.dev/jiri" 14 "go.fuchsia.dev/jiri/cmdline" 15 "go.fuchsia.dev/jiri/project" 16 ) 17 18 var ( 19 // Flags for configuring project attributes for remote imports. 20 flagImportName, flagImportRemoteBranch, flagImportRoot string 21 // Flags for controlling the behavior of the command. 22 flagImportOverwrite bool 23 flagImportOut string 24 flagImportDelete bool 25 flagImportRevision string 26 flagImportList bool 27 flagImportJsonOutput string 28 ) 29 30 func init() { 31 cmdImport.Flags.StringVar(&flagImportName, "name", "manifest", `The name of the remote manifest project.`) 32 cmdImport.Flags.StringVar(&flagImportRemoteBranch, "remote-branch", "main", `The branch of the remote manifest project to track, without the leading "origin/".`) 33 cmdImport.Flags.StringVar(&flagImportRevision, "revision", "", `Revision to check out for the remote.`) 34 cmdImport.Flags.StringVar(&flagImportRoot, "root", "", `Root to store the manifest project locally.`) 35 36 cmdImport.Flags.BoolVar(&flagImportOverwrite, "overwrite", false, `Write a new .jiri_manifest file with the given specification. If it already exists, the existing content will be ignored and the file will be overwritten.`) 37 cmdImport.Flags.StringVar(&flagImportOut, "out", "", `The output file. Uses <root>/.jiri_manifest if unspecified. Uses stdout if set to "-".`) 38 cmdImport.Flags.BoolVar(&flagImportDelete, "delete", false, `Delete existing import. Import is matched using <manifest>, <remote> and name. <remote> is optional.`) 39 cmdImport.Flags.BoolVar(&flagImportList, "list", false, `List all the imports from .jiri_manifest. This flag doesn't accept any arguments. -json-out flag can be used to specify json output file.`) 40 cmdImport.Flags.StringVar(&flagImportJsonOutput, "json-output", "", `Json output file from -list flag.`) 41 } 42 43 var cmdImport = &cmdline.Command{ 44 Runner: jiri.RunnerFunc(runImport), 45 Name: "import", 46 Short: "Adds imports to .jiri_manifest file", 47 Long: ` 48 Command "import" adds imports to the [root]/.jiri_manifest file, which specifies 49 manifest information for the jiri tool. The file is created if it doesn't 50 already exist, otherwise additional imports are added to the existing file. 51 52 An <import> element is added to the manifest representing a remote manifest 53 import. The manifest file path is relative to the root directory of the remote 54 import repository. 55 56 Example: 57 $ jiri import myfile https://foo.com/bar.git 58 59 Run "jiri help manifest" for details on manifests. 60 `, 61 ArgsName: "<manifest> <remote>", 62 ArgsLong: ` 63 <manifest> specifies the manifest file to use. 64 65 <remote> specifies the remote manifest repository. 66 `, 67 } 68 69 func isFile(file string) (bool, error) { 70 fileInfo, err := os.Stat(file) 71 if err != nil { 72 if os.IsNotExist(err) { 73 return false, nil 74 } 75 return false, err 76 } 77 return !fileInfo.IsDir(), nil 78 } 79 80 type Import struct { 81 Manifest string `json:"manifest"` 82 Name string `json:"name"` 83 Remote string `json:"remote"` 84 Revision string `json:"revision"` 85 RemoteBranch string `json:"remoteBranch"` 86 Root string `json:"root"` 87 } 88 89 func getListObject(imports []project.Import) []Import { 90 arr := []Import{} 91 for _, i := range imports { 92 i.RemoveDefaults() 93 obj := Import{ 94 Manifest: i.Manifest, 95 Name: i.Name, 96 Remote: i.Remote, 97 Revision: i.Revision, 98 RemoteBranch: i.RemoteBranch, 99 Root: i.Root, 100 } 101 arr = append(arr, obj) 102 } 103 return arr 104 } 105 106 func runImport(jirix *jiri.X, args []string) error { 107 if flagImportDelete && flagImportOverwrite { 108 return jirix.UsageErrorf("cannot use -delete and -overwrite together") 109 } 110 if flagImportList && flagImportOverwrite { 111 return jirix.UsageErrorf("cannot use -list and -overwrite together") 112 } 113 if flagImportDelete && flagImportList { 114 return jirix.UsageErrorf("cannot use -delete and -list together") 115 } 116 117 if flagImportList && len(args) != 0 { 118 return jirix.UsageErrorf("wrong number of arguments with list flag: %v", len(args)) 119 } 120 if flagImportDelete && len(args) != 1 && len(args) != 2 { 121 return jirix.UsageErrorf("wrong number of arguments with delete flag") 122 } else if !flagImportDelete && !flagImportList && len(args) != 2 { 123 return jirix.UsageErrorf("wrong number of arguments") 124 } 125 126 // Initialize manifest. 127 var manifest *project.Manifest 128 manifestExists, err := isFile(jirix.JiriManifestFile()) 129 if err != nil { 130 return err 131 } 132 if !flagImportOverwrite && manifestExists { 133 m, err := project.ManifestFromFile(jirix, jirix.JiriManifestFile()) 134 if err != nil { 135 return err 136 } 137 manifest = m 138 } 139 if manifest == nil { 140 manifest = &project.Manifest{} 141 } 142 143 if flagImportList { 144 imports := getListObject(manifest.Imports) 145 if flagImportJsonOutput == "" { 146 for _, i := range imports { 147 fmt.Printf("* import\t%s\n", i.Name) 148 fmt.Printf(" Manifest:\t%s\n", i.Manifest) 149 fmt.Printf(" Remote:\t%s\n", i.Remote) 150 fmt.Printf(" Revision:\t%s\n", i.Revision) 151 fmt.Printf(" RemoteBranch:\t%s\n", i.RemoteBranch) 152 fmt.Printf(" Root:\t%s\n", i.Root) 153 } 154 return nil 155 } else { 156 out, err := json.MarshalIndent(imports, "", " ") 157 if err != nil { 158 return fmt.Errorf("failed to serialize JSON output: %s\n", err) 159 } 160 return os.WriteFile(flagImportJsonOutput, out, 0644) 161 } 162 } 163 164 if flagImportDelete { 165 var tempImports []project.Import 166 deletedImports := make(map[string]project.Import) 167 for _, imp := range manifest.Imports { 168 if imp.Manifest == args[0] && imp.Name == flagImportName { 169 match := true 170 if len(args) == 2 { 171 match = false 172 if imp.Remote == args[1] { 173 match = true 174 } 175 } 176 if match { 177 deletedImports[imp.Name+"~"+imp.Manifest+"~"+imp.Remote] = imp 178 continue 179 } 180 } 181 tempImports = append(tempImports, imp) 182 } 183 if len(deletedImports) > 1 { 184 return fmt.Errorf("More than 1 import meets your criteria. Please provide remote.") 185 } else if len(deletedImports) == 1 { 186 var data []byte 187 for _, i := range deletedImports { 188 data, err = xml.Marshal(i) 189 if err != nil { 190 return err 191 } 192 break 193 } 194 jirix.Logger.Infof("Deleted one import:\n%s", string(data)) 195 } 196 manifest.Imports = tempImports 197 } else { 198 for _, imp := range manifest.Imports { 199 if imp.Manifest == args[0] && imp.Remote == args[1] && imp.Name == flagImportName { 200 //Already exists, skip 201 jirix.Logger.Debugf("Skip import. Duplicate entry") 202 return nil 203 } 204 } 205 // There's not much error checking when writing the .jiri_manifest file; 206 // errors will be reported when "jiri update" is run. 207 manifest.Imports = append(manifest.Imports, project.Import{ 208 Manifest: args[0], 209 Name: flagImportName, 210 Remote: args[1], 211 RemoteBranch: flagImportRemoteBranch, 212 Revision: flagImportRevision, 213 Root: flagImportRoot, 214 }) 215 } 216 217 // Write output to stdout or file. 218 outFile := flagImportOut 219 if outFile == "" { 220 outFile = jirix.JiriManifestFile() 221 } 222 if outFile == "-" { 223 bytes, err := manifest.ToBytes() 224 if err != nil { 225 return err 226 } 227 _, err = os.Stdout.Write(bytes) 228 return err 229 } 230 return manifest.ToFile(jirix, outFile) 231 }