github.com/polvi/nsproxy@v0.0.0-20140119202807-96ac732fa7f3/third_party.go (about)

     1  // +build ignore
     2  
     3  /*
     4  Copyright 2013 Brandon Philips
     5  
     6  Licensed under the Apache License, Version 2.0 (the "License");
     7  you may not use this file except in compliance with the License.
     8  You may obtain a copy of the License at
     9  
    10       http://www.apache.org/licenses/LICENSE-2.0
    11  
    12  Unless required by applicable law or agreed to in writing, software
    13  distributed under the License is distributed on an "AS IS" BASIS,
    14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  See the License for the specific language governing permissions and
    16  limitations under the License.
    17  */
    18  
    19  // This program builds a project and is a copy of third_party.go. See
    20  // github.com/philips/third_party.go
    21  //
    22  // $ go run third_party.go
    23  //
    24  // See the README file for more details.
    25  package main
    26  
    27  import (
    28  	"fmt"
    29  	"io"
    30  	"io/ioutil"
    31  	"log"
    32  	"os"
    33  	"os/exec"
    34  	"path"
    35  	"path/filepath"
    36  	"strings"
    37  )
    38  
    39  const (
    40  	DefaultThirdParty = "third_party"
    41  )
    42  
    43  // thirdPartyDir creates a string path to the third_party directory based on
    44  // the current working directory.
    45  func thirdPartyDir() string {
    46  	root, err := os.Getwd()
    47  	if err != nil {
    48  		log.Fatalf("Failed to get the current working directory: %v", err)
    49  	}
    50  	return path.Join(root, DefaultThirdParty)
    51  }
    52  
    53  func srcDir() string {
    54  	return path.Join(thirdPartyDir(), "src")
    55  }
    56  
    57  // binDir creates a string path to the GOBIN directory based on the current
    58  // working directory.
    59  func binDir() string {
    60  	root, err := os.Getwd()
    61  	if err != nil {
    62  		log.Fatalf("Failed to get the current working directory: %v", err)
    63  	}
    64  	return path.Join(root, "bin")
    65  }
    66  
    67  // run execs a command like a shell script piping everything to the parent's
    68  // stderr/stdout and uses the given environment.
    69  func run(name string, arg ...string) *os.ProcessState {
    70  	cmd := exec.Command(name, arg...)
    71  
    72  	stdout, err := cmd.StdoutPipe()
    73  	if err != nil {
    74  		fmt.Fprintf(os.Stderr, err.Error())
    75  		os.Exit(1)
    76  	}
    77  	stderr, err := cmd.StderrPipe()
    78  	if err != nil {
    79  		fmt.Fprintf(os.Stderr, err.Error())
    80  		os.Exit(1)
    81  	}
    82  	err = cmd.Start()
    83  	if err != nil {
    84  		fmt.Fprintf(os.Stderr, err.Error())
    85  		os.Exit(1)
    86  	}
    87  	go io.Copy(os.Stdout, stdout)
    88  	go io.Copy(os.Stderr, stderr)
    89  	cmd.Wait()
    90  
    91  	return cmd.ProcessState
    92  }
    93  
    94  // setupProject does the initial setup of the third_party src directory
    95  // including setting up the symlink to the cwd from the src directory.
    96  func setupProject(pkg string) {
    97  	root, err := os.Getwd()
    98  	if err != nil {
    99  		log.Fatalf("Failed to get the current working directory: %v", err)
   100  	}
   101  
   102  	src := path.Join(thirdPartyDir(), "src", pkg)
   103  	srcdir := path.Dir(src)
   104  
   105  	os.MkdirAll(srcdir, 0755)
   106  
   107  	rel, err := filepath.Rel(srcdir, root)
   108  	if err != nil {
   109  		log.Fatalf("creating relative third party path: %v", err)
   110  	}
   111  
   112  	err = os.Symlink(rel, src)
   113  	if err != nil && os.IsExist(err) == false {
   114  		log.Fatalf("creating project third party symlink: %v", err)
   115  	}
   116  }
   117  
   118  func getVc(root string) versionControl {
   119  	for _, v := range []string{".git", ".hg"} {
   120  		r := path.Join(root, v)
   121  		info, err := os.Stat(r)
   122  
   123  		if err != nil || !info.IsDir() {
   124  			continue
   125  		}
   126  
   127  		base := path.Base(r)
   128  		switch base {
   129  		case ".git":
   130  			return vcGit(r)
   131  		case ".hg":
   132  			return vcHg(r)
   133  		}
   134  	}
   135  	return new(vcNoop)
   136  }
   137  
   138  type versionControl interface {
   139  	commit() string
   140  	update(string) error
   141  }
   142  
   143  //  Performs noops on all VC operations.
   144  type vcNoop struct{}
   145  
   146  func (v *vcNoop) commit() string {
   147  	return ""
   148  }
   149  
   150  func (v *vcNoop) update(dir string) error {
   151  	return nil
   152  }
   153  
   154  type vcHg string
   155  
   156  // vcHg.commit returns the current HEAD commit hash for a given hg dir.
   157  func (v vcHg) commit() string {
   158  	out, err := exec.Command("hg", "id", "-i", "-R", string(v)).Output()
   159  	if err != nil {
   160  		return ""
   161  	}
   162  	return string(out)
   163  }
   164  
   165  // vcHg.udpate updates the given hg dir to ref.
   166  func (v vcHg) update(ref string) error {
   167  	_, err := exec.Command("hg",
   168  		"update",
   169  		"-r", ref,
   170  		"-R", string(v),
   171  		"--cwd", path.Dir(string(v)),
   172  	).Output()
   173  	if err != nil {
   174  		return err
   175  	}
   176  	return nil
   177  }
   178  
   179  type vcGit string
   180  
   181  // vcGit.commit returns the current HEAD commit hash for a given git dir.
   182  func (v vcGit) commit() string {
   183  	out, err := exec.Command("git", "--git-dir="+string(v), "rev-parse", "HEAD").Output()
   184  	if err != nil {
   185  		return ""
   186  	}
   187  	return string(out)
   188  }
   189  
   190  // vcHg.udpate updates the given git dir to ref.
   191  func (v vcGit) update(ref string) error {
   192  	_, err := exec.Command("git",
   193  		"--work-tree="+path.Dir(string(v)),
   194  		"--git-dir="+string(v),
   195  		"reset", "--hard", ref,
   196  	).Output()
   197  	if err != nil {
   198  		return err
   199  	}
   200  	return nil
   201  }
   202  
   203  // commit grabs the commit id from hg or git as a string.
   204  func commit(dir string) string {
   205  	return getVc(dir).commit()
   206  }
   207  
   208  // removeVcs removes a .git or .hg directory from the given root if it exists.
   209  func removeVcs(root string) (bool, string) {
   210  	for _, v := range []string{".git", ".hg"} {
   211  		r := path.Join(root, v)
   212  		info, err := os.Stat(r)
   213  
   214  		if err != nil {
   215  			continue
   216  		}
   217  
   218  		// We didn't find it, next!
   219  		if info.IsDir() == false {
   220  			continue
   221  		}
   222  
   223  		// We found it, grab the commit and remove the directory
   224  		c := commit(root)
   225  		err = os.RemoveAll(r)
   226  		if err != nil {
   227  			log.Fatalf("removeVcs: %v", err)
   228  		}
   229  		return true, c
   230  	}
   231  
   232  	return false, ""
   233  }
   234  
   235  // bump takes care of grabbing a package, getting the package git hash and
   236  // removing all of the version control stuff.
   237  func bump(pkg, version string) {
   238  	tpd := thirdPartyDir()
   239  
   240  	temp, err := ioutil.TempDir(tpd, "bump")
   241  	if err != nil {
   242  		log.Fatalf("bump: %v", err)
   243  	}
   244  	defer os.RemoveAll(temp)
   245  
   246  	os.Setenv("GOPATH", temp)
   247  	run("go", "get", "-u", "-d", pkg)
   248  
   249  	for {
   250  		root := path.Join(temp, "src", pkg) // the temp installation root
   251  		home := path.Join(tpd, "src", pkg)  // where the package will end up
   252  
   253  		if version != "" {
   254  			err := getVc(root).update(version)
   255  			if err != nil {
   256  				log.Fatalf("bump: %v", err)
   257  			}
   258  		}
   259  
   260  		ok, c := removeVcs(root)
   261  		if ok {
   262  			// Create the path leading up to the package
   263  			err := os.MkdirAll(path.Dir(home), 0755)
   264  			if err != nil {
   265  				log.Fatalf("bump: %v", err)
   266  			}
   267  
   268  			// Remove anything that might have been there
   269  			err = os.RemoveAll(home)
   270  			if err != nil {
   271  				log.Fatalf("bump: %v", err)
   272  			}
   273  
   274  			// Finally move the package
   275  			err = os.Rename(root, home)
   276  			if err != nil {
   277  				log.Fatalf("bump: %v", err)
   278  			}
   279  
   280  			fmt.Printf("%s %s\n", pkg, strings.TrimSpace(c))
   281  			break
   282  		}
   283  
   284  		// Pop off and try to find this directory!
   285  		pkg = path.Dir(pkg)
   286  		if pkg == "." {
   287  			return
   288  		}
   289  	}
   290  }
   291  
   292  // validPkg uses go list to decide if the given path is a valid go package.
   293  // This is used by the bumpAll walk to bump all of the existing packages.
   294  func validPkg(pkg string) bool {
   295  	env := append(os.Environ(),
   296  	)
   297  	cmd := exec.Command("go", "list", pkg)
   298  	cmd.Env = env
   299  
   300  	out, err := cmd.Output()
   301  	if err != nil {
   302  		return false
   303  	}
   304  
   305  	if pkg == strings.TrimSpace(string(out)) {
   306  		return true
   307  	}
   308  
   309  	return false
   310  }
   311  
   312  // bumpWalk walks the third_party directory and bumps all of the packages that it finds.
   313  func bumpWalk(path string, info os.FileInfo, err error) error {
   314  	if err != nil {
   315  		return nil
   316  	}
   317  
   318  	// go packages are always directories
   319  	if info.IsDir() == false {
   320  		return nil
   321  	}
   322  
   323  	parts := strings.Split(path, srcDir()+"/")
   324  	if len(parts) == 1 {
   325  		return nil
   326  	}
   327  
   328  	pkg := parts[1]
   329  
   330  	if validPkg(pkg) == false {
   331  		return nil
   332  	}
   333  
   334  	bump(pkg, "")
   335  
   336  	return nil
   337  }
   338  
   339  func bumpAll() {
   340  	err := filepath.Walk(srcDir(), bumpWalk)
   341  	if err != nil {
   342  		log.Fatalf(err.Error())
   343  	}
   344  }
   345  
   346  func main() {
   347  	log.SetFlags(0)
   348  
   349  	// third_party manages GOPATH, no one else
   350  	os.Setenv("GOPATH", thirdPartyDir())
   351  	os.Setenv("GOBIN", binDir())
   352  
   353  	if len(os.Args) <= 1 {
   354  		log.Fatalf("No command")
   355  	}
   356  
   357  	cmd := os.Args[1]
   358  
   359  	if cmd == "setup" && len(os.Args) > 2 {
   360  		setupProject(os.Args[2])
   361  		return
   362  	}
   363  
   364  	if cmd == "bump" && len(os.Args) > 2 {
   365  		ref := ""
   366  		if len(os.Args) > 3 {
   367  			ref = os.Args[3]
   368  		}
   369  
   370  		bump(os.Args[2], ref)
   371  		return
   372  	}
   373  
   374  	if cmd == "bump-all" && len(os.Args) > 1 {
   375  		bumpAll()
   376  		return
   377  	}
   378  
   379  	ps := run("go", os.Args[1:]...)
   380  
   381  	if ps.Success() == false {
   382  		os.Exit(1)
   383  	}
   384  }