mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-26 20:01:02 +00:00
more work on the command line utility
This commit is contained in:
parent
f51b4d5ef3
commit
c4c7b8e006
14 changed files with 282 additions and 468 deletions
2
Makefile
2
Makefile
|
@ -13,7 +13,7 @@ test:
|
||||||
go test -cover -short ./...
|
go test -cover -short ./...
|
||||||
|
|
||||||
build:
|
build:
|
||||||
go build -o debian/drone/usr/local/bin/drone -ldflags "-X main.revision $(SHA)" github.com/drone/drone/client
|
go build -o debian/drone/usr/local/bin/drone -ldflags "-X main.revision $(SHA)" github.com/drone/drone/cmd
|
||||||
go build -o debian/drone/usr/local/bin/droned -ldflags "-X main.revision $(SHA)" github.com/drone/drone/server
|
go build -o debian/drone/usr/local/bin/droned -ldflags "-X main.revision $(SHA)" github.com/drone/drone/server
|
||||||
|
|
||||||
install:
|
install:
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -10,6 +10,27 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
token string
|
||||||
|
url string
|
||||||
|
|
||||||
|
Commits *CommitService
|
||||||
|
Repos *RepoService
|
||||||
|
Users *UserService
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(token, url string) *Client {
|
||||||
|
c := Client{
|
||||||
|
token: token,
|
||||||
|
url: url,
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Commits = &CommitService{&c}
|
||||||
|
c.Repos = &RepoService{&c}
|
||||||
|
c.Users = &UserService{&c}
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrNotFound = errors.New("Not Found")
|
ErrNotFound = errors.New("Not Found")
|
||||||
ErrForbidden = errors.New("Forbidden")
|
ErrForbidden = errors.New("Forbidden")
|
||||||
|
@ -18,17 +39,12 @@ var (
|
||||||
ErrInternalServer = errors.New("Internal Server Error")
|
ErrInternalServer = errors.New("Internal Server Error")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
// runs an http.Request and parses the JSON-encoded http.Response,
|
||||||
Token string
|
|
||||||
URL string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do submits an http.Request and parses the JSON-encoded http.Response,
|
|
||||||
// storing the result in the value pointed to by v.
|
// storing the result in the value pointed to by v.
|
||||||
func (c *Client) Do(method, path string, in, out interface{}) error {
|
func (c *Client) run(method, path string, in, out interface{}) error {
|
||||||
|
|
||||||
// create the URI
|
// create the URI
|
||||||
uri, err := url.Parse(c.URL + path)
|
uri, err := url.Parse(c.url + path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -37,9 +53,9 @@ func (c *Client) Do(method, path string, in, out interface{}) error {
|
||||||
uri.Scheme = "http"
|
uri.Scheme = "http"
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.Token) > 0 {
|
if len(c.token) > 0 {
|
||||||
params := uri.Query()
|
params := uri.Query()
|
||||||
params.Add("access_token", c.Token)
|
params.Add("access_token", c.token)
|
||||||
uri.RawQuery = params.Encode()
|
uri.RawQuery = params.Encode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,3 +118,36 @@ func (c *Client) Do(method, path string, in, out interface{}) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// do makes an http.Request and returns the response
|
||||||
|
func (c *Client) do(method, path string) (*http.Response, error) {
|
||||||
|
|
||||||
|
// create the URI
|
||||||
|
uri, err := url.Parse(c.url + path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(uri.Scheme) == 0 {
|
||||||
|
uri.Scheme = "http"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.token) > 0 {
|
||||||
|
params := uri.Query()
|
||||||
|
params.Add("access_token", c.token)
|
||||||
|
uri.RawQuery = params.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the request
|
||||||
|
req := &http.Request{
|
||||||
|
URL: uri,
|
||||||
|
Method: method,
|
||||||
|
ProtoMajor: 1,
|
||||||
|
ProtoMinor: 1,
|
||||||
|
Close: true,
|
||||||
|
ContentLength: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
// make the request using the default http client
|
||||||
|
return http.DefaultClient.Do(req)
|
||||||
|
}
|
52
client/commits.go
Normal file
52
client/commits.go
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/drone/drone/shared/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CommitService struct {
|
||||||
|
*Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /v1/repos/{host}/{owner}/{name}/branch/{branch}/commit/{commit}
|
||||||
|
func (s *CommitService) Get(host, owner, name, branch, sha string) (*model.Commit, error) {
|
||||||
|
var path = fmt.Sprintf("/v1/repos/%s/%s/%s/branch/%s/commit/%s", host, owner, name, branch, sha)
|
||||||
|
var commit = model.Commit{}
|
||||||
|
var err = s.run("GET", path, nil, &commit)
|
||||||
|
return &commit, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /v1/repos/{host}/{owner}/{name}/branches/{branch}/commits/{commit}/console
|
||||||
|
func (s *CommitService) GetOutput(host, owner, name, branch, sha string) (io.ReadCloser, error) {
|
||||||
|
var path = fmt.Sprintf("/v1/repos/%s/%s/%s/branch/%s/commit/%s/console", host, owner, name, branch, sha)
|
||||||
|
resp, err := s.do("GET", path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return resp.Body, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /v1/repos/{host}/{owner}/{name}/branches/{branch}/commits/{commit}?action=rebuild
|
||||||
|
func (s *CommitService) Rebuild(host, owner, name, branch, sha string) error {
|
||||||
|
var path = fmt.Sprintf("/v1/repos/%s/%s/%s/branch/%s/commit/%s", host, owner, name, branch, sha)
|
||||||
|
return s.run("POST", path, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /v1/repos/{host}/{owner}/{name}/feed
|
||||||
|
func (s *CommitService) List(host, owner, name string) ([]*model.Commit, error) {
|
||||||
|
var path = fmt.Sprintf("/v1/repos/%s/%s/%s/feed", host, owner, name)
|
||||||
|
var list []*model.Commit
|
||||||
|
var err = s.run("GET", path, nil, &list)
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /v1/repos/{host}/{owner}/{name}/branch/{branch}
|
||||||
|
func (s *CommitService) ListBranch(host, owner, name, branch string) ([]*model.Commit, error) {
|
||||||
|
var path = fmt.Sprintf("/v1/repos/%s/%s/%s/branch/%s", host, owner, name, branch)
|
||||||
|
var list []*model.Commit
|
||||||
|
var err = s.run("GET", path, nil, &list)
|
||||||
|
return list, err
|
||||||
|
}
|
314
client/main.go
314
client/main.go
|
@ -1,314 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/drone/drone/shared/build"
|
|
||||||
"github.com/drone/drone/shared/build/docker"
|
|
||||||
"github.com/drone/drone/shared/build/log"
|
|
||||||
"github.com/drone/drone/shared/build/repo"
|
|
||||||
"github.com/drone/drone/shared/build/script"
|
|
||||||
|
|
||||||
"gopkg.in/yaml.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// identity file (id_rsa) that will be injected
|
|
||||||
// into the container if specified
|
|
||||||
identity = flag.String("identity", "", "")
|
|
||||||
|
|
||||||
// runs Drone in parallel mode if True
|
|
||||||
parallel = flag.Bool("parallel", false, "")
|
|
||||||
|
|
||||||
// build will timeout after N milliseconds.
|
|
||||||
// this will default to 500 minutes (6 hours)
|
|
||||||
timeout = flag.Duration("timeout", 300*time.Minute, "")
|
|
||||||
|
|
||||||
// build will run in a privileged container
|
|
||||||
privileged = flag.Bool("privileged", false, "")
|
|
||||||
|
|
||||||
// runs Drone with verbose output if True
|
|
||||||
verbose = flag.Bool("v", false, "")
|
|
||||||
|
|
||||||
// displays the help / usage if True
|
|
||||||
help = flag.Bool("h", false, "")
|
|
||||||
|
|
||||||
// version number, currently deterined by the
|
|
||||||
// git revision number (sha)
|
|
||||||
version string
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// default logging
|
|
||||||
log.SetPrefix("\033[2m[DRONE] ")
|
|
||||||
log.SetSuffix("\033[0m\n")
|
|
||||||
log.SetOutput(os.Stdout)
|
|
||||||
log.SetPriority(log.LOG_NOTICE)
|
|
||||||
docker.Logging = false
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// Parse the input parameters
|
|
||||||
flag.Usage = usage
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if *help {
|
|
||||||
flag.Usage()
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *verbose {
|
|
||||||
log.SetPriority(log.LOG_DEBUG)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must speicify a command
|
|
||||||
args := flag.Args()
|
|
||||||
if len(args) == 0 {
|
|
||||||
flag.Usage()
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
// run drone build assuming the current
|
|
||||||
// working directory contains the drone.yml
|
|
||||||
case args[0] == "build" && len(args) == 1:
|
|
||||||
path, _ := os.Getwd()
|
|
||||||
path = filepath.Join(path, ".drone.yml")
|
|
||||||
run(path)
|
|
||||||
|
|
||||||
// run drone build where the path to the
|
|
||||||
// source directory is provided
|
|
||||||
case args[0] == "build" && len(args) == 2:
|
|
||||||
path := args[1]
|
|
||||||
path = filepath.Clean(path)
|
|
||||||
path, _ = filepath.Abs(path)
|
|
||||||
path = filepath.Join(path, ".drone.yml")
|
|
||||||
run(path)
|
|
||||||
|
|
||||||
// run drone vet where the path to the
|
|
||||||
// source directory is provided
|
|
||||||
case args[0] == "vet" && len(args) == 2:
|
|
||||||
path := args[1]
|
|
||||||
path = filepath.Clean(path)
|
|
||||||
path, _ = filepath.Abs(path)
|
|
||||||
path = filepath.Join(path, ".drone.yml")
|
|
||||||
vet(path)
|
|
||||||
|
|
||||||
// run drone vet assuming the current
|
|
||||||
// working directory contains the drone.yml
|
|
||||||
case args[0] == "vet" && len(args) == 1:
|
|
||||||
path, _ := os.Getwd()
|
|
||||||
path = filepath.Join(path, ".drone.yml")
|
|
||||||
vet(path)
|
|
||||||
|
|
||||||
// print the version / revision number
|
|
||||||
case args[0] == "version" && len(args) == 1:
|
|
||||||
println(version)
|
|
||||||
|
|
||||||
// print the help message
|
|
||||||
case args[0] == "help" && len(args) == 1:
|
|
||||||
flag.Usage()
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func vet(path string) {
|
|
||||||
// parse the Drone yml file
|
|
||||||
script, err := script.ParseBuildFile(path)
|
|
||||||
if err != nil {
|
|
||||||
log.Err(err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// print the Drone yml as parsed
|
|
||||||
out, _ := yaml.Marshal(script)
|
|
||||||
log.Noticef("parsed yaml:\n%s", string(out))
|
|
||||||
}
|
|
||||||
|
|
||||||
func run(path string) {
|
|
||||||
dockerClient := docker.New()
|
|
||||||
|
|
||||||
// parse the Drone yml file
|
|
||||||
s, err := script.ParseBuildFile(path)
|
|
||||||
if err != nil {
|
|
||||||
log.Err(err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove deploy & publish sections
|
|
||||||
// for now, until I fix bug
|
|
||||||
s.Publish = nil
|
|
||||||
s.Deploy = nil
|
|
||||||
|
|
||||||
// get the repository root directory
|
|
||||||
dir := filepath.Dir(path)
|
|
||||||
code := repo.Repo{
|
|
||||||
Name: filepath.Base(dir),
|
|
||||||
Branch: "HEAD", // should we do this?
|
|
||||||
Path: dir,
|
|
||||||
}
|
|
||||||
|
|
||||||
// does the local repository match the
|
|
||||||
// $GOPATH/src/{package} pattern? This is
|
|
||||||
// important so we know the target location
|
|
||||||
// where the code should be copied inside
|
|
||||||
// the container.
|
|
||||||
if gopath, ok := getRepoPath(dir); ok {
|
|
||||||
code.Dir = gopath
|
|
||||||
|
|
||||||
} else if gopath, ok := getGoPath(dir); ok {
|
|
||||||
// in this case we found a GOPATH and
|
|
||||||
// reverse engineered the package path
|
|
||||||
code.Dir = gopath
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// otherwise just use directory name
|
|
||||||
code.Dir = filepath.Base(dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is where the code gets uploaded to the container
|
|
||||||
// TODO move this code to the build package
|
|
||||||
code.Dir = filepath.Join("/var/cache/drone/src", filepath.Clean(code.Dir))
|
|
||||||
|
|
||||||
// track all build results
|
|
||||||
var builders []*build.Builder
|
|
||||||
|
|
||||||
// ssh key to import into container
|
|
||||||
var key []byte
|
|
||||||
if len(*identity) != 0 {
|
|
||||||
key, err = ioutil.ReadFile(*identity)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("[Error] Could not find or read identity file %s\n", *identity)
|
|
||||||
os.Exit(1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
builds := []*script.Build{s}
|
|
||||||
|
|
||||||
// loop through and create builders
|
|
||||||
for _, b := range builds { //script.Builds {
|
|
||||||
builder := build.New(dockerClient)
|
|
||||||
builder.Build = b
|
|
||||||
builder.Repo = &code
|
|
||||||
builder.Key = key
|
|
||||||
builder.Stdout = os.Stdout
|
|
||||||
builder.Timeout = *timeout
|
|
||||||
builder.Privileged = *privileged
|
|
||||||
|
|
||||||
if *parallel == true {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
builder.Stdout = &buf
|
|
||||||
}
|
|
||||||
|
|
||||||
builders = append(builders, builder)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch *parallel {
|
|
||||||
case false:
|
|
||||||
runSequential(builders)
|
|
||||||
case true:
|
|
||||||
runParallel(builders)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if in parallel mode, print out the buffer
|
|
||||||
// if we had a failure
|
|
||||||
for _, builder := range builders {
|
|
||||||
if builder.BuildState.ExitCode == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if buf, ok := builder.Stdout.(*bytes.Buffer); ok {
|
|
||||||
log.Noticef("printing stdout for failed build %s", builder.Build.Name)
|
|
||||||
println(buf.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// this exit code is initially 0 and will
|
|
||||||
// be set to an error code if any of the
|
|
||||||
// builds fail.
|
|
||||||
var exit int
|
|
||||||
|
|
||||||
fmt.Printf("\nDrone Build Results \033[90m(%v)\033[0m\n", len(builders))
|
|
||||||
|
|
||||||
// loop through and print results
|
|
||||||
for _, builder := range builders {
|
|
||||||
build := builder.Build
|
|
||||||
res := builder.BuildState
|
|
||||||
duration := time.Duration(res.Finished - res.Started)
|
|
||||||
switch {
|
|
||||||
case builder.BuildState.ExitCode == 0:
|
|
||||||
fmt.Printf(" \033[32m\u2713\033[0m %v \033[90m(%v)\033[0m\n", build.Name, humanizeDuration(duration*time.Second))
|
|
||||||
case builder.BuildState.ExitCode != 0:
|
|
||||||
fmt.Printf(" \033[31m\u2717\033[0m %v \033[90m(%v)\033[0m\n", build.Name, humanizeDuration(duration*time.Second))
|
|
||||||
exit = builder.BuildState.ExitCode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Exit(exit)
|
|
||||||
}
|
|
||||||
|
|
||||||
func runSequential(builders []*build.Builder) {
|
|
||||||
// loop through and execute each build
|
|
||||||
for _, builder := range builders {
|
|
||||||
if err := builder.Run(); err != nil {
|
|
||||||
log.Errf("Error executing build: %s", err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runParallel(builders []*build.Builder) {
|
|
||||||
// spawn four worker goroutines
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
for _, builder := range builders {
|
|
||||||
// Increment the WaitGroup counter
|
|
||||||
wg.Add(1)
|
|
||||||
// Launch a goroutine to run the build
|
|
||||||
go func(builder *build.Builder) {
|
|
||||||
defer wg.Done()
|
|
||||||
builder.Run()
|
|
||||||
}(builder)
|
|
||||||
time.Sleep(500 * time.Millisecond) // get weird iptables failures unless we sleep.
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for the workers to finish
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
var usage = func() {
|
|
||||||
fmt.Println(`Drone is a tool for building and testing code in Docker containers.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
drone command [arguments]
|
|
||||||
|
|
||||||
The commands are:
|
|
||||||
|
|
||||||
build build and test the repository
|
|
||||||
version print the version number
|
|
||||||
vet validate the yaml configuration file
|
|
||||||
|
|
||||||
-v runs drone with verbose output
|
|
||||||
-h display this help and exit
|
|
||||||
--parallel runs drone build tasks in parallel
|
|
||||||
--timeout=300ms timeout build after 300 milliseconds
|
|
||||||
--privileged runs drone build in a privileged container
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
drone build builds the source in the pwd
|
|
||||||
drone build /path/to/repo builds the source repository
|
|
||||||
|
|
||||||
Use "drone help [command]" for more information about a command.
|
|
||||||
`)
|
|
||||||
}
|
|
46
client/repos.go
Normal file
46
client/repos.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/drone/drone/shared/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RepoService struct {
|
||||||
|
*Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /v1/repos/{host}/{owner}/{name}
|
||||||
|
func (s *RepoService) Get() (*model.Repo, error) {
|
||||||
|
var path = fmt.Sprintf("/v1/repos/%s/%s/%s")
|
||||||
|
var repo = model.Repo{}
|
||||||
|
var err = s.run("PUT", path, nil, &repo)
|
||||||
|
return &repo, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PUT /v1/repos/{host}/{owner}/{name}
|
||||||
|
func (s *RepoService) Update(repo *model.Repo) (*model.Repo, error) {
|
||||||
|
var path = fmt.Sprintf("/v1/repos/%s/%s/%s")
|
||||||
|
var result = model.Repo{}
|
||||||
|
var err = s.run("PUT", path, &repo, &result)
|
||||||
|
return &result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /v1/repos/{host}/{owner}/{name}
|
||||||
|
func (s *RepoService) Enable(host, owner, name string) error {
|
||||||
|
var path = fmt.Sprintf("/v1/repos/%s/%s/%s", host, owner, name)
|
||||||
|
return s.run("POST", path, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE /v1/repos/{host}/{owner}/{name}
|
||||||
|
func (s *RepoService) Disable(host, owner, name string) error {
|
||||||
|
var path = fmt.Sprintf("/v1/repos/%s/%s/%s", host, owner, name)
|
||||||
|
return s.run("DELETE", path, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /v1/user/repos
|
||||||
|
func (s *RepoService) List() ([]*model.Repo, error) {
|
||||||
|
var repos []*model.Repo
|
||||||
|
var err = s.run("GET", "/v1/user/repos", nil, &repos)
|
||||||
|
return repos, err
|
||||||
|
}
|
47
client/users.go
Normal file
47
client/users.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/drone/drone/shared/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserService struct {
|
||||||
|
*Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /v1/users/{host}/{login}
|
||||||
|
func (s *UserService) Get(remote, login string) (*model.User, error) {
|
||||||
|
var path = fmt.Sprintf("/v1/users/%s/%s", remote, login)
|
||||||
|
var user = model.User{}
|
||||||
|
var err = s.run("GET", path, nil, &user)
|
||||||
|
return &user, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /v1/user
|
||||||
|
func (s *UserService) GetCurrent() (*model.User, error) {
|
||||||
|
var user = model.User{}
|
||||||
|
var err = s.run("GET", "/v1/user", nil, &user)
|
||||||
|
return &user, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /v1/users/{host}/{login}
|
||||||
|
func (s *UserService) Create(remote, login string) (*model.User, error) {
|
||||||
|
var path = fmt.Sprintf("/v1/users/%s/%s", remote, login)
|
||||||
|
var user = model.User{}
|
||||||
|
var err = s.run("POST", path, nil, &user)
|
||||||
|
return &user, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE /v1/users/{host}/{login}
|
||||||
|
func (s *UserService) Delete(remote, login string) error {
|
||||||
|
var path = fmt.Sprintf("/v1/users/%s/%s", remote, login)
|
||||||
|
return s.run("DELETE", path, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /v1/users
|
||||||
|
func (s *UserService) List() ([]*model.User, error) {
|
||||||
|
var users []*model.User
|
||||||
|
var err = s.run("GET", "/v1/users", nil, &users)
|
||||||
|
return users, err
|
||||||
|
}
|
|
@ -1,90 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// getGoPath checks the source codes absolute path
|
|
||||||
// in reference to the host operating system's GOPATH
|
|
||||||
// to correctly determine the code's package path. This
|
|
||||||
// is Go-specific, since Go code must exist in
|
|
||||||
// $GOPATH/src/github.com/{owner}/{name}
|
|
||||||
func getGoPath(dir string) (string, bool) {
|
|
||||||
path := os.Getenv("GOPATH")
|
|
||||||
if len(path) == 0 {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
// append src to the GOPATH, since
|
|
||||||
// the code will be stored in the src dir
|
|
||||||
path = filepath.Join(path, "src")
|
|
||||||
if !filepath.HasPrefix(dir, path) {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove the prefix from the directory
|
|
||||||
// this should leave us with the go package name
|
|
||||||
return dir[len(path):], true
|
|
||||||
}
|
|
||||||
|
|
||||||
var gopathExp = regexp.MustCompile("./src/(github.com/[^/]+/[^/]+|bitbucket.org/[^/]+/[^/]+|code.google.com/[^/]+/[^/]+)")
|
|
||||||
|
|
||||||
// getRepoPath checks the source codes absolute path
|
|
||||||
// on the host operating system in an attempt
|
|
||||||
// to correctly determine the code's package path. This
|
|
||||||
// is Go-specific, since Go code must exist in
|
|
||||||
// $GOPATH/src/github.com/{owner}/{name}
|
|
||||||
func getRepoPath(dir string) (path string, ok bool) {
|
|
||||||
// let's get the package directory based
|
|
||||||
// on the path in the host OS
|
|
||||||
indexes := gopathExp.FindStringIndex(dir)
|
|
||||||
if len(indexes) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
index := indexes[len(indexes)-1]
|
|
||||||
|
|
||||||
// if the dir is /home/ubuntu/go/src/github.com/foo/bar
|
|
||||||
// the index will start at /src/github.com/foo/bar.
|
|
||||||
// We'll need to strip "/src/" which is where the
|
|
||||||
// magic number 5 comes from.
|
|
||||||
index = strings.LastIndex(dir, "/src/")
|
|
||||||
return dir[index+5:], true
|
|
||||||
}
|
|
||||||
|
|
||||||
// getGitOrigin checks the .git origin in an attempt
|
|
||||||
// to correctly determine the code's package path. This
|
|
||||||
// is Go-specific, since Go code must exist in
|
|
||||||
// $GOPATH/src/github.com/{owner}/{name}
|
|
||||||
func getGitOrigin(dir string) (path string, ok bool) {
|
|
||||||
// TODO
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// prints the time as a human readable string
|
|
||||||
func humanizeDuration(d time.Duration) string {
|
|
||||||
if seconds := int(d.Seconds()); seconds < 1 {
|
|
||||||
return "Less than a second"
|
|
||||||
} else if seconds < 60 {
|
|
||||||
return fmt.Sprintf("%d seconds", seconds)
|
|
||||||
} else if minutes := int(d.Minutes()); minutes == 1 {
|
|
||||||
return "About a minute"
|
|
||||||
} else if minutes < 60 {
|
|
||||||
return fmt.Sprintf("%d minutes", minutes)
|
|
||||||
} else if hours := int(d.Hours()); hours == 1 {
|
|
||||||
return "About an hour"
|
|
||||||
} else if hours < 48 {
|
|
||||||
return fmt.Sprintf("%d hours", hours)
|
|
||||||
} else if hours < 24*7*2 {
|
|
||||||
return fmt.Sprintf("%d days", hours/24)
|
|
||||||
} else if hours < 24*30*3 {
|
|
||||||
return fmt.Sprintf("%d weeks", hours/24/7)
|
|
||||||
} else if hours < 24*365*2 {
|
|
||||||
return fmt.Sprintf("%d months", hours/24/30)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%f years", d.Hours()/24/365)
|
|
||||||
}
|
|
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/drone/drone/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewDisableCommand returns the CLI command for "disable".
|
// NewDisableCommand returns the CLI command for "disable".
|
||||||
|
@ -17,18 +18,13 @@ func NewDisableCommand() cli.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
// disableCommandFunc executes the "disable" command.
|
// disableCommandFunc executes the "disable" command.
|
||||||
func disableCommandFunc(c *cli.Context, client *Client) error {
|
func disableCommandFunc(c *cli.Context, client *client.Client) error {
|
||||||
var repo string
|
var host, owner, name string
|
||||||
var arg = c.Args()
|
var args = c.Args()
|
||||||
|
|
||||||
if len(arg) != 0 {
|
if len(args) != 0 {
|
||||||
repo = arg[0]
|
host, owner, name = parseRepo(args[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
err := client.Do("DELETE", "/v1/repos/"+repo, nil, nil)
|
return client.Repos.Disable(host, owner, name)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/drone/drone/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewEnableCommand returns the CLI command for "enable".
|
// NewEnableCommand returns the CLI command for "enable".
|
||||||
|
@ -17,18 +18,13 @@ func NewEnableCommand() cli.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
// enableCommandFunc executes the "enable" command.
|
// enableCommandFunc executes the "enable" command.
|
||||||
func enableCommandFunc(c *cli.Context, client *Client) error {
|
func enableCommandFunc(c *cli.Context, client *client.Client) error {
|
||||||
var repo string
|
var host, owner, name string
|
||||||
var arg = c.Args()
|
var args = c.Args()
|
||||||
|
|
||||||
if len(arg) != 0 {
|
if len(args) != 0 {
|
||||||
repo = arg[0]
|
host, owner, name = parseRepo(args[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
err := client.Do("POST", "/v1/repos/"+repo, nil, nil)
|
return client.Repos.Enable(host, owner, name)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,25 +4,28 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/drone/drone/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type handlerFunc func(*cli.Context, *Client) error
|
type handlerFunc func(*cli.Context, *client.Client) error
|
||||||
|
|
||||||
// handle wraps the command function handlers and
|
// handle wraps the command function handlers and
|
||||||
// sets up the environment.
|
// sets up the environment.
|
||||||
func handle(c *cli.Context, fn handlerFunc) {
|
func handle(c *cli.Context, fn handlerFunc) {
|
||||||
client := Client{}
|
var token = c.GlobalString("token")
|
||||||
client.Token = os.Getenv("DRONE_TOKEN")
|
var server = c.GlobalString("server")
|
||||||
client.URL = os.Getenv("DRONE_HOST")
|
|
||||||
|
|
||||||
// if no url is provided we can default
|
// if no server url is provided we can default
|
||||||
// to the hosted Drone service.
|
// to the hosted Drone service.
|
||||||
if len(client.URL) == 0 {
|
if len(server) == 0 {
|
||||||
client.URL = "http://test.drone.io"
|
server = "http://test.drone.io"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create the drone client
|
||||||
|
client := client.New(token, server)
|
||||||
|
|
||||||
// handle the function
|
// handle the function
|
||||||
if err := fn(c, &client); err != nil {
|
if err := fn(c, client); err != nil {
|
||||||
println(err.Error())
|
println(err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
22
cmd/main.go
22
cmd/main.go
|
@ -5,11 +5,31 @@ import (
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// commit sha for the current build.
|
||||||
|
version string = "0.3-dev"
|
||||||
|
revision string
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
app.Name = "drone"
|
app.Name = "drone"
|
||||||
app.Version = "1.0"
|
app.Version = version
|
||||||
app.Usage = "command line utility"
|
app.Usage = "command line utility"
|
||||||
|
app.Flags = []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "t, token",
|
||||||
|
Value: "",
|
||||||
|
Usage: "server auth token",
|
||||||
|
EnvVar: "DRONE_TOKEN",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "s, server",
|
||||||
|
Value: "",
|
||||||
|
Usage: "server location",
|
||||||
|
EnvVar: "DRONE_SERVER",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
NewBuildCommand(),
|
NewBuildCommand(),
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/drone/drone/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewRestartCommand returns the CLI command for "restart".
|
// NewRestartCommand returns the CLI command for "restart".
|
||||||
|
@ -19,22 +18,19 @@ func NewRestartCommand() cli.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
// restartCommandFunc executes the "restart" command.
|
// restartCommandFunc executes the "restart" command.
|
||||||
func restartCommandFunc(c *cli.Context, client *Client) error {
|
func restartCommandFunc(c *cli.Context, client *client.Client) error {
|
||||||
var branch string = "master"
|
var host, owner, repo, branch, sha string
|
||||||
var commit string
|
var args = c.Args()
|
||||||
var repo string
|
|
||||||
var arg = c.Args()
|
|
||||||
|
|
||||||
switch len(arg) {
|
if len(args) == 5 {
|
||||||
case 2:
|
host, owner, repo = parseRepo(args[0])
|
||||||
repo = arg[0]
|
} else {
|
||||||
commit = arg[1]
|
host = "unknown"
|
||||||
case 3:
|
owner = "unknown"
|
||||||
repo = arg[0]
|
repo = "unknown"
|
||||||
branch = arg[1]
|
branch = "unknown"
|
||||||
commit = arg[2]
|
sha = "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
path := fmt.Sprintf("/v1/repos/%s/branches/%s/commits/%s?action=rebuild", repo, branch, commit)
|
return client.Commits.Rebuild(host, owner, repo, branch, sha)
|
||||||
return client.Do("POST", path, nil, nil)
|
|
||||||
}
|
}
|
||||||
|
|
14
cmd/util.go
14
cmd/util.go
|
@ -9,6 +9,20 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func parseRepo(str string) (host, owner, repo string) {
|
||||||
|
var parts = strings.Split(str, "/")
|
||||||
|
if len(repo) != 3 {
|
||||||
|
host = "undefined"
|
||||||
|
owner = "undefined"
|
||||||
|
repo = "undefined"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
host = parts[0]
|
||||||
|
owner = parts[1]
|
||||||
|
repo = parts[2]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// getGoPath checks the source codes absolute path
|
// getGoPath checks the source codes absolute path
|
||||||
// in reference to the host operating system's GOPATH
|
// in reference to the host operating system's GOPATH
|
||||||
// to correctly determine the code's package path. This
|
// to correctly determine the code's package path. This
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
"github.com/drone/drone/shared/model"
|
"github.com/drone/drone/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewWhoamiCommand returns the CLI command for "whoami".
|
// NewWhoamiCommand returns the CLI command for "whoami".
|
||||||
|
@ -20,9 +20,8 @@ func NewWhoamiCommand() cli.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
// whoamiCommandFunc executes the "logout" command.
|
// whoamiCommandFunc executes the "logout" command.
|
||||||
func whoamiCommandFunc(c *cli.Context, client *Client) error {
|
func whoamiCommandFunc(c *cli.Context, client *client.Client) error {
|
||||||
user := model.User{}
|
user, err := client.Users.GetCurrent()
|
||||||
err := client.Do("GET", "/v1/user", nil, &user)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue