Mercurial and Git clients can push and pull from this alias URL to interact with this repository. You can change to which repository an alias points by going to the Aliases link on the project page.
packagekilnimport("bufio""bytes""encoding/json""fmt""io""io/ioutil""net/http""net/url""os""os/exec""os/user""path/filepath""runtime""strings")// Holds the bare minimum amount of information required to talk to a Kiln instancetypeKilnClientstruct{credentials*kilnCredential}// Stores kiln credentials in the Kiln configuration filetypekilnCredentialstruct{// Base URL of Kiln instanceKilnUrlstring`json:"kilnUrl"`// User for whom this token appliesUserstring`json:"user"`// Kiln API tokenTokenstring`json:"token"`}typeapiParamsmap[string]stringtypeKilnCredentialsmap[string]map[string]stringfuncNewKilnClient(kilnUrl*url.URL)*KilnClient{user:=""ifkilnUrl.User!=nil{user=kilnUrl.User.Username()}return&KilnClient{&kilnCredential{KilnUrl:kilnUrl.String(),User:user}}}func(k*KilnClient)LoadCredentials()bool{ifcreds,err:=loadCredentials();err==nil{iftoken,ok:=creds[k.credentials.User][k.credentials.KilnUrl];ok{k.credentials.Token=tokenreturntrue}}returnfalse}func(k*KilnClient)StoreCredentials()(errerror){creds,_:=loadCredentials()if_,ok:=creds[k.credentials.User];!ok{creds[k.credentials.User]=make(map[string]string)}creds[k.credentials.User][k.credentials.KilnUrl]=k.credentials.Tokenerr=creds.storeCredentials()return}func(k*KilnClient)DeleteCredentials()(errerror){creds,err:=loadCredentials()iferr!=nil{return}ifuser,ok:=creds[k.credentials.User];ok{delete(user,k.credentials.KilnUrl)}err=creds.storeCredentials()return}// Logs a user into Kiln, returning true and storing their token in the// KilnClient if successful, and returning an error otherwisefunc(k*KilnClient)Logon()error{login,password:=requestUserCredentials()resp,err:=k.apiGet("Auth/Login",apiParams{"sUser":login,"sPassword":password})iferr!=nil{returnfmt.Errorf("unable to contact Kiln: %v\n",err)}varerrorsmap[string][]KilnErroriferr=json.Unmarshal(resp,&errors);err==nil{iferr,_:=errors["errors"];len(err)>0{returnfmt.Errorf("failed: %v\n",err[0].Description)}}iferr=json.Unmarshal(resp,&k.credentials.Token);err!=nil{returnfmt.Errorf("failed to parse token: %v",err)}returnnil}// Makes sure the client has credentials, taking them through the logon// sequence if notfunc(k*KilnClient)EnsureCredentials()error{ifk.credentials.Token==""&&!k.LoadCredentials(){iferr:=k.Logon();err!=nil{returnerr}k.StoreCredentials()}returnnil}// Browses the history tab of the repositoryfunc(k*KilnClient)BrowseHistory(repostring)error{returnbrowse(k.repoRoute(repo,""))}// Browse the settings tab for the repositoryfunc(k*KilnClient)BrowseSettings(repostring)error{returnbrowse(k.repoRoute(repo,"Settings"))}// Browse the related tab for repositoryfunc(k*KilnClient)BrowseRelated(repostring)error{returnbrowse(k.repoRoute(repo,"Related"))}// Browse a commit, expanding out to the full SHA beforehandfunc(k*KilnClient)BrowseCommit(repostring,commitstring)(errerror){ifout,err:=exec.Command("git","rev-parse",commit).Output();err==nil{commit=strings.TrimSpace(string(out))ifstrings.HasPrefix(commit,"fatal:"){err=fmt.Errorf("commit couldn't be resolved (try \"git fetch\")\n")}else{browse(k.repoRoute(repo,"History/"+commit))}}return}// Browse a file in Kilnfunc(k*KilnClient)BrowseFile(repostring,filestring)error{path,err:=repoRelativePath(file)iferr!=nil{returnerr}returnbrowse(k.repoRoute(repo,fmt.Sprintf("Files%v",path)))}// Browse an annotated file in Kilnfunc(k*KilnClient)BrowseAnnotatedFile(repostring,filestring)error{path,err:=repoRelativePath(file)iferr!=nil{returnerr}returnbrowse(k.repoRoute(repo,fmt.Sprintf("Files%v?view=annotate",path)))}// Browse a file in Kilnfunc(k*KilnClient)BrowseFileHistory(repostring,filestring)error{path,err:=repoRelativePath(file)iferr!=nil{returnerr}returnbrowse(k.repoRoute(repo,fmt.Sprintf("FileHistory%v",path)))}// Find the root of the Git repofuncGitRoot()(pathstring,errerror){out,err:=exec.Command("git","rev-parse","--show-toplevel").Output()iferr!=nil{return}path=strings.TrimSpace(string(out))ifstrings.HasPrefix(path,"fatal:"){err=fmt.Errorf("unable to find root: %v",path)}return}// Opens a web browser in a cross-platform wayfuncbrowse(locationstring)error{switchruntime.GOOS{case"linux":returnexec.Command("xdg-open",location).Start()case"windows":returnexec.Command("cmd","/c","start",location).Start()case"darwin":returnexec.Command("open",location).Start()default:returnfmt.Errorf("%v is an unsupported platform",runtime.GOOS)}}// Change a relative or absolute path into a path relative to the repository rootfuncrepoRelativePath(pathstring)(string,error){root,err:=GitRoot()iferr!=nil{return"",err}absPath,err:=filepath.Abs(path)iferr!=nil{return"",err}absPath=filepath.ToSlash(absPath)returnstrings.TrimPrefix(absPath,root),nil}// Returns the full URL for relative Kiln URLfunc(k*KilnClient)kilnRoute(routestring)string{returnstrings.TrimRight(k.credentials.KilnUrl,"/")+"/"+strings.TrimLeft(route,"/")}// Returns the full URL for an API call in Kilnfunc(k*KilnClient)apiRoute(routestring)string{returnk.kilnRoute("Api/1.0/"+strings.TrimLeft(route,"/"))}// Returns the full URL for a given API call or Kiln routefunc(k*KilnClient)repoRoute(repostring,actionstring)string{returnk.kilnRoute(fmt.Sprintf("Code/%v/%v",repo,action))}// Returns the body from a Get API callfunc(k*KilnClient)apiGet(routestring,paramsapiParams)([]byte,error){v:=url.Values{} for key, value := range params {
v.Set(key, value)
}
+ if k.credentials.Token != "" {+ v.Set("token", k.credentials.Token)+ } resp, err := http.Get(k.apiRoute(route) + "?" + v.Encode())
if err != nil {
return nil, err
}deferresp.Body.Close()returnioutil.ReadAll(resp.Body)}// Encode a path by Kiln's hex encodingfunchexEncoded(pathstring)string{utf8:=[]byte(path)hexBytes:=make([]string,len(utf8))foridx,b:=rangeutf8{hexBytes[idx]=fmt.Sprintf("%x",b)}returnstrings.Join(hexBytes,"")}// Request new credentials from the user, securelyfuncrequestUserCredentials()(login,passwordstring){scanner:=bufio.NewScanner(os.Stdin)for{fmt.Print("Login: ")scanner.Scan()login=scanner.Text()ifstrings.Trim(login,"\t ")==""{fmt.Println("Please enter your Kiln name or email address")continue}password,_=getPass("Password: ")return}}// Load any existing credentials from the user's credential storefuncloadCredentials()(credentialsKilnCredentials,errerror){credentials=make(KilnCredentials)path:=filepath.Join(configDirectory(),"kiln_client.json")fd,err:=os.Open(path)iferr!=nil{return}deferfd.Close()data,err:=ioutil.ReadAll(fd)iferr!=nil{return}varcreds[]kilnCredentialiferr=json.Unmarshal(data,&creds);err!=nil{return}for_,cred:=rangecreds{if_,ok:=credentials[cred.User];!ok{credentials[cred.User]=make(map[string]string)}credentials[cred.User][cred.KilnUrl]=cred.Token}return}// Store all credentials in the credential store, overwriting any already presentfunc(credentialsKilnCredentials)storeCredentials()(errerror){iferr=os.MkdirAll(configDirectory(),0700);err!=nil{return}path:=filepath.Join(configDirectory(),"kiln_client.json")fd,err:=os.Create(path)iferr!=nil{return}deferfd.Close()creds:=make([]*kilnCredential,0,10)foruser,urls:=rangecredentials{forurl,token:=rangeurls{creds=append(creds,&kilnCredential{KilnUrl:url,User:user,Token:token})}}data,_:=json.Marshal(creds)_,err=io.Copy(fd,bytes.NewReader(data))return}// Finds the directory in which to store Kiln files. Platform-dependent.funcconfigDirectory()string{switchruntime.GOOS{case"windows":returnfilepath.Join(os.Getenv("APPDATA"),"Kiln")default:usr,err:=user.Current()iferr!=nil{panic("unable to determine current user")}returnfilepath.Join(usr.HomeDir,".config","kiln")}}
Attach a Trello Card
Add a tag
Your session has expired
You are no longer logged in. Please log in and try your request again.
Filter RSS Feed
This RSS feed URL allows you to see the contents of your current filter using any feed reader.
This link includes a special authentication token. If you share the URL with anyone else, they can see this RSS feed's activity. You can disable these tokens when needed.
Your current filter is unsaved; changing it won't affect this RSS feed.