From 43f2e48dd027cdaaa23a146c8ce6623a001e9a34 Mon Sep 17 00:00:00 2001 From: mpl Date: Wed, 13 Apr 2016 22:55:18 +0200 Subject: [PATCH] website: sync commits to github Fixes issue #708 Change-Id: I0e081724134ab08c736b00bd2ebe81eb9028ace7 --- website/camweb.go | 3 ++ website/email.go | 5 ++ website/github.go | 122 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 website/github.go diff --git a/website/camweb.go b/website/camweb.go index 15bf31bae..ff1e51ef3 100644 --- a/website/camweb.go +++ b/website/camweb.go @@ -656,6 +656,9 @@ func main() { } } readTemplates() + if err := initGithubSyncing(); err != nil { + log.Fatalf("error setting up syncing to github: %v") + } go runDemoBlobserverLoop() mux := http.DefaultServeMux diff --git a/website/email.go b/website/email.go index ffd88286a..715b77017 100644 --- a/website/email.go +++ b/website/email.go @@ -241,6 +241,11 @@ func pollCommits(dir string) { } } } + if githubSSHKey != "" { + if err := syncToGithub(dir, hashes[0]); err != nil { + log.Printf("Failed to push to github: %v") + } + } } func recentCommits(dir string) (hashes []string, err error) { diff --git a/website/github.go b/website/github.go new file mode 100644 index 000000000..86ff03ba3 --- /dev/null +++ b/website/github.go @@ -0,0 +1,122 @@ +/* +Copyright 2016 The Camlistore Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + "os/user" + "path/filepath" + + "go4.org/wkfs" +) + +const githubSSHKeyGCS = "/gcs/camlistore-website-resource/id_github_camlistorebot_push" + +var githubSSHKey string // Also used to detect whether we do the syncs to github + +func githubSSHConfig(filename string) string { + return ` +Host github.com + User git + IdentityFile ` + filename + ` + IdentitiesOnly yes +` +} + +func initGithubSyncing() error { + if !inProd { + return nil + } + keyData, err := wkfs.ReadFile(githubSSHKeyGCS) + if err != nil { + log.Print("Not syncing to github, because no ssh key found.") + return nil + } + u, err := user.Current() + if err != nil { + return fmt.Errorf("can't look up user: %v", err) + } + dir, err := ioutil.TempDir("", "ssh") + if err != nil { + return fmt.Errorf("failed to create temp dir for github SSH key: %v", err) + } + keyFile := filepath.Join(dir, "id_github_camlistorebot_push") + if err := ioutil.WriteFile(keyFile, keyData, 0600); err != nil { + return fmt.Errorf("failed to create temp github SSH key: %v", err) + } + if err := os.MkdirAll(filepath.Join(u.HomeDir, ".ssh"), 0700); err != nil { + return fmt.Errorf("failed to create ssh config dir %v: %v", filepath.Join(u.HomeDir, ".ssh"), err) + } + if err := ioutil.WriteFile( + filepath.Join(u.HomeDir, ".ssh", "id_github_camlistorebot_push"), + []byte(githubSSHConfig(keyFile)), + 0600); err != nil { + return fmt.Errorf("failed to create github SSH config: %v", err) + } + githubSSHKey = keyFile + return nil +} + +// githubHEAD returns the hash of the HEAD commit on the github repo. +// The gerritHEAD argument is used as an optimization in the request to github: +// if it is found as the HEAD commit, the request is not counted in our +// non-authenticated requests quota. +func githubHEAD(gerritHEAD string) (string, error) { + const ( + headAPI = "https://api.github.com/repos/camlistore/camlistore/commits/HEAD" + mimeType = "application/vnd.github.VERSION.sha" + ) + req, err := http.NewRequest("GET", headAPI, nil) + if err != nil { + return "", err + } + req.Header.Add("If-None-Match", `"`+gerritHEAD+`"`) + req.Header.Add("Accept", mimeType) + resp, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + if resp.StatusCode == http.StatusNotModified { + return gerritHEAD, nil + } + ghCommit, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + return string(ghCommit), nil +} + +func syncToGithub(dir, gerritHEAD string) error { + gh, err := githubHEAD(gerritHEAD) + if err != nil { + return fmt.Errorf("error looking up the github HEAD commit: %v", err) + } + if gh == gerritHEAD { + return nil + } + cmd := execGit(dir, "push", "git@github.com:camlistore/camlistore.git", "master:master") + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("error running git push to github: %v\n%s", err, out) + } + return nil +}