Skip to content

Instantly share code, notes, and snippets.

@bradfitz
Created December 14, 2014 23:43
Show Gist options
  • Save bradfitz/3bcf4f8cf181bb033762 to your computer and use it in GitHub Desktop.
Save bradfitz/3bcf4f8cf181bb033762 to your computer and use it in GitHub Desktop.
mark all Camlistore issues as moved
package main
import (
"bytes"
"encoding/xml"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"strings"
"golang.org/x/oauth2"
)
var Endpoint = oauth2.Endpoint{
AuthURL: "https://accounts.google.com/o/oauth2/auth",
TokenURL: "https://accounts.google.com/o/oauth2/token",
}
var cfg = &oauth2.Config{
ClientID: "5612254979-5c6d1li3sgf8h3bq7ra3qvh7rljdeg73.apps.googleusercontent.com",
ClientSecret: "JJ_mZY-7If0LD4oqb9tEHeoX",
Endpoint: Endpoint,
Scopes: []string{"https://code.google.com/feeds/issues"},
RedirectURL: "urn:ietf:wg:oauth:2.0:oob",
}
var client *http.Client
var flagIssue = flag.Int("issue", 0, "issue number to move")
func main() {
flag.Parse()
/*
// fmt.Println(cfg.AuthCodeURL("state"))
fmt.Println("token = %#v", t)
t, err := cfg.Exchange(nil, "4/XImnfaUkdDi7B1U6tvDOvH9kuzc7xqU4Y3IsPfJCUrE.ktZwYNWo9iQdgrKXntQAax10qASWlAI")
if err != nil {
log.Fatal(err)
}
*/
t := &oauth2.Token{
AccessToken: "xxxxxx",
}
tr := &oauth2.Transport{
Source: singleToken{t},
Base: http.DefaultTransport,
}
client = &http.Client{Transport: tr}
move(*flagIssue)
return
gate := make(chan bool, 50)
for i := 52; i <= 556; i++ {
i := i
gate <- true
go func() {
move(i)
<-gate
}()
//move(*flagIssue)
}
}
type issueInfo struct {
closed bool
moved bool // has moved comment
gone bool
}
func lookupIssue(id int) (inf issueInfo) {
res, err := client.Get("https://code.google.com/p/camlistore/issues/detail?id=" + fmt.Sprint(id))
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
if res.StatusCode == 404 {
inf.gone = true
return
}
if res.StatusCode != 200 {
log.Fatalf("issue %d has status code %v", id, res.StatusCode)
}
slurp, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Fatal(err)
}
body := string(slurp)
inf.closed = strings.Contains(body, `class="closed_colors"`)
inf.moved = strings.Contains(body, `This issue has moved to `)
return
}
func move(id int) {
issue := lookupIssue(id)
log.Printf("Issue %d = %+v", id, issue)
if issue.moved || issue.gone {
return
}
status := ""
if !issue.closed {
status = "<issues:status>Moved</issues:status>"
}
var buf bytes.Buffer
buf.WriteString(`<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns='http://www.w3.org/2005/Atom' xmlns:issues='http://schemas.google.com/projecthosting/issues/2009'>
<content type='html'>`)
xml.Escape(&buf, []byte(fmt.Sprintf("This issue has moved to https://camlistore.org/issue/%v\n", id)))
buf.WriteString(`</content>
<author>
<name>ignored</name>
</author>
<issues:sendEmail>False</issues:sendEmail>
<issues:updates>
<issues:label>IssueMoved</issues:label>
<issues:label>Restrict-AddIssueComment-Commit</issues:label>
` + status + `
</issues:updates>
</entry>
`)
u := "https://code.google.com/feeds/issues/p/camlistore/issues/" + fmt.Sprint(id) + "/comments/full"
req, err := http.NewRequest("POST", u, &buf)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Content-Type", "application/atom+xml")
resp, err := client.Do(req)
if err != nil {
log.Fatalf("Do: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != 201 {
buf.Reset()
io.Copy(&buf, resp.Body)
log.Fatalf("Error: %s", buf.Bytes())
}
}
type singleToken struct {
t *oauth2.Token
}
func (s singleToken) Token() (*oauth2.Token, error) { return s.t, nil }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment