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