mirror of
https://github.com/gojp/goreportcard.git
synced 2026-01-29 06:49:05 +08:00
114
main.go
114
main.go
@@ -94,6 +94,7 @@ type score struct {
|
||||
type checksResp struct {
|
||||
Checks []score `json:"checks"`
|
||||
Average float64 `json:"average"`
|
||||
Grade Grade `json:"grade"`
|
||||
Files int `json:"files"`
|
||||
Issues int `json:"issues"`
|
||||
Repo string `json:"repo"`
|
||||
@@ -117,47 +118,69 @@ func getFromCache(repo string) (checksResp, error) {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func checkHandler(w http.ResponseWriter, r *http.Request) {
|
||||
repo := r.FormValue("repo")
|
||||
// Grade represents a grade returned by the server, which is normally
|
||||
// somewhere between A+ (highest) and F (lowest).
|
||||
type Grade string
|
||||
|
||||
// The Grade constants below indicate the current available
|
||||
// grades.
|
||||
const (
|
||||
GradeAPlus Grade = "A+"
|
||||
GradeA = "A"
|
||||
GradeB = "B"
|
||||
GradeC = "C"
|
||||
GradeD = "D"
|
||||
GradeE = "E"
|
||||
GradeF = "F"
|
||||
)
|
||||
|
||||
// getGrade is a helper for getting the grade for a percentage
|
||||
func getGrade(percentage float64) Grade {
|
||||
switch true {
|
||||
case percentage > 90:
|
||||
return GradeAPlus
|
||||
case percentage > 80:
|
||||
return GradeA
|
||||
case percentage > 70:
|
||||
return GradeB
|
||||
case percentage > 60:
|
||||
return GradeC
|
||||
case percentage > 50:
|
||||
return GradeD
|
||||
case percentage > 40:
|
||||
return GradeE
|
||||
default:
|
||||
return GradeF
|
||||
}
|
||||
}
|
||||
|
||||
func newChecksResp(repo string, forceRefresh bool) (checksResp, error) {
|
||||
url := repo
|
||||
if !strings.HasPrefix(url, "https://github.com/") {
|
||||
url = "https://github.com/" + url
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
// if this is a GET request, fetch from cached version in mongo
|
||||
if r.Method == "GET" {
|
||||
if !forceRefresh {
|
||||
resp, err := getFromCache(repo)
|
||||
if err != nil {
|
||||
// just log the error and continue
|
||||
log.Println(err)
|
||||
} else {
|
||||
b, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
log.Println("ERROR: could not marshal json:", err)
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
w.Write(b)
|
||||
log.Println("Loaded from cache!", repo)
|
||||
return
|
||||
resp.Grade = getGrade(resp.Average * 100) // grade is not stored for some repos, yet
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
// fetch the repo and grade it
|
||||
err := clone(url)
|
||||
if err != nil {
|
||||
log.Println("ERROR: could not clone repo: ", err)
|
||||
http.Error(w, fmt.Sprintf("Could not clone repo: %v", err), 500)
|
||||
return
|
||||
return checksResp{}, fmt.Errorf("Could not clone repo: %v", err)
|
||||
}
|
||||
|
||||
dir := dirName(url)
|
||||
filenames, err := check.GoFiles(dir)
|
||||
if err != nil {
|
||||
log.Println("ERROR: could not get filenames: ", err)
|
||||
http.Error(w, fmt.Sprintf("Could not get filenames: %v", err), 500)
|
||||
return
|
||||
return checksResp{}, fmt.Errorf("Could not get filenames: %v", err)
|
||||
}
|
||||
checks := []check.Check{check.GoFmt{Dir: dir, Filenames: filenames},
|
||||
check.GoVet{Dir: dir, Filenames: filenames},
|
||||
@@ -198,6 +221,22 @@ func checkHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
resp.Average = avg / float64(len(checks))
|
||||
resp.Issues = len(issues)
|
||||
resp.Grade = getGrade(resp.Average * 100)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func checkHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
repo := r.FormValue("repo")
|
||||
forceRefresh := r.Method != "GET" // if this is a GET request, try fetch from cached version in mongo first
|
||||
resp, err := newChecksResp(repo, forceRefresh)
|
||||
if err != nil {
|
||||
b, _ := json.Marshal(err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write(b)
|
||||
}
|
||||
|
||||
b, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
@@ -224,9 +263,35 @@ func reportHandler(w http.ResponseWriter, r *http.Request, org, repo string) {
|
||||
http.ServeFile(w, r, "templates/home.html")
|
||||
}
|
||||
|
||||
func makeReportHandler(fn func(http.ResponseWriter, *http.Request, string, string)) http.HandlerFunc {
|
||||
func badgeURL(grade Grade) string {
|
||||
colorMap := map[Grade]string{
|
||||
GradeAPlus: "brightgreen",
|
||||
GradeA: "brightgreen",
|
||||
GradeB: "yellowgreen",
|
||||
GradeC: "yellow",
|
||||
GradeD: "orange",
|
||||
GradeE: "red",
|
||||
GradeF: "red",
|
||||
}
|
||||
url := fmt.Sprintf("https://img.shields.io/badge/go_report-%s-%s.svg", grade, colorMap[grade])
|
||||
return url
|
||||
}
|
||||
|
||||
func badgeHandler(w http.ResponseWriter, r *http.Request, org, repo string) {
|
||||
name := fmt.Sprintf("%s/%s", org, repo)
|
||||
resp, err := newChecksResp(name, false)
|
||||
if err != nil {
|
||||
log.Printf("ERROR: fetching badge for %s: %v", name, err)
|
||||
http.Redirect(w, r, "https://img.shields.io/badge/go%20report-error-lightgrey.svg", http.StatusTemporaryRedirect)
|
||||
return
|
||||
}
|
||||
|
||||
http.Redirect(w, r, badgeURL(resp.Grade), http.StatusTemporaryRedirect)
|
||||
}
|
||||
|
||||
func makeHandler(name string, fn func(http.ResponseWriter, *http.Request, string, string)) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
validPath := regexp.MustCompile(`^/report/([a-zA-Z0-9\-_]+)/([a-zA-Z0-9\-_]+)$`)
|
||||
validPath := regexp.MustCompile(fmt.Sprintf(`^/%s/([a-zA-Z0-9\-_]+)/([a-zA-Z0-9\-_]+)$`, name))
|
||||
|
||||
m := validPath.FindStringSubmatch(r.URL.Path)
|
||||
if m == nil {
|
||||
@@ -244,7 +309,8 @@ func main() {
|
||||
|
||||
http.HandleFunc("/assets/", assetsHandler)
|
||||
http.HandleFunc("/checks", checkHandler)
|
||||
http.HandleFunc("/report/", makeReportHandler(reportHandler))
|
||||
http.HandleFunc("/report/", makeHandler("report", reportHandler))
|
||||
http.HandleFunc("/badge/", makeHandler("badge", badgeHandler))
|
||||
http.HandleFunc("/", homeHandler)
|
||||
|
||||
fmt.Println("Running on 127.0.01:8080...")
|
||||
|
||||
@@ -258,31 +258,16 @@
|
||||
</tr>
|
||||
</script>
|
||||
<script id="template-lastrefresh" type="text/x-handlebars-template">
|
||||
<p>Last refresh: {{last_refresh}} <a href=""><strong>Update now</strong></a></p>
|
||||
<!--
|
||||
<p>Last refresh: {{last_refresh}} <a class="refresh-button" href=""><strong>Update now</strong></a></p>
|
||||
<div class="col-md-12 text-right">
|
||||
<a class="copy-button" data-repo="{{repo}}" href="/badge/{{repo}}"><img src="/badge/{{repo}}"/></a>
|
||||
</div>
|
||||
-->
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
var loading = false;
|
||||
|
||||
// add a helper for getting the grade for a percentage
|
||||
var getGrade = function(percentage) {
|
||||
switch(true){
|
||||
case percentage > 90:
|
||||
return "A+";
|
||||
case percentage > 80:
|
||||
return "A";
|
||||
case percentage > 70:
|
||||
return "B";
|
||||
case percentage > 60:
|
||||
return "C";
|
||||
case percentage > 50:
|
||||
return "D";
|
||||
case percentage > 40:
|
||||
return "E";
|
||||
default:
|
||||
return "F";
|
||||
}
|
||||
};
|
||||
|
||||
Handlebars.registerHelper('gradeMessage', function(grade, options) {
|
||||
var gradeMessages = {
|
||||
"A+": "Excellent!",
|
||||
@@ -356,10 +341,8 @@
|
||||
}
|
||||
|
||||
var populateResults = function(data){
|
||||
console.log("data is", data);
|
||||
var checks = data.checks;
|
||||
var $resultsText = $(".results-text");
|
||||
data.grade = getGrade(data.average * 100);
|
||||
$resultsText.html($(templates.grade(data)));
|
||||
|
||||
var $table = $(".table.results");
|
||||
@@ -379,19 +362,30 @@
|
||||
$(".container-results").removeClass('hidden').slideDown();
|
||||
|
||||
$lastRefresh = $(templates.lastrefresh(data));
|
||||
$(".container-update").html($lastRefresh).find("a").on("click", function(e){
|
||||
$div = $(".container-update").html($lastRefresh);
|
||||
$div.find("a.refresh-button").on("click", function(e){
|
||||
loadData.call($("form#check-form")[0], false);
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
function alertMessage(msg){
|
||||
var html = templates.alert({message: msg});
|
||||
var $alert = $(html);
|
||||
$alert.on("click", function(){
|
||||
$(this).closest(".alert").remove();
|
||||
});
|
||||
$alert.hide();
|
||||
$alert.appendTo("#notifications");
|
||||
$alert.slideDown();
|
||||
}
|
||||
|
||||
var loadData = function(getRequest){
|
||||
loading = true;
|
||||
var $form = $(this),
|
||||
url = $form.attr("action"),
|
||||
method = $form.attr("method"),
|
||||
data = {};
|
||||
console.log("GOnna make", getRequest ? "GET" : "POST");
|
||||
shrinkHeader();
|
||||
hideResults();
|
||||
showGopher();
|
||||
@@ -402,14 +396,7 @@
|
||||
data: data,
|
||||
dataType: "json"
|
||||
}).fail(function(xhr, status, err){;
|
||||
var html = templates.alert({message: "<strong>Oops!</strong> There was an error processing your request: " + err});
|
||||
var $alert = $(html);
|
||||
$alert.on("click", function(){
|
||||
$(this).closest(".alert").remove();
|
||||
});
|
||||
$alert.hide();
|
||||
$alert.appendTo("#notifications");
|
||||
$alert.slideDown();
|
||||
alertMessage("<strong>Oops!</strong> There was an error processing your request: " + err);
|
||||
}).done(function(data, textStatus, jqXHR){
|
||||
hideGopher();
|
||||
populateResults(data);
|
||||
|
||||
Reference in New Issue
Block a user