// CheckAndUpdate checks whether we should update Please and does so if needed. // If it requires an update it will never return, it will either die on failure or on success will exec the new Please. // Conversely, if an update isn't required it will return. It may adjust the version in the configuration. // updatesEnabled indicates whether updates are enabled (i.e. not run with --noupdate) // updateCommand indicates whether an update is specifically requested (due to e.g. `plz update`) // forceUpdate indicates whether the user passed --force on the command line, in which case we // will always update even if the version exists. func CheckAndUpdate(config *core.Configuration, updatesEnabled, updateCommand, forceUpdate bool) { if !forceUpdate && !shouldUpdate(config, updatesEnabled, updateCommand) { return } word := describe(config.Please.Version, core.PleaseVersion, true) log.Warning("%s to Please version %s (currently %s)", word, config.Please.Version, core.PleaseVersion) // Must lock here so that the update process doesn't race when running two instances // simultaneously. core.AcquireRepoLock() defer core.ReleaseRepoLock() // If the destination exists and the user passed --force, remove it to force a redownload. newDir := core.ExpandHomePath(path.Join(config.Please.Location, config.Please.Version.String())) log.Notice("%s", newDir) if forceUpdate && core.PathExists(newDir) { if err := os.RemoveAll(newDir); err != nil { log.Fatalf("Failed to remove existing directory: %s", err) } } // Download it. newPlease := downloadAndLinkPlease(config) // Now run the new one. args := append([]string{newPlease}, os.Args[1:]...) log.Info("Executing %s", strings.Join(args, " ")) if err := syscall.Exec(newPlease, args, os.Environ()); err != nil { log.Fatalf("Failed to exec new Please version %s: %s", newPlease, err) } // Shouldn't ever get here. We should have either exec'd or died above. panic("please update failed in an an unexpected and exciting way") }
func newDirCache(config *core.Configuration) *dirCache { cache := new(dirCache) // Absolute paths are allowed. Relative paths are interpreted relative to the repo root. if config.Cache.Dir[0] == '/' { cache.Dir = config.Cache.Dir } else { cache.Dir = path.Join(core.RepoRoot, config.Cache.Dir) } // Make directory if it doesn't exist. if err := os.MkdirAll(cache.Dir, core.DirPermissions); err != nil { panic(fmt.Sprintf("Failed to create root cache directory %s: %s", cache.Dir, err)) } // Fire off the cache cleaner process. if config.Cache.DirCacheCleaner != "" && config.Cache.DirCacheCleaner != "none" { go func() { cleaner := core.ExpandHomePath(config.Cache.DirCacheCleaner) log.Info("Running cache cleaner: %s --dir %s --high_water_mark %s --low_water_mark %s", cleaner, cache.Dir, config.Cache.DirCacheHighWaterMark, config.Cache.DirCacheLowWaterMark) if _, err := syscall.ForkExec(cleaner, []string{ cleaner, "--dir", cache.Dir, "--high_water_mark", config.Cache.DirCacheHighWaterMark, "--low_water_mark", config.Cache.DirCacheLowWaterMark, }, nil); err != nil { log.Errorf("Failed to start cache cleaner: %s", err) } }() } return cache }
// downloadAndLinkPlease downloads a new Please version and links it into place, if needed. // It returns the new location and dies on failure. func downloadAndLinkPlease(config *core.Configuration) string { config.Please.Location = core.ExpandHomePath(config.Please.Location) newPlease := path.Join(config.Please.Location, config.Please.Version.String(), "please") if !core.PathExists(newPlease) { downloadPlease(config) } if !verifyNewPlease(newPlease, config.Please.Version.String()) { cleanDir(path.Join(config.Please.Location, config.Please.Version.String())) log.Fatalf("Not continuing.") } linkNewPlease(config) return newPlease }
// Parses an incoming source label as either a file or a build label. // Identifies if the file is owned by this package and returns an error if not. func parseSource(src, packageName string, systemAllowed bool) (core.BuildInput, error) { if core.LooksLikeABuildLabel(src) { return core.TryParseBuildLabel(src, packageName) } else if src == "" { return nil, fmt.Errorf("Empty source path (in package %s)", packageName) } else if strings.Contains(src, "../") { return nil, fmt.Errorf("'%s' (in package %s) is an invalid path; build target paths can't contain ../", src, packageName) } else if src[0] == '/' || src[0] == '~' { if !systemAllowed { return nil, fmt.Errorf("'%s' (in package %s) is an absolute path; that's not allowed.", src, packageName) } return core.SystemFileLabel{Path: core.ExpandHomePath(src)}, nil } else if strings.Contains(src, "/") { // Target is in a subdirectory, check nobody else owns that. for dir := path.Dir(path.Join(packageName, src)); dir != packageName && dir != "."; dir = path.Dir(dir) { if core.IsPackage(dir) { return nil, fmt.Errorf("Package %s tries to use file %s, but that belongs to another package (%s).", packageName, src, dir) } } } return core.FileLabel{File: src, Package: packageName}, nil }