func updateCLI(channel string) { if channel == "?" { // do not update dev version return } manifest, err := getUpdateManifest(channel) if err != nil { Warn("Error updating CLI") PrintError(err, false) return } if manifest.Version == Version && manifest.Channel == Channel { return } LogIfError(golock.Lock(updateLockPath)) unlock := func() { golock.Unlock(updateLockPath) } defer unlock() Errf("Updating Heroku v4 CLI to %s (%s)... ", manifest.Version, manifest.Channel) build := manifest.Builds[runtime.GOOS][runtime.GOARCH] // on windows we can't remove an existing file or remove the running binary // so we download the file to binName.new // move the running binary to binName.old (deleting any existing file first) // rename the downloaded file to binName if err := downloadBin(binPath+".new", build.URL); err != nil { panic(err) } if fileSha1(binPath+".new") != build.Sha1 { panic("SHA mismatch") } os.Remove(binPath + ".old") os.Rename(binPath, binPath+".old") if err := os.Rename(binPath+".new", binPath); err != nil { panic(err) } os.Remove(binPath + ".old") Errln("done") unlock() clearAutoupdateFile() // force full update reexec() // reexec to finish updating with new code }
func installPlugins(names ...string) error { for _, name := range names { lockfile := updateLockPath + "." + name LogIfError(golock.Lock(lockfile)) } err := gode.InstallPackage(names...) if err != nil { return err } plugins := make([]*Plugin, 0, len(names)) for _, name := range names { plugins = append(plugins, getPlugin(name, true)) } AddPluginsToCache(plugins...) for _, name := range names { lockfile := updateLockPath + "." + name LogIfError(golock.Unlock(lockfile)) } return nil }
// Setup downloads and sets up node in the RootPath directory func Setup() error { golock.Lock(lockPath) defer golock.Unlock(lockPath) t := findTarget() if t == nil { return errors.New(`node does not offer a prebuilt binary for your OS. You'll need to compile the tarball from nodejs.org and place it in ~/.heroku/node-v` + Version) } exists, err := t.isSetup() if err != nil { return err } if exists { return nil } if err := t.setup(); err != nil { return err } SetRootPath(rootPath) // essentially sets this node as the current one return t.clearOldNodeInstalls() }
func updatePlugins() { plugins := PluginNamesNotSymlinked() if len(plugins) == 0 { return } Err("Updating plugins... ") packages, err := gode.OutdatedPackages(plugins...) PrintError(err) if len(packages) > 0 { for name, version := range packages { lockfile := updateLockPath + "." + name LogIfError(golock.Lock(lockfile)) err := gode.InstallPackage(name + "@" + version) PrintError(err) AddPluginsToCache(getPlugin(name, true)) LogIfError(golock.Unlock(lockfile)) } Errf("done. Updated %d %s.\n", len(packages), plural("package", len(packages))) } else { Errln("no plugins to update.") } }
// unlock a plugin func unlockPlugin(name string) { LogIfError(golock.Unlock(updateLockPath + "." + name)) }
func runFn(plugin *Plugin, topic, command string) func(ctx *Context) { return func(ctx *Context) { lockfile := updateLockPath + "." + plugin.Name if exists, _ := fileExists(lockfile); exists { golock.Lock(lockfile) golock.Unlock(lockfile) } ctx.Dev = isPluginSymlinked(plugin.Name) ctxJSON, err := json.Marshal(ctx) if err != nil { panic(err) } title, _ := json.Marshal(processTitle(ctx)) script := fmt.Sprintf(` 'use strict'; var moduleName = '%s'; var moduleVersion = '%s'; var topic = '%s'; var command = '%s'; process.title = %s; var ctx = %s; ctx.version = ctx.version + ' ' + moduleName + '/' + moduleVersion + ' node-' + process.version; var logPath = %s; process.chdir(ctx.cwd); function repair (name) { console.error('Attempting to repair ' + name + '...'); require('child_process') .spawnSync('heroku', ['plugins:install', name], {stdio: [0,1,2]}); console.error('Repair complete. Try running your command again.'); } if (!ctx.dev) { process.on('uncaughtException', function (err) { console.error(' ! Error in ' + moduleName + ':') if (err.message) { console.error(' ! ' + err.message); if (err.message.indexOf('Cannot find module') != -1) { repair(moduleName); process.exit(1); } } else { console.error(' ! ' + err); } if (err.stack) { var fs = require('fs'); var log = function (line) { var d = new Date().toISOString() .replace(/T/, ' ') .replace(/-/g, '/') .replace(/\..+/, ''); fs.appendFileSync(logPath, d + ' ' + line + '\n'); } log('Error during ' + topic + ':' + command); log(err.stack); console.error(' ! See ' + logPath + ' for more info.'); } process.exit(1); }); } if (command === '') { command = null } var module = require(moduleName); var cmd = module.commands.filter(function (c) { return c.topic === topic && c.command == command; })[0]; cmd.run(ctx);`, plugin.Name, plugin.Version, topic, command, string(title), ctxJSON, strconv.Quote(ErrLogPath)) // swallow sigint since the plugin will handle it swallowSignal(os.Interrupt) cmd := gode.RunScript(script) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if ctx.Flags["debugger"] == true { cmd = gode.DebugScript(script) } if err := cmd.Run(); err != nil { os.Exit(getExitCode(err)) } } }