func (dir *CharmDir) MaybeGenerateVersionString(logger Logger) (string, string, error) {
	// vcsStrategies is the strategies to use to access the version file content.
	vcsStrategies := map[string]vcsCMD{
		"hg": vcsCMD{
			vcsType:       "hg",
			args:          []string{"id", "-n"},
			usesTypeCheck: usesHg,
		},
		"git": vcsCMD{
			vcsType:       "git",
			args:          []string{"describe", "--dirty", "--always"},
			usesTypeCheck: usesGit,
		},
		"bzr": vcsCMD{
			vcsType:       "bzr",
			args:          []string{"version-info"},
			usesTypeCheck: usesBzr,
		},
	}

	// Nowadays most vcs used are git, we want to make sure that git is the first one we test
	vcsOrder := [...]string{"git", "hg", "bzr"}
	cmdWaitTime := 2 * time.Second

	absPath := dir.Path
	if !filepath.IsAbs(absPath) {
		var err error
		absPath, err = filepath.Abs(dir.Path)
		if err != nil {
			return "", "", errors.Annotatef(err, "failed resolving relative path %q", dir.Path)
		}
	}

	for _, vcsType := range vcsOrder {
		vcsCmd := vcsStrategies[vcsType]
		ctx, cancel := context.WithTimeout(context.Background(), cmdWaitTime)
		if vcsCmd.usesTypeCheck(ctx, dir.Path, cancel) {
			cmd := exec.Command(vcsCmd.vcsType, vcsCmd.args...)
			// We need to make sure that the working directory will be the one we execute the commands from.
			cmd.Dir = dir.Path
			// Version string value is written to stdout if successful.
			out, err := cmd.Output()
			if err != nil {
				// We had an error but we still know that we use a vcs, hence we can stop here and handle it.
				return "", vcsType, vcsCmd.commonErrHandler(err, absPath)
			}
			output := strings.TrimSuffix(string(out), "\n")
			return output, vcsType, nil
		}
	}

	// If all strategies fail we fallback to check the version below
	if file, err := os.Open(dir.join("version")); err == nil {
		logger.Debugf("charm is not in version control, but uses a version file, charm path %q", absPath)
		ver, err := ReadVersion(file)
		file.Close()
		if err != nil {
			return "", versionFileVersionType, err
		}
		return ver, versionFileVersionType, nil
	}
	logger.Infof("charm is not versioned, charm path %q", absPath)
	return "", "", nil
}
// MaybeGenerateVersionString generates charm version string.
// We want to know whether parent folders use one of these vcs, that's why we
// try to execute each one of them
// The second return value is the detected vcs type.
MaybeGenerateVersionString generates charm version string.
We want to know whether parent folders use one of these vcs, that's why we
try to execute each one of them
The second return value is the detected vcs type.
// MaybeGenerateVersionString generates charm version string . // We want to know whether parent folders use one of these vcs that ' s why we // try to execute each one of them // The second return value is the detected vcs type .
func (dir *CharmDir) MaybeGenerateVersionString(logger Logger) (string, string, error) {
	vcsStrategies := map[string]vcsCMD{
		"hg": vcsCMD{
			vcsType:       "hg",
			args:          []string{"id", "-n"},
			usesTypeCheck: usesHg,
		},
		"git": vcsCMD{
			vcsType:       "git",
			args:          []string{"describe", "--dirty", "--always"},
			usesTypeCheck: usesGit,
		},
		"bzr": vcsCMD{
			vcsType:       "bzr",
			args:          []string{"version-info"},
			usesTypeCheck: usesBzr,
		},
	}
	vcsOrder := [...]string{"git", "hg", "bzr"}
	cmdWaitTime := 2 * time.Second
	absPath := dir.Path
	if !filepath.IsAbs(absPath) {
		var err error
		absPath, err = filepath.Abs(dir.Path)
		if err != nil {
			return "", "", errors.Annotatef(err, "failed resolving relative path %q", dir.Path)
		}
	}
	for _, vcsType := range vcsOrder {
		vcsCmd := vcsStrategies[vcsType]
		ctx, cancel := context.WithTimeout(context.Background(), cmdWaitTime)
		if vcsCmd.usesTypeCheck(ctx, dir.Path, cancel) {
			cmd := exec.Command(vcsCmd.vcsType, vcsCmd.args...)
			cmd.Dir = dir.Path
			out, err := cmd.Output()
			if err != nil {
				return "", vcsType, vcsCmd.commonErrHandler(err, absPath)
			}
			output := strings.TrimSuffix(string(out), "\n")
			return output, vcsType, nil
		}
	}
	if file, err := os.Open(dir.join("version")); err == nil {
		logger.Debugf("charm is not in version control, but uses a version file, charm path %q", absPath)
		ver, err := ReadVersion(file)
		file.Close()
		if err != nil {
			return "", versionFileVersionType, err
		}
		return ver, versionFileVersionType, nil
	}
	logger.Infof("charm is not versioned, charm path %q", absPath)
	return "", "", nil
}
func ( dir * CharmDir ) MaybeGenerateVersionString ( logger Logger ) ( string , string , error ) { vcsStrategies := map [ string ] vcsCMD { " " : vcsCMD { vcsType : " " , args : [ ] string { " " , " " } , usesTypeCheck : usesHg , } , " " : vcsCMD { vcsType : " " , args : [ ] string { " " , " " , " " } , usesTypeCheck : usesGit , } , " " : vcsCMD { vcsType : " " , args : [ ] string { " " } , usesTypeCheck : usesBzr , } , } vcsOrder := [ ... ] string { " " , " " , " " } cmdWaitTime := 2 * time . Second absPath := dir . Path if ! filepath . IsAbs ( absPath ) { var err error absPath , err = filepath . Abs ( dir . Path ) if err != nil { return " " , " " , errors . Annotatef ( err , " " , dir . Path ) } } for _ , vcsType := range vcsOrder { vcsCmd := vcsStrategies [ vcsType ] ctx , cancel := context . WithTimeout ( context . Background ( ) , cmdWaitTime ) if vcsCmd . usesTypeCheck ( ctx , dir . Path , cancel ) { cmd := exec . Command ( vcsCmd . vcsType , vcsCmd . args ... ) cmd . Dir = dir . Path out , err := cmd . Output ( ) if err != nil { return " " , vcsType , vcsCmd . commonErrHandler ( err , absPath ) } output := strings . TrimSuffix ( string ( out ) , " \n " ) return output , vcsType , nil } } if file , err := os . Open ( dir . join ( " " ) ) ; err == nil { logger . Debugf ( " " , absPath ) ver , err := ReadVersion ( file ) file . Close ( ) if err != nil { return " " , versionFileVersionType , err } return ver , versionFileVersionType , nil } logger . Infof ( " " , absPath ) return " " , " " , nil }
MaybeGenerateVersionString generates charm version string.
MaybeGenerateVersionString generates charm version string .
// vcsStrategies is the strategies to use to access the version file content. // Nowadays most vcs used are git, we want to make sure that git is the first one we test // We need to make sure that the working directory will be the one we execute the commands from. // Version string value is written to stdout if successful. // We had an error but we still know that we use a vcs, hence we can stop here and handle it. // If all strategies fail we fallback to check the version below