Пример #1
0
func downloadReleaseFromGithub(r repo, artifact string, alias string) {
	client := github.NewClient(nil)

	config, err := loadConfig()
	if err != nil {
		DieWithError(err, "Could not load user config")
	}

	if token, ok := config.Config["token"]; ok {
		log.Debug("Using Github token from config.")
		// if the user has a token stored, use it
		ts := oauth2.StaticTokenSource(
			&oauth2.Token{AccessToken: token},
		)
		tc := oauth2.NewClient(oauth2.NoContext, ts)
		client = github.NewClient(tc)
	} else {
		log.Debug("No Github token found in config. Add it with `mup config` if you need it")
	}

	// with a provided token, this will also get repos for the authenticated user
	// TODO: support pagination
	releases, resp, err := client.Repositories.ListReleases(r.Owner, r.Repo, nil)
	if resp.StatusCode == 404 {
		Die("Could not find repository %s/%s. Maybe it doesn't exist, or you need to add your token for μpdater to have access.", r.Owner, r.Repo)
	} else if err != nil {
		DieWithError(err, "Error reaching Github")
	}

	updateFromGithubReleases(client, r, releases, "", artifact, alias)
}
Пример #2
0
// RemoveApp - remove an existing app
func RemoveApp(c *cli.Context) {
	r := getRepoFromCli(c)

	cacheDir := getCacheDir()
	ownerPath := filepath.Join(cacheDir, r.Owner)
	repoPath := filepath.Join(cacheDir, r.Owner, r.Repo)

	// remove the repo directory
	if err := os.RemoveAll(repoPath); err != nil {
		DieWithError(err, "Could not remove app")
	}

	// if the owner directory is empty, remove it too
	if _, err := os.Stat(ownerPath); os.IsNotExist(err) {
		log.Debug("No libraries from user/org %s have been added. Nothing left to do", r.Owner)
		os.Exit(0)
	}

	files, err := ioutil.ReadDir(ownerPath)
	if err != nil {
		DieWithError(err, "Could not get contents of %s to see if it needed to be removed", ownerPath)
	}

	if len(files) > 0 {
		log.Debug("Directory for user/org %s is not empty, so not deleting it.", r.Owner)
		os.Exit(0)
	}

	log.Debug("No remaining apps for %s. Deleting parent directory", r.Owner)
	if err = os.RemoveAll(ownerPath); err != nil {
		DieWithError(err, "Unable to cleanup parent directory for user/org at %s", ownerPath)
	}
}
Пример #3
0
func loadConfig() (config, error) {
	var cfg config

	configPath, err := getConfigPath()
	if err != nil {
		return cfg, err
	}

	// if the config file doesn't exist, return a new one
	if _, err = os.Stat(configPath); os.IsNotExist(err) {
		log.Debug("Config file doesn't exist. Creating an empty config.")
		cfg = config{
			Version: 1,
			Config:  map[string]string{},
		}
		return cfg, nil
	}

	configFile, err := os.Open(configPath)
	if err != nil {
		return cfg, err
	}

	jsonParser := json.NewDecoder(configFile)
	if err = jsonParser.Decode(&cfg); err != nil {
		return cfg, err
	}
	return cfg, nil
}
Пример #4
0
// DieWithError - print the msg and exit with unsuccessful code
func DieWithError(err error, msg string, a ...interface{}) {
	log.Error(msg, a...)
	if err != nil {
		log.Debug(err.Error())
	}
	os.Exit(1)
}
Пример #5
0
// AddApp - add a new app to be managed
func AddApp(c *cli.Context) {
	r := getRepoFromCli(c)

	artifact := c.String("artifact")
	alias := c.String("alias")

	// create the repo dir; if it already exists, exit with success
	_, alreadyExists := ensureRepoDir(r)
	if alreadyExists {
		log.Debug("This app has already been added")
		os.Exit(0)
	}

	// TODO: if we fail to add an app, we don't want to leave the directory around
	downloadReleaseFromGithub(r, artifact, alias)
}
Пример #6
0
// RemoveConfigEntry - remove config
func RemoveConfigEntry(c *cli.Context) {
	if len(c.Args()) != 1 {
		Die("You must provide the key of the config to remove")
	}

	cfg, err := loadConfig()
	if err != nil {
		DieWithError(err, "Could not load existing config")
	}

	key := c.Args()[0]
	log.Debug("Removing config entry for %s", key)

	delete(cfg.Config, key)
	persistConfig(cfg)
}
Пример #7
0
// AddConfigEntry - add config
func AddConfigEntry(c *cli.Context) {
	if len(c.Args()) != 2 {
		Die("You must provide a key and a value.")
	}

	cfg, err := loadConfig()
	if err != nil {
		DieWithError(err, "Could not load existing config")
	}

	key := c.Args()[0]
	value := c.Args()[1]
	log.Debug("Addding config entry %s=%s", key, value)

	cfg.Config[key] = value
	persistConfig(cfg)
}
Пример #8
0
// ListConfigEntries - list config
func ListConfigEntries(c *cli.Context) {
	cfg, err := loadConfig()
	if err != nil {
		DieWithError(err, "Could not load existing config")
	}

	log.Debug("Listing existing config")
	keys := make([]string, len(cfg.Config))
	longest := 0
	i := 0
	for k := range cfg.Config {
		keys[i] = k
		if len(k) > longest {
			longest = len(k)
		}
		i++
	}

	sort.Strings(keys)
	for _, key := range keys {
		fmt.Fprintf(os.Stdout, "%-*s -> %v\n", longest, key, cfg.Config[key])
	}
}
Пример #9
0
func updateFromGithubReleases(client *github.Client, r repo, releases []github.RepositoryRelease, version string, artifact string, alias string) {
	// TODO: clean up this logic (e.g. use a Matcher -> is the version requested or greater than existing version)
	searchSV, err := semver.Make(cleanVersionName(version))
	hasSearchVersion := false
	if version != "" && err != nil {
		DieWithError(err, "Cannot update to a non-semver compatible version %s", version)
	} else if version != "" {
		hasSearchVersion = true
	}

	// TODO: shouldn't need to load this again
	cacheDir := getCacheDir()
	repoPath := filepath.Join(cacheDir, r.Owner, r.Repo)

	// find the ID of the last version we updated to (directory on disk)
	files, err := ioutil.ReadDir(repoPath)
	if err != nil {
		DieWithError(err, "Could not read from repo directory %s", r.Repo)
	}

	var newest semver.Version
	for _, f := range files {
		fname := cleanVersionName(f.Name())

		sv, err := semver.Make(fname)
		if err != nil {
			log.Debug("Found directory with a non-semver name: %s. Skipping.", f.Name())
			continue
		}

		// just skip non-semver directories for now
		if sv.Compare(newest) > 0 {
			newest = sv
		}
	}

	var releaseToInstall github.RepositoryRelease
	foundRelease := false
	for _, release := range releases {
		rname := cleanVersionName(*release.TagName)
		sv, err := semver.Make(rname)
		if err != nil {
			log.Debug("Found release version with a non-semver name: %s. Skipping", *release.TagName)
		}

		if *release.Draft || *release.Prerelease {
			log.Debug("Skipping release %s because it's a draft or a pre-release", *release.TagName)
		}

		if hasSearchVersion && searchSV.Equals(sv) {
			foundRelease = true
			releaseToInstall = release
		} else if sv.Compare(newest) > 0 {
			foundRelease = true
			releaseToInstall = release
		}

		if foundRelease {
			break
		}
	}

	if !foundRelease {
		if hasSearchVersion {
			Die("Could not find version %s", version)
		} else {
			Die("Could not find a newer version to install")
		}
	}

	// TODO: install this version
	releasePath := filepath.Join(cacheDir, r.Owner, r.Repo, *releaseToInstall.TagName)
	if err := os.Mkdir(releasePath, 0740); err != nil {
		DieWithError(err, "Could not create cache directory for %s/%s/%s", r.Owner, r.Repo, *releaseToInstall.TagName)
	}

	if len(releaseToInstall.Assets) > 1 && len(artifact) == 0 {
		// TODO: we shouldn't create the directory until we're sure we can install
		Die("Multiple artifacts for this app - you must specify only one with --artifact")
	} else {
		artifact = *releaseToInstall.Assets[0].Name
	}

	if len(alias) == 0 {
		log.Debug("No alias specified - using app name: %s", artifact)
		alias = artifact
	}

	for _, asset := range releaseToInstall.Assets {
		if *asset.Name != artifact {
			continue
		}

		closer, redirect, err := client.Repositories.DownloadReleaseAsset(r.Owner, r.Repo, *asset.ID)
		if closer == nil {
			resp, e := http.Get(redirect)
			if e != nil {
				DieWithError(e, "Could not follow redirect to asset: %s", redirect)
			}
			closer = resp.Body
		}
		log.Debug("Following redirect to download %s", *asset.Name)
		if err != nil {
			DieWithError(err, "Could not download asset %s", *asset.Name)
		}

		assetFile := filepath.Join(releasePath, *asset.Name)
		file, err := os.Create(assetFile)
		if err != nil {
			DieWithError(err, "Could not create file for %s", *asset.Name)
		}

		defer closer.Close()
		if _, err = io.Copy(file, closer); err != nil {
			DieWithError(err, "Could not write %s", *asset.Name)
		}
		log.Debug("Wrote asset to disk: %s", *asset.Name)
		if err = os.Chmod(assetFile, 0744); err != nil {
			DieWithError(err, "Could not add execute permission to %s", *asset.Name)
		}

		// create symlink in /usr/local/bin
		linkPath := filepath.Join("/usr/local/bin", alias)
		log.Debug("Creating link %s -> %s", linkPath, assetFile)
		os.Symlink(assetFile, linkPath)
	}

}