github.com/charypar/monobuild@v0.0.0-20211122220434-fd884ed50212/cmd/diff.go (about)

     1  package cmd
     2  
     3  import (
     4  	"bufio"
     5  	"errors"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"log"
     9  	"os"
    10  
    11  	"github.com/charypar/monobuild/cli"
    12  	"github.com/spf13/cobra"
    13  )
    14  
    15  type diffOptions struct {
    16  	baseBranch    string
    17  	baseCommit    string
    18  	mainBranch    bool
    19  	rebuildStrong bool
    20  	dotHighlight  bool
    21  }
    22  
    23  var diffOpts diffOptions
    24  
    25  var diffCmd = &cobra.Command{
    26  	Use:   "diff [-]",
    27  	Short: "Build schedule for components affected by git changes",
    28  	Long: `Create a build schedule based on git history and dependency graph.
    29  Each line in the output is a component and its dependencies. 
    30  The format of each line is:
    31  
    32  <component>: <dependency>, <dependency>, <dependency>, ...
    33  
    34  Diff can output either the build schedule (using only strong dependencies) or 
    35  the original dependeny graph (using all dependencies).
    36  
    37  By default changed files are determined from the local git repository. 
    38  Optionally, they can be provided externaly from stdin, by adding a hypen (-) after
    39  the diff command.`,
    40  	Args: func(cmd *cobra.Command, args []string) error {
    41  		if len(args) > 1 {
    42  			return errors.New("Too many arguments")
    43  		}
    44  		if len(args) == 1 && args[0] != "-" {
    45  			return fmt.Errorf("Invalid first argument: %s, only \"-\" is allowed", args[0])
    46  		}
    47  
    48  		return nil
    49  	},
    50  	Run: diffFn,
    51  }
    52  
    53  func init() {
    54  	rootCmd.AddCommand(diffCmd)
    55  
    56  	diffCmd.Flags().StringVar(&diffOpts.baseBranch, "base-branch", "master", "Base branch to use for comparison")
    57  	diffCmd.Flags().StringVar(&diffOpts.baseCommit, "base-commit", "HEAD^1", "Base commit to compare with (useful in main-brahnch mode when using rebase merging)")
    58  	diffCmd.Flags().BoolVar(&diffOpts.mainBranch, "main-branch", false, "Run in main branch mode (i.e. only compare with parent commit)")
    59  	diffCmd.Flags().BoolVar(&diffOpts.rebuildStrong, "rebuild-strong", false, "Include all strong dependencies of affected components")
    60  	diffCmd.Flags().BoolVar(&commonOpts.printDependencies, "dependencies", false, "Ouput the dependencies, not the build schedule")
    61  	diffCmd.Flags().BoolVar(&commonOpts.dotFormat, "dot", false, "Print in DOT format for GraphViz")
    62  	diffCmd.Flags().BoolVar(&commonOpts.printFull, "full", false, "Print the full dependency graph including strengths")
    63  }
    64  
    65  func diffFn(cmd *cobra.Command, args []string) {
    66  	// first we tediously process the CLI flags
    67  	var branchMode cli.DiffMode
    68  	changedFiles := []string{}
    69  
    70  	if len(args) > 0 && args[0] == "-" {
    71  		branchMode = cli.Direct
    72  
    73  		// Read stdin into []string
    74  		scanner := bufio.NewScanner(os.Stdin)
    75  		for scanner.Scan() {
    76  			changedFiles = append(changedFiles, scanner.Text())
    77  		}
    78  
    79  	} else if diffOpts.mainBranch {
    80  		branchMode = cli.MainBranch
    81  	} else {
    82  		branchMode = cli.FeatureBranch
    83  	}
    84  
    85  	var format cli.OutputFormat
    86  	if commonOpts.dotFormat {
    87  		format = cli.Dot
    88  	} else {
    89  		format = cli.Text
    90  	}
    91  
    92  	diffContext := cli.DiffContext{
    93  		Mode:         branchMode,
    94  		BaseBranch:   diffOpts.baseBranch,
    95  		BaseCommit:   diffOpts.baseCommit,
    96  		ChangedFiles: changedFiles,
    97  	}
    98  	scope := cli.Scope{Scope: commonOpts.scope, TopLevel: commonOpts.topLevel}
    99  
   100  	var outType cli.OutputType
   101  	if commonOpts.printFull {
   102  		outType = cli.Full
   103  	} else if commonOpts.printDependencies {
   104  		outType = cli.Dependencies
   105  	} else {
   106  		outType = cli.Schedule
   107  	}
   108  
   109  	outputOpts := cli.OutputOptions{Format: format, Type: outType}
   110  
   111  	repoManifest := ""
   112  	if len(commonOpts.repoManifestFile) > 0 {
   113  		bytes, err := ioutil.ReadFile(commonOpts.repoManifestFile)
   114  		if err != nil {
   115  			log.Fatal(err)
   116  		}
   117  
   118  		repoManifest = string(bytes)
   119  	}
   120  
   121  	// run the CLI command
   122  	dependencies, schedule, impacted, err := cli.Diff(commonOpts.dependencyFilesGlob, diffContext, scope, diffOpts.rebuildStrong, repoManifest)
   123  	if err != nil {
   124  		log.Fatal(err)
   125  	}
   126  
   127  	fmt.Print(cli.Format(dependencies, schedule, impacted, outputOpts))
   128  }