You've already forked postgres_exporter
mirror of
https://github.com/prometheus-community/postgres_exporter.git
synced 2025-08-06 17:22:43 +03:00
This commit implements a massive refactor of the repository, and moves the build system over to use Mage (magefile.org) which should allow seamless building across multiple platforms.
358 lines
9.2 KiB
Go
358 lines
9.2 KiB
Go
// Copyright (c) 2013 Yasuhiro Matsumoto, Jason McVetta.
|
|
// This is Free Software, released under the MIT license.
|
|
// See http://mattn.mit-license.org/2013 for details.
|
|
|
|
// goveralls is a Go client for Coveralls.io.
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
_ "crypto/sha512"
|
|
"encoding/json"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
|
|
"golang.org/x/tools/cover"
|
|
)
|
|
|
|
/*
|
|
https://coveralls.io/docs/api_reference
|
|
*/
|
|
|
|
type Flags []string
|
|
|
|
func (a *Flags) String() string {
|
|
return strings.Join(*a, ",")
|
|
}
|
|
|
|
func (a *Flags) Set(value string) error {
|
|
*a = append(*a, value)
|
|
return nil
|
|
}
|
|
|
|
var (
|
|
extraFlags Flags
|
|
pkg = flag.String("package", "", "Go package")
|
|
verbose = flag.Bool("v", false, "Pass '-v' argument to 'go test' and output to stdout")
|
|
race = flag.Bool("race", false, "Pass '-race' argument to 'go test'")
|
|
debug = flag.Bool("debug", false, "Enable debug output")
|
|
coverprof = flag.String("coverprofile", "", "If supplied, use a go cover profile (comma separated)")
|
|
covermode = flag.String("covermode", "count", "sent as covermode argument to go test")
|
|
repotoken = flag.String("repotoken", os.Getenv("COVERALLS_TOKEN"), "Repository Token on coveralls")
|
|
endpoint = flag.String("endpoint", "https://coveralls.io", "Hostname to submit Coveralls data to")
|
|
service = flag.String("service", "travis-ci", "The CI service or other environment in which the test suite was run. ")
|
|
shallow = flag.Bool("shallow", false, "Shallow coveralls internal server errors")
|
|
ignore = flag.String("ignore", "", "Comma separated files to ignore")
|
|
show = flag.Bool("show", false, "Show which package is being tested")
|
|
)
|
|
|
|
// usage supplants package flag's Usage variable
|
|
var usage = func() {
|
|
cmd := os.Args[0]
|
|
// fmt.Fprintf(os.Stderr, "Usage of %s:\n", cmd)
|
|
s := "Usage: %s [options]\n"
|
|
fmt.Fprintf(os.Stderr, s, cmd)
|
|
flag.PrintDefaults()
|
|
}
|
|
|
|
// A SourceFile represents a source code file and its coverage data for a
|
|
// single job.
|
|
type SourceFile struct {
|
|
Name string `json:"name"` // File path of this source file
|
|
Source string `json:"source"` // Full source code of this file
|
|
Coverage []interface{} `json:"coverage"` // Requires both nulls and integers
|
|
}
|
|
|
|
// A Job represents the coverage data from a single run of a test suite.
|
|
type Job struct {
|
|
RepoToken *string `json:"repo_token,omitempty"`
|
|
ServiceJobId string `json:"service_job_id"`
|
|
ServicePullRequest string `json:"service_pull_request,omitempty"`
|
|
ServiceName string `json:"service_name"`
|
|
SourceFiles []*SourceFile `json:"source_files"`
|
|
Git *Git `json:"git,omitempty"`
|
|
RunAt time.Time `json:"run_at"`
|
|
}
|
|
|
|
// A Response is returned by the Coveralls.io API.
|
|
type Response struct {
|
|
Message string `json:"message"`
|
|
URL string `json:"url"`
|
|
Error bool `json:"error"`
|
|
}
|
|
|
|
// getPkgs returns packages for mesuring coverage. Returned packages doesn't
|
|
// contain vendor packages.
|
|
func getPkgs(pkg string) ([]string, error) {
|
|
if pkg == "" {
|
|
pkg = "./..."
|
|
}
|
|
out, err := exec.Command("go", "list", pkg).CombinedOutput()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
allPkgs := strings.Split(strings.Trim(string(out), "\n"), "\n")
|
|
pkgs := make([]string, 0, len(allPkgs))
|
|
for _, p := range allPkgs {
|
|
if !strings.Contains(p, "/vendor/") {
|
|
pkgs = append(pkgs, p)
|
|
}
|
|
}
|
|
return pkgs, nil
|
|
}
|
|
|
|
func getCoverage() ([]*SourceFile, error) {
|
|
if *coverprof != "" {
|
|
return parseCover(*coverprof)
|
|
}
|
|
|
|
// pkgs is packages to run tests and get coverage.
|
|
pkgs, err := getPkgs(*pkg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
coverpkg := fmt.Sprintf("-coverpkg=%s", strings.Join(pkgs, ","))
|
|
var pfss [][]*cover.Profile
|
|
for _, line := range pkgs {
|
|
f, err := ioutil.TempFile("", "goveralls")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
f.Close()
|
|
cmd := exec.Command("go")
|
|
outBuf := new(bytes.Buffer)
|
|
cmd.Stdout = outBuf
|
|
cmd.Stderr = outBuf
|
|
coverm := *covermode
|
|
if *race {
|
|
coverm = "atomic"
|
|
}
|
|
args := []string{"go", "test", "-covermode", coverm, "-coverprofile", f.Name(), coverpkg}
|
|
if *verbose {
|
|
args = append(args, "-v")
|
|
cmd.Stdout = os.Stdout
|
|
}
|
|
if *race {
|
|
args = append(args, "-race")
|
|
}
|
|
args = append(args, extraFlags...)
|
|
args = append(args, line)
|
|
cmd.Args = args
|
|
|
|
if *show {
|
|
fmt.Println("goveralls:", line)
|
|
}
|
|
err = cmd.Run()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%v: %v", err, outBuf.String())
|
|
}
|
|
|
|
pfs, err := cover.ParseProfiles(f.Name())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = os.Remove(f.Name())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pfss = append(pfss, pfs)
|
|
}
|
|
|
|
sourceFiles, err := toSF(mergeProfs(pfss))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return sourceFiles, nil
|
|
}
|
|
|
|
var vscDirs = []string{".git", ".hg", ".bzr", ".svn"}
|
|
|
|
func findRepositoryRoot(dir string) (string, bool) {
|
|
for _, vcsdir := range vscDirs {
|
|
if d, err := os.Stat(filepath.Join(dir, vcsdir)); err == nil && d.IsDir() {
|
|
return dir, true
|
|
}
|
|
}
|
|
nextdir := filepath.Dir(dir)
|
|
if nextdir == dir {
|
|
return "", false
|
|
}
|
|
return findRepositoryRoot(nextdir)
|
|
}
|
|
|
|
func getCoverallsSourceFileName(name string) string {
|
|
if dir, ok := findRepositoryRoot(name); !ok {
|
|
return name
|
|
} else {
|
|
filename := strings.TrimPrefix(name, dir+string(os.PathSeparator))
|
|
return filename
|
|
}
|
|
}
|
|
|
|
func process() error {
|
|
log.SetFlags(log.Ltime | log.Lshortfile)
|
|
//
|
|
// Parse Flags
|
|
//
|
|
flag.Usage = usage
|
|
flag.Var(&extraFlags, "flags", "extra flags to the tests")
|
|
flag.Parse()
|
|
if len(flag.Args()) > 0 {
|
|
flag.Usage()
|
|
os.Exit(1)
|
|
}
|
|
|
|
//
|
|
// Setup PATH environment variable
|
|
//
|
|
paths := filepath.SplitList(os.Getenv("PATH"))
|
|
if goroot := os.Getenv("GOROOT"); goroot != "" {
|
|
paths = append(paths, filepath.Join(goroot, "bin"))
|
|
}
|
|
if gopath := os.Getenv("GOPATH"); gopath != "" {
|
|
for _, path := range filepath.SplitList(gopath) {
|
|
paths = append(paths, filepath.Join(path, "bin"))
|
|
}
|
|
}
|
|
os.Setenv("PATH", strings.Join(paths, string(filepath.ListSeparator)))
|
|
|
|
//
|
|
// Initialize Job
|
|
//
|
|
var jobId string
|
|
if travisJobId := os.Getenv("TRAVIS_JOB_ID"); travisJobId != "" {
|
|
jobId = travisJobId
|
|
} else if circleCiJobId := os.Getenv("CIRCLE_BUILD_NUM"); circleCiJobId != "" {
|
|
jobId = circleCiJobId
|
|
} else if appveyorJobId := os.Getenv("APPVEYOR_JOB_ID"); appveyorJobId != "" {
|
|
jobId = appveyorJobId
|
|
} else if semaphoreJobId := os.Getenv("SEMPAHORE_BUILD_NUMBER"); semaphoreJobId != "" {
|
|
jobId = semaphoreJobId
|
|
}
|
|
|
|
if *repotoken == "" {
|
|
repotoken = nil // remove the entry from json
|
|
}
|
|
var pullRequest string
|
|
if prNumber := os.Getenv("CIRCLE_PR_NUMBER"); prNumber != "" {
|
|
// for Circle CI (pull request from forked repo)
|
|
pullRequest = prNumber
|
|
} else if prNumber := os.Getenv("TRAVIS_PULL_REQUEST"); prNumber != "" && prNumber != "false" {
|
|
pullRequest = prNumber
|
|
} else if prURL := os.Getenv("CI_PULL_REQUEST"); prURL != "" {
|
|
// for Circle CI
|
|
pullRequest = regexp.MustCompile(`[0-9]+$`).FindString(prURL)
|
|
} else if prNumber := os.Getenv("APPVEYOR_PULL_REQUEST_NUMBER"); prNumber != "" {
|
|
pullRequest = prNumber
|
|
} else if prNumber := os.Getenv("PULL_REQUEST_NUMBER"); prNumber != "" {
|
|
pullRequest = prNumber
|
|
}
|
|
|
|
sourceFiles, err := getCoverage()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
j := Job{
|
|
RunAt: time.Now(),
|
|
RepoToken: repotoken,
|
|
ServicePullRequest: pullRequest,
|
|
Git: collectGitInfo(),
|
|
SourceFiles: sourceFiles,
|
|
}
|
|
|
|
// Only include a job ID if it's known, otherwise, Coveralls looks
|
|
// for the job and can't find it.
|
|
if jobId != "" {
|
|
j.ServiceJobId = jobId
|
|
j.ServiceName = *service
|
|
}
|
|
|
|
// Ignore files
|
|
if len(*ignore) > 0 {
|
|
patterns := strings.Split(*ignore, ",")
|
|
for i, pattern := range patterns {
|
|
patterns[i] = strings.TrimSpace(pattern)
|
|
}
|
|
var files []*SourceFile
|
|
Files:
|
|
for _, file := range j.SourceFiles {
|
|
for _, pattern := range patterns {
|
|
match, err := filepath.Match(pattern, file.Name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if match {
|
|
fmt.Printf("ignoring %s\n", file.Name)
|
|
continue Files
|
|
}
|
|
}
|
|
files = append(files, file)
|
|
}
|
|
j.SourceFiles = files
|
|
}
|
|
|
|
if *debug {
|
|
b, err := json.MarshalIndent(j, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
log.Printf("Posting data: %s", b)
|
|
}
|
|
|
|
b, err := json.Marshal(j)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
params := make(url.Values)
|
|
params.Set("json", string(b))
|
|
res, err := http.PostForm(*endpoint+"/api/v1/jobs", params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer res.Body.Close()
|
|
bodyBytes, err := ioutil.ReadAll(res.Body)
|
|
if err != nil {
|
|
return fmt.Errorf("Unable to read response body from coveralls: %s", err)
|
|
}
|
|
|
|
if res.StatusCode >= http.StatusInternalServerError && *shallow {
|
|
fmt.Println("coveralls server failed internally")
|
|
return nil
|
|
}
|
|
|
|
if res.StatusCode != 200 {
|
|
return fmt.Errorf("Bad response status from coveralls: %d\n%s", res.StatusCode, bodyBytes)
|
|
}
|
|
var response Response
|
|
if err = json.Unmarshal(bodyBytes, &response); err != nil {
|
|
return fmt.Errorf("Unable to unmarshal response JSON from coveralls: %s\n%s", err, bodyBytes)
|
|
}
|
|
if response.Error {
|
|
return errors.New(response.Message)
|
|
}
|
|
fmt.Println(response.Message)
|
|
fmt.Println(response.URL)
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
if err := process(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "%s\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|