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 instancetypeClientstruct{credentials*credential}// Stores kiln credentials in the Kiln configuration filetypecredentialstruct{// Base URL of Kiln instanceKilnUrlstring`json:"kilnUrl"`// User for whom this token appliesUserstring`json:"user"`// Kiln API tokenTokenstring`json:"token"`}type apiParams map[string]string
+func (p *apiParams) Values() url.Values {+ v := url.Values{}+ for key, value := range *p {+ v.Set(key, value)+ }+ return v+}+type Credentials map[string]map[string]string
func NewClient(kilnUrl *url.URL) *Client {
user:=""ifkilnUrl.User!=nil{user=kilnUrl.User.Username()}return&Client{&credential{KilnUrl:kilnUrl.String(),User:user}}}func(k*Client)LoadCredentials()bool{ifcreds,err:=loadCredentials();err==nil{iftoken,ok:=creds[k.credentials.User][k.credentials.KilnUrl];ok{k.credentials.Token=tokenreturntrue}}returnfalse}func(k*Client)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*Client)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// Client if successful, and returning an error otherwisefunc(k*Client)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",err)}varerrorsApiErrorsiferr=json.Unmarshal(resp,&errors);err==nil{iferr,_:=errors["errors"];len(err)>0{returnfmt.Errorf("failed: %v",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*Client)EnsureCredentials()error{ifk.credentials.Token==""&&!k.LoadCredentials(){iferr:=k.Logon();err!=nil{returnerr}k.StoreCredentials()}returnnil}// Resolve a Git SHAfunc(k*Client)ResolveSHA(commitstring)(string,error){ifout,err:=exec.Command("git","rev-parse",commit).CombinedOutput();err==nil{commit:=strings.TrimSpace(string(out))ifstrings.HasPrefix(commit,"fatal:"){return"",fmt.Errorf("commit couldn't be resolved (try \"git fetch\" first)")}else{returncommit,nil}}return"",fmt.Errorf("commit couldn't be resolved (try \"git fetch\" first)")}// Browses the history tab of the repositoryfunc(k*Client)BrowseHistory(repostring)error{returnbrowse(k.repoRoute(repo,""))}// Browse the settings tab for the repositoryfunc(k*Client)BrowseSettings(repostring)error{returnbrowse(k.repoRoute(repo,"Settings"))}// Browse the related tab for repositoryfunc(k*Client)BrowseRelated(repostring)error{returnbrowse(k.repoRoute(repo,"Related"))}// Browse a commit, expanding out to the full SHA beforehandfunc(k*Client)BrowseCommit(repostring,commitstring)(errerror){ifcommit,err=k.ResolveSHA(commit);err==nil{err=browse(k.repoRoute(repo,"History/"+commit))}return}// Browse a file in Kilnfunc(k*Client)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*Client)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*Client)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*Client)kilnRoute(routestring)string{returnstrings.TrimRight(k.credentials.KilnUrl,"/")+"/"+strings.TrimLeft(route,"/")}// Returns the full URL for an API call in Kilnfunc(k*Client)apiRoute(routestring)string{returnk.kilnRoute("Api/1.0/"+strings.TrimLeft(route,"/"))}// Returns the full URL for a given API call or Kiln routefunc(k*Client)repoRoute(repostring,actionstring)string{returnk.kilnRoute(fmt.Sprintf("Code/%v/%v",repo,action))}// Returns the body from an API call via HTTP GET
func (k *Client) apiGet(route string, params apiParams) ([]byte, error) {
- return k.apiRequest(route, params, "GET")
+ return k.apiRequest(route, params.Values(), "GET")
}
// Returns the body from an API call via HTTP POST
func (k *Client) apiPost(route string, params apiParams) ([]byte, error) {
- return k.apiRequest(route, params, "POST")
-}
--func (k *Client) apiRequest(route string, paramsapiParams, method string) ([]byte, error) {
- v := url.Values{}- for key, value := range params {- v.Set(key, value)- }+ return k.apiRequest(route, params.Values(), "POST")
+}
++func (k *Client) apiRequest(route string, valuesurl.Values, method string) ([]byte, error) {
if k.credentials.Token != "" {
-v.Set("token", k.credentials.Token)
+values.Set("token", k.credentials.Token)
}
var resp *http.Response
var err error
if method == "GET" {
- resp, err = http.Get(k.apiRoute(route) + "?" + v.Encode())
+ resp, err = http.Get(k.apiRoute(route) + "?" + values.Encode())
} else if method == "POST" {
- resp, err = http.PostForm(k.apiRoute(route), v)
+ resp, err = http.PostForm(k.apiRoute(route), values)
}
if err != nil {
returnnil,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()(credentialsCredentials,errerror){credentials=make(Credentials)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[]credentialiferr=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(credentialsCredentials)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([]*credential,0,10)foruser,urls:=rangecredentials{forurl,token:=rangeurls{creds=append(creds,&credential{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.