func GetBuildInfo(env Env, job string, id int) (*BuildInfo, error) {
	code, rsp, _, err := req(env, "POST", "job/"+job+"/"+strconv.Itoa(id)+"/api/json", []byte{})
	if err != nil {
		panic(err)
	}
	if code != 200 {
		return nil, errors.New("failed to get job details,code" + strconv.Itoa(code) + ", " + string(rsp))
	}
	var bi BuildInfo
	err = json.Unmarshal(rsp, &bi)
	if err != nil {
		return nil, err
	}
	return &bi, nil
}
func GetJobParameterDefinitions(env Env, jobName string) []ParameterDefinitions {
bundle:=GetBundle(env)
params:= []ParameterDefinitions{}
for _,jp:= range bundle.JobsParameters{
if jp.Name == jobName{
params = jp.Parameters
}
}

var fetchParameters = func() []ParameterDefinitions{
jobinfo:=GetJobInfo(env,jobName)
parameters:=jobinfo.GetParameterDefinitions()
mutex.Lock()
defer mutex.Unlock()
bundle.JobsParameters = append(bundle.JobsParameters,JobsParameters{Name: jobName, Parameters: parameters})
updateCache(env, bundle)
return parameters
}

if len(params)>0{
fmt.Println("async")
go fetchParameters()
}else{
fmt.Println("sync")
return fetchParameters()
}
return params



}
package jj
import (
	"encoding/json"
	"errors"
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
)
func GetBundle(env Env) *Bundle {
	for _, b := range bundles {
		if b.Name == env.Name {
			return b
		}
	}
	return nil
}
func DelEnv(name EName) error {
	for i, e := range config.Envs {
		if e.Name == name {
			config.Envs = append(config.Envs[:i], config.Envs[i+1:]...)
			SetConf()
			return nil
		}
	}
	return fmt.Errorf("'%s' name is not found", name)
}
func Check(env Env) error {
	code, rspbin, _, err := req(env, "GET", "api/json", []byte{})
	if err != nil {
		return err
	}
	if code != 200 {
		return fmt.Errorf("http code: %d\nresponse: %s", code, rspbin)
	}
	return nil
}
func GetEnvs() []Env {
	return config.Envs
}
func GetEnv(name string) (error, Env) {
	var env Env
	if name == "" {
		name = string(GetDefEnv())
	}
	for _, e := range GetEnvs() {
		if e.Name == EName(name) {
			env = e
			break
		}
	}
	if env == (Env{}) {
		return ErrNoEnv, env
	}
	return nil, env
}
func (ji *JobInfo) GetParameterDefinitions() []ParameterDefinitions {
	for _, j := range ji.Property {
		if len(j.ParameterDefinitions) > 0 {
			return j.ParameterDefinitions
		}
	}
	return []ParameterDefinitions{}
}
func GetDefEnv() EName {
	if config.Use == "" {
		return GetEnvs()[0].Name
	}
	return config.Use
}
func SetDef(eName string) {
	var env Env
	for _, e := range GetEnvs() {
		if e.Name == EName(eName) {
			env = e
			break
		}
	}
	if env == (Env{}) {
		panic("Environment " + eName + " is not found or could not be initialised")
	}
	config.Use = env.Name
	SetConf()
}
func SetConf() {
	out, _ := yaml.Marshal(config)
	if _, err := os.Stat(homeDir); os.IsNotExist(err) {
		err := os.MkdirAll(homeDir, os.ModePerm)
		if err != nil {
			panic(err)
		}
	}
	err := ioutil.WriteFile(homeDir+configFile, out, 0644)
	if err != nil {
		panic(err)
	}
}
func SetEnv(env Env) {
	added := false
	for i, e := range config.Envs {
		if e.Name == env.Name {
			config.Envs[i] = env
			added = true
			break
		}
	}
	if !added {
		config.Envs = append(config.Envs, env)
	}
	SetConf()
}
func GetJobInfo(env Env, jobName string) (error, *JobInfo) {
	bundle := GetBundle(env)
	var jobInfo *JobInfo
	for _, ji := range bundle.JobsInfo {
		if ji.Name == jobName {
			jobInfo = &ji
			break
		}
	}
	var fetchJobInfo = func() (error, JobInfo) {
		var ji JobInfo
		code, rsp, _, err := req(env, "POST", "job/"+jobName+"/api/json", []byte{})
		if err != nil {
			return err, ji
		}
		if code != 200 {
			return ErrNoJob, ji
		}
		err = json.Unmarshal(rsp, &ji)
		if err != nil {
			panic("failed to get Job information")
		}
		mutex.Lock()
		defer mutex.Unlock()
		bundle.JobsInfo = append(bundle.JobsInfo, ji)
		updateCache(env, bundle)
		return nil, ji
	}
	if jobInfo != nil {
		go fetchJobInfo()
	} else {
		err, ji := fetchJobInfo()
		if err != nil {
			return err, jobInfo
		}
		jobInfo = &ji
	}
	return nil, jobInfo
}