github.com/thetechnoweenie/graven@v1.0.2/commands/bump.go (about)

     1  package commands
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"strings"
     8  	"text/template"
     9  
    10  	"github.com/cbegin/graven/domain"
    11  	"github.com/urfave/cli"
    12  )
    13  
    14  const (
    15  	versionPackage  = "version"
    16  	versionFileName = "version.go"
    17  	versionTemplate = `// graven - This file was generated. It will be overwritten. Do not modify.
    18  package {{.Package}}
    19  
    20  var Version = "{{.Version}}"
    21  `
    22  )
    23  
    24  var BumpCommand = cli.Command{
    25  	Name:  "bump",
    26  	Usage: "Manage the version (major, minor, patch) and clear or set qualifier (e.g. DEV)",
    27  	Description: `
    28  	Bump manages incremental version updates using simple semantic versioning practices.
    29  
    30  	The valid arguments are:
    31  
    32  	major - bumps the major version X._._
    33  	minor - bumps the minor version _.X._
    34  	patch - bumps the patch version _._.X
    35  	clear - clears the qualifier if any
    36  	_____ - Anything else, sets the qualifier (e.g. SNAPSHOT, DEV, ALPHA)`,
    37  	Action: bump,
    38  }
    39  
    40  func bump(c *cli.Context) error {
    41  	project, err := domain.FindProject()
    42  	if err != nil {
    43  		return err
    44  	}
    45  
    46  	oldVersion := project.Version
    47  	arg := c.Args().First()
    48  	if arg == "" {
    49  		fmt.Printf("%v\n", oldVersion)
    50  		return nil
    51  	}
    52  	if err := bumpVersion(project, arg); err != nil {
    53  		return err
    54  	}
    55  	if err := writeVersionFile(project); err != nil {
    56  		return err
    57  	}
    58  	newVersion := project.Version
    59  	fmt.Printf("Version changed from %v to %v\n", oldVersion, newVersion)
    60  	return nil
    61  }
    62  
    63  func writeVersionFile(project *domain.Project) error {
    64  	versionPath := project.ProjectPath(versionPackage)
    65  	versionFile := project.ProjectPath(versionPackage, versionFileName)
    66  
    67  	if err := validateHeader(versionFile); err != nil {
    68  		return err
    69  	}
    70  
    71  	_ = os.Mkdir(versionPath, 0755) // ignore error. we'll catch file errors later
    72  
    73  	file, err := os.Create(versionFile)
    74  	defer file.Close()
    75  	if err != nil {
    76  		return err
    77  	}
    78  	tmpl, err := template.New("version").Parse(versionTemplate)
    79  	if err != nil {
    80  		return err
    81  	}
    82  
    83  	tmpl.Execute(file, struct {
    84  		Version string
    85  		Package string
    86  	}{
    87  		Version: project.Version,
    88  		Package: versionPackage,
    89  	})
    90  	return nil
    91  }
    92  
    93  func validateHeader(versionFile string) error {
    94  	const headerLength = 10
    95  	file, err := os.Open(versionFile)
    96  	defer file.Close()
    97  	if os.IsNotExist(err) {
    98  		// it's okay if the file doesn't exist
    99  		return nil
   100  	} else if err != nil {
   101  		// but fail for any other reason
   102  		return err
   103  	}
   104  
   105  	var buffer = make([]byte, headerLength)
   106  	_, err = file.Read(buffer)
   107  	if err != nil {
   108  		return err
   109  	}
   110  	if string(buffer) != versionTemplate[:headerLength] {
   111  		return fmt.Errorf("Header in %v doesn't match, so graven won't overwrite it.", versionFile)
   112  	}
   113  	return nil
   114  }
   115  
   116  func bumpVersion(project *domain.Project, arg string) error {
   117  	version := domain.Version{}
   118  
   119  	if err := version.Parse(project.Version); err != nil {
   120  		return fmt.Errorf("Error parsing version: %v", err)
   121  	}
   122  
   123  	switch arg {
   124  	case "major":
   125  		version.Major++
   126  		version.Minor = 0
   127  		version.Patch = 0
   128  		version.Qualifier = ""
   129  	case "minor":
   130  		version.Minor++
   131  		version.Patch = 0
   132  		version.Qualifier = ""
   133  	case "patch":
   134  		version.Patch++
   135  		version.Qualifier = ""
   136  	case "clear":
   137  		version.Qualifier = ""
   138  	case "":
   139  	default:
   140  		version.Qualifier = arg
   141  	}
   142  
   143  	project.Version = version.ToString()
   144  
   145  	if err := updateProjectFileVersion(project); err != nil {
   146  		return err
   147  	}
   148  
   149  	return nil
   150  }
   151  
   152  func updateProjectFileVersion(project *domain.Project) error {
   153  	fileInfo, err := os.Stat(project.FilePath)
   154  	if err != nil {
   155  		return err
   156  	}
   157  
   158  	input, err := ioutil.ReadFile(project.FilePath)
   159  	if err != nil {
   160  		return err
   161  	}
   162  
   163  	eol := ""
   164  	lines := strings.Split(string(input), "\n")
   165  	for i, line := range lines {
   166  		if strings.HasPrefix(line, "version:") {
   167  			if strings.HasSuffix(line, "\r") {
   168  				eol = "\r"
   169  			}
   170  			lines[i] = fmt.Sprintf("version: %v%v", project.Version, eol)
   171  		}
   172  	}
   173  
   174  	output := strings.Join(lines, "\n")
   175  	if err := ioutil.WriteFile(project.FilePath, []byte(output), fileInfo.Mode()); err != nil {
   176  		return err
   177  	}
   178  
   179  	return nil
   180  }