Merge branch 'master' into master

This commit is contained in:
Marko Gaćeša 2021-06-29 11:21:05 +02:00 committed by GitHub
commit a6f931e697
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
143 changed files with 4412 additions and 23114 deletions

1
.gitignore vendored
View file

@ -1,4 +1,5 @@
.vscode
__debug_bin
*.sqlite
*.txt
*.out

View file

@ -2,8 +2,6 @@
2. Install go 1.11 or later with Go modules enabled
3. Install binaries to $GOPATH/bin
go install github.com/drone/drone/cmd/drone-agent
go install github.com/drone/drone/cmd/drone-controller
go install github.com/drone/drone/cmd/drone-server
4. Start the server at localhost:8080

View file

@ -5,6 +5,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
## [2.0.1]
### Added
- support for configuring the internal yaml cache size.
## [2.0.0]
### Added
- feature flags for mixed-mode database encryption.
### Changed
- user-interface re-design
### Breaking
- removed deprecated kubernetes integration in favor of official kubernetes runner.
- removed deprecated nomad integration in favor of official nomad runner.
@ -82,13 +94,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- page to view the latest build per branch.
### Fixed
- sync routine not executing asyncronously, being cancelled by http context.
- sync routine not executing asynchronously, being cancelled by http context.
- sync routine should ignore gitlab subrepositories
- convert deploy events in 0.8 yaml to promote events.
- do not execute cron job for disabled repositories. [#2931](https://github.com/drone/drone/issues/2931).
- remove trailing slash from gitea url to prevent oauth2 token refresh errors, by [@cmj0121](https://github.com/cmj0121). [#2920](https://github.com/drone/drone/issues/2920).
- disable font ligatures in build log output. [drone/drone-ui#322](https://github.com/drone/drone-ui/pull/322).
- missing am/pm in timstamps
- missing am/pm in timestamps
## [1.6.5] - 2020-01-29
### Changed

View file

@ -87,6 +87,7 @@ tasks:
- cmd: go test -count=1 github.com/drone/drone/store/secret/global
- cmd: go test -count=1 github.com/drone/drone/store/stage
- cmd: go test -count=1 github.com/drone/drone/store/step
- cmd: go test -count=1 github.com/drone/drone/store/template
- cmd: go test -count=1 github.com/drone/drone/store/user
- cmd: docker kill mysql
@ -120,6 +121,7 @@ tasks:
- cmd: go test -count=1 github.com/drone/drone/store/secret/global
- cmd: go test -count=1 github.com/drone/drone/store/stage
- cmd: go test -count=1 github.com/drone/drone/store/step
- cmd: go test -count=1 github.com/drone/drone/store/template
- cmd: go test -count=1 github.com/drone/drone/store/user
- cmd: docker kill postgres
silent: true

View file

@ -111,12 +111,17 @@ type (
// Database provides the database configuration.
Database struct {
Driver string `envconfig:"DRONE_DATABASE_DRIVER" default:"sqlite3"`
Datasource string `envconfig:"DRONE_DATABASE_DATASOURCE" default:"core.sqlite"`
Secret string `envconfig:"DRONE_DATABASE_SECRET"`
Driver string `envconfig:"DRONE_DATABASE_DRIVER" default:"sqlite3"`
Datasource string `envconfig:"DRONE_DATABASE_DATASOURCE" default:"core.sqlite"`
Secret string `envconfig:"DRONE_DATABASE_SECRET"`
MaxConnections int `envconfig:"DRONE_DATABASE_MAX_CONNECTIONS" default:"0"`
// Feature flag
LegacyBatch bool `envconfig:"DRONE_DATABASE_LEGACY_BATCH"`
// Feature flag
EncryptUserTable bool `envconfig:"DRONE_DATABASE_ENCRYPT_USER_TABLE"`
EncryptMixedContent bool `envconfig:"DRONE_DATABASE_ENCRYPT_MIXED_MODE"`
}
// Docker provides docker configuration
@ -302,6 +307,7 @@ type (
Endpoint string `envconfig:"DRONE_CONVERT_PLUGIN_ENDPOINT"`
Secret string `envconfig:"DRONE_CONVERT_PLUGIN_SECRET"`
SkipVerify bool `envconfig:"DRONE_CONVERT_PLUGIN_SKIP_VERIFY"`
CacheSize int `envconfig:"DRONE_CONVERT_PLUGIN_CACHE_SIZE" default:"10"`
Timeout time.Duration `envconfig:"DRONE_CONVERT_TIMEOUT" default:"1m"`
}

View file

@ -20,7 +20,6 @@ import (
"github.com/drone/drone/plugin/admission"
"github.com/drone/drone/plugin/config"
"github.com/drone/drone/plugin/converter"
"github.com/drone/drone/plugin/converter/starlark"
"github.com/drone/drone/plugin/registry"
"github.com/drone/drone/plugin/secret"
"github.com/drone/drone/plugin/validator"
@ -77,15 +76,18 @@ func provideConfigPlugin(client *scm.Client, contents core.FileService, conf spe
// provideConvertPlugin is a Wire provider function that returns
// a yaml conversion plugin based on the environment
// configuration.
func provideConvertPlugin(client *scm.Client, conf spec.Config) core.ConvertService {
func provideConvertPlugin(client *scm.Client, conf spec.Config, templateStore core.TemplateStore) core.ConvertService {
return converter.Combine(
converter.Legacy(false),
starlark.New(
converter.Starlark(
conf.Starlark.Enabled,
),
converter.Jsonnet(
conf.Jsonnet.Enabled,
),
converter.Template(
templateStore,
),
converter.Memoize(
converter.Remote(
conf.Convert.Endpoint,
@ -94,6 +96,7 @@ func provideConvertPlugin(client *scm.Client, conf spec.Config) core.ConvertServ
conf.Convert.SkipVerify,
conf.Convert.Timeout,
),
conf.Convert.CacheSize,
),
)
}

View file

@ -31,9 +31,11 @@ import (
"github.com/drone/drone/store/shared/encrypt"
"github.com/drone/drone/store/stage"
"github.com/drone/drone/store/step"
"github.com/drone/drone/store/template"
"github.com/drone/drone/store/user"
"github.com/google/wire"
"github.com/sirupsen/logrus"
)
// wire set for loading the stores.
@ -52,6 +54,7 @@ var storeSet = wire.NewSet(
secret.New,
global.New,
step.New,
template.New,
)
// provideDatabase is a Wire provider function that provides a
@ -60,13 +63,27 @@ func provideDatabase(config config.Config) (*db.DB, error) {
return db.Connect(
config.Database.Driver,
config.Database.Datasource,
config.Database.MaxConnections,
)
}
// provideEncrypter is a Wire provider function that provides a
// database encrypter, configured from the environment.
func provideEncrypter(config config.Config) (encrypt.Encrypter, error) {
return encrypt.New(config.Database.Secret)
enc, err := encrypt.New(config.Database.Secret)
// mixed-content mode should be set to true if the database
// originally had encryption disabled and therefore has
// plaintext entries. This prevents Drone from returning an
// error if decryption fails; on failure, the ciphertext is
// returned as-is and the error is ignored.
if aesgcm, ok := enc.(*encrypt.Aesgcm); ok {
logrus.Debugln("main: database encryption enabled")
if config.Database.EncryptMixedContent {
logrus.Debugln("main: database encryption mixed-mode enabled")
aesgcm.Compat = true
}
}
return enc, err
}
// provideBuildStore is a Wire provider function that provides a
@ -123,15 +140,6 @@ func provideRepoStore(db *db.DB) core.RepositoryStore {
return repos
}
// provideUserStore is a Wire provider function that provides a
// user datastore, configured from the environment, with metrics
// enabled.
func provideUserStore(db *db.DB) core.UserStore {
users := user.New(db)
metric.UserCount(users)
return users
}
// provideBatchStore is a Wire provider function that provides a
// batcher. If the experimental batcher is enabled it is returned.
func provideBatchStore(db *db.DB, config config.Config) core.Batcher {
@ -140,3 +148,32 @@ func provideBatchStore(db *db.DB, config config.Config) core.Batcher {
}
return batch2.New(db)
}
// provideUserStore is a Wire provider function that provides a
// user datastore, configured from the environment, with metrics
// enabled.
func provideUserStore(db *db.DB, enc encrypt.Encrypter, config config.Config) core.UserStore {
// create the user store with encryption iff the user
// encryption feature flag is enabled.
//
// why not enable by default? because the user table is
// accessed on every http request and we are unsure what,
// if any performance implications user table encryption
// may have on the system.
//
// it is very possible there are zero material performance
// implications, however, if there is a performance regression
// we could look at implementing in-memory lru caching, which
// we already employ in other areas of the software.
if config.Database.EncryptUserTable {
logrus.Debugln("main: database encryption enabled for user table")
users := user.New(db, enc)
metric.UserCount(users)
return users
}
noenc, _ := encrypt.New("")
users := user.New(db, noenc)
metric.UserCount(users)
return users
}

View file

@ -1,6 +1,6 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//go:generate go run github.com/google/wire/cmd/wire
//+build !wireinject
package main
@ -25,6 +25,7 @@ import (
"github.com/drone/drone/store/secret"
"github.com/drone/drone/store/secret/global"
"github.com/drone/drone/store/step"
"github.com/drone/drone/store/template"
"github.com/drone/drone/trigger"
cron2 "github.com/drone/drone/trigger/cron"
)
@ -44,7 +45,11 @@ func InitializeApplication(config2 config.Config) (application, error) {
if err != nil {
return application{}, err
}
userStore := provideUserStore(db)
encrypter, err := provideEncrypter(config2)
if err != nil {
return application{}, err
}
userStore := provideUserStore(db, encrypter, config2)
renewer := token.Renewer(refresher, userStore)
commitService := commit.New(client, renewer)
cronStore := cron.New(db)
@ -60,7 +65,8 @@ func InitializeApplication(config2 config.Config) (application, error) {
coreCanceler := canceler.New(buildStore, corePubsub, repositoryStore, scheduler, stageStore, statusService, stepStore, userStore, webhookSender)
fileService := provideContentService(client, renewer)
configService := provideConfigPlugin(client, fileService, config2)
convertService := provideConvertPlugin(client, config2)
templateStore := template.New(db)
convertService := provideConvertPlugin(client, config2, templateStore)
validateService := provideValidatePlugin(config2)
triggerer := trigger.New(coreCanceler, configService, convertService, commitService, statusService, buildStore, scheduler, repositoryStore, userStore, validateService, webhookSender)
cronScheduler := cron2.New(commitService, cronStore, repositoryStore, userStore, triggerer)
@ -70,10 +76,6 @@ func InitializeApplication(config2 config.Config) (application, error) {
logStore := provideLogStore(db, config2)
logStream := livelog.New()
netrcService := provideNetrcService(client, renewer, config2)
encrypter, err := provideEncrypter(config2)
if err != nil {
return application{}, err
}
secretStore := secret.New(db, encrypter)
globalSecretStore := global.New(db, encrypter)
buildManager := manager.New(buildStore, configService, convertService, corePubsub, logStore, logStream, netrcService, repositoryStore, scheduler, secretStore, globalSecretStore, statusService, stageStore, stepStore, system, userStore, webhookSender)
@ -93,7 +95,7 @@ func InitializeApplication(config2 config.Config) (application, error) {
syncer := provideSyncer(repositoryService, repositoryStore, userStore, batcher, config2)
transferer := transfer.New(repositoryStore, permStore)
userService := user.New(client, renewer)
server := api.New(buildStore, commitService, cronStore, corePubsub, globalSecretStore, hookService, logStore, coreLicense, licenseService, organizationService, permStore, repositoryStore, repositoryService, scheduler, secretStore, stageStore, stepStore, statusService, session, logStream, syncer, system, transferer, triggerer, userStore, userService, webhookSender)
server := api.New(buildStore, commitService, cronStore, corePubsub, globalSecretStore, hookService, logStore, coreLicense, licenseService, organizationService, permStore, repositoryStore, repositoryService, scheduler, secretStore, stageStore, stepStore, statusService, session, logStream, syncer, system, templateStore, transferer, triggerer, userStore, userService, webhookSender)
admissionService := provideAdmissionPlugin(client, organizationService, userService, config2)
hookParser := parser.New(client)
coreLinker := linker.New(client)

View file

@ -77,7 +77,7 @@ type BuildStore interface {
LatestBranches(context.Context, int64) ([]*Build, error)
// LatestPulls returns the latest builds from the
// datastore by pull requeset.
// datastore by pull request.
LatestPulls(context.Context, int64) ([]*Build, error)
// LatestDeploys returns the latest builds from the

View file

@ -19,17 +19,20 @@ import "context"
type (
// Step represents an individual step in the stage.
Step struct {
ID int64 `json:"id"`
StageID int64 `json:"step_id"`
Number int `json:"number"`
Name string `json:"name"`
Status string `json:"status"`
Error string `json:"error,omitempty"`
ErrIgnore bool `json:"errignore,omitempty"`
ExitCode int `json:"exit_code"`
Started int64 `json:"started,omitempty"`
Stopped int64 `json:"stopped,omitempty"`
Version int64 `json:"version"`
ID int64 `json:"id"`
StageID int64 `json:"step_id"`
Number int `json:"number"`
Name string `json:"name"`
Status string `json:"status"`
Error string `json:"error,omitempty"`
ErrIgnore bool `json:"errignore,omitempty"`
ExitCode int `json:"exit_code"`
Started int64 `json:"started,omitempty"`
Stopped int64 `json:"stopped,omitempty"`
Version int64 `json:"version"`
DependsOn []string `json:"depends_on,omitempty"`
Image string `json:"image,omitempty"`
Detached bool `json:"detached,omitempty"`
}
// StepStore persists build step information to storage.

79
core/template.go Normal file
View file

@ -0,0 +1,79 @@
// Copyright 2019 Drone IO, Inc.
//
// 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 core
import (
"context"
"github.com/drone/drone/handler/api/errors"
)
var (
errTemplateNameInvalid = errors.New("No Template Name Provided")
errTemplateDataInvalid = errors.New("No Template Data Provided")
)
type (
TemplateArgs struct {
Kind string
Load string
Data map[string]interface{}
}
Template struct {
Id int64 `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Namespace string `json:"namespace,omitempty"`
Data string `json:"data,omitempty"`
Created int64 `json:"created,omitempty"`
Updated int64 `json:"updated,omitempty"`
}
// TemplateStore manages repository templates.
TemplateStore interface {
// List returns template list at org level
List(ctx context.Context, namespace string) ([]*Template, error)
// ListAll returns templates list from the datastore.
ListAll(ctx context.Context) ([]*Template, error)
// Find returns a template from the datastore.
Find(ctx context.Context, id int64) (*Template, error)
// FindName returns a template from the data store
FindName(ctx context.Context, name string, namespace string) (*Template, error)
// Create persists a new template to the datastore.
Create(ctx context.Context, template *Template) error
// Update persists an updated template to the datastore.
Update(ctx context.Context, template *Template) error
// Delete deletes a template from the datastore.
Delete(ctx context.Context, template *Template) error
}
)
// Validate validates the required fields and formats.
func (s *Template) Validate() error {
switch {
case len(s.Name) == 0:
return errTemplateNameInvalid
case len(s.Data) == 0:
return errTemplateDataInvalid
default:
return nil
}
}

View file

@ -16,7 +16,7 @@ package core
import "context"
// Transferer handles transfering repository ownership from one
// Transferer handles transferring repository ownership from one
// user to another user account.
type Transferer interface {
Transfer(ctx context.Context, user *User) error

View file

@ -47,6 +47,16 @@ type (
Hash string `json:"-"`
}
// UserParams defines user query parameters.
UserParams struct {
// Sort instructs the system to sort by Login if true,
// else sort by primary key.
Sort bool
Page int64
Size int64
}
// UserStore defines operations for working with users.
UserStore interface {
// Find returns a user from the datastore.
@ -61,6 +71,9 @@ type (
// List returns a list of users from the datastore.
List(context.Context) ([]*User, error)
// ListRange returns a range of users from the datastore.
ListRange(context.Context, UserParams) ([]*User, error)
// Create persists a new user to the datastore.
Create(context.Context, *User) error
@ -88,13 +101,13 @@ type (
}
)
// Validate valides the user and returns an error if the
// Validate validates the user and returns an error if the
// validation fails.
func (u *User) Validate() error {
switch {
case !govalidator.IsByteLength(u.Login, 1, 50):
return errUsernameLen
case !govalidator.Matches(u.Login, "^[a-zA-Z0-9_-]+$"):
case !govalidator.Matches(u.Login, "^[.a-zA-Z0-9_-]+$"):
return errUsernameChar
default:
return nil

View file

@ -39,6 +39,10 @@ func TestValidateUser(t *testing.T) {
user: &User{Login: "octocat"},
err: nil,
},
{
user: &User{Login: "octocat.with.dot"},
err: nil,
},
{
user: &User{Login: "OctO-Cat_01"},
err: nil,

View file

@ -1 +1,57 @@
This directory contains docker compose files used by the core development team for local development and testing purposes only. These are not part of the core distribution, and are not intended for use outside of the core development team. We are not currently accepting changes or additions to these files.
# Local development
This directory contains Docker compose files used by the core development team for local development and testing purposes only. These are not part of the core distribution, and are not intended for use outside of the core development team. We are not currently accepting changes or additions to these files.
## Running a Drone deployment locally using Github
At the end of this guide you will have a drone server and a drone runner that is hooked up to your Github account. This will allow you to trigger builds on your Github repositories.
### (prerequisite) Setup a Github oauth application
Create an oauth application here <https://github.com/settings/developers>
The most important entry is setting the `Authorization callback URL` you can set this to `http://localhost:8080/login`
You will also need to create a client secret for the application.
Now you have the `DRONE_GITHUB_CLIENT_ID` and `DRONE_GITHUB_CLIENT_SECRET`
### (prerequisite) Setup Ngrok
Ngrok allows us to send the webhooks from Github to our local Drone setup.
Follow the guide here <https://dashboard.ngrok.com/get-started/setup>
### Running Drone
+ Move into the `drone/docker/compose/drone-github` folder.
+ Run Ngrok against port `8080` it will run in the foreground.
``` bash
./ngrok http 8080
```
Take note of the forwarding hostname this is your `DRONE_SERVER_PROXY_HOST` EG
``` bash
Forwarding http://c834c33asdde.ngrok.io -> http://localhost:8080
```
+ You will want to edit the Docker compose file `docker-compose.yml` updating in the following entries.
``` bash
DRONE_SERVER_PROXY_HOST=${DRONE_SERVER_PROXY_HOST} # taken from Ngrok
DRONE_GITHUB_CLIENT_ID=${DRONE_GITHUB_CLIENT_ID} # taken from your Github oauth application
DRONE_GITHUB_CLIENT_SECRET=${DRONE_GITHUB_CLIENT_SECRET} # taken from your Github oauth application
```
NB for `DRONE_SERVER_PROXY_HOST` do not include http/https.
+ Run docker compose
``` bash
docker-compose up
```
Now you can go access the Drone ui at <http://localhost:8080>

View file

@ -24,5 +24,6 @@ services:
- DRONE_RPC_HOST=drone
- DRONE_RPC_PROTO=http
- DRONE_RPC_SECRET=bea26a2221fd8090ea38720fc445eca6
- DRONE_TMATE_ENABLED=true
volumes:
- /var/run/docker.sock:/var/run/docker.sock

6
go.mod
View file

@ -15,12 +15,12 @@ require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/drone/drone-go v1.4.1-0.20201109202657-b9e58bbbcf27
github.com/drone/drone-runtime v1.1.1-0.20200623162453-61e33e2cab5d
github.com/drone/drone-ui v0.0.0-20201110214517-ac1349fcc19c
github.com/drone/drone-ui v0.0.0-20210623224836-9a5c77ebfdb7
github.com/drone/drone-yaml v1.2.4-0.20200326192514-6f4d6dfb39e4
github.com/drone/envsubst v1.0.3-0.20200709231038-aa43e1c1a629
github.com/drone/go-license v1.0.2
github.com/drone/go-login v1.0.4-0.20190311170324-2a4df4f242a2
github.com/drone/go-scm v1.8.0
github.com/drone/go-scm v1.15.1
github.com/drone/signal v1.0.0
github.com/dustin/go-humanize v1.0.0
github.com/go-chi/chi v3.3.3+incompatible
@ -29,7 +29,7 @@ require (
github.com/go-sql-driver/mysql v1.4.0
github.com/golang/mock v1.3.1
github.com/google/go-cmp v0.4.0
github.com/google/go-jsonnet v0.16.0
github.com/google/go-jsonnet v0.17.0
github.com/google/wire v0.2.1
github.com/gorhill/cronexpr v0.0.0-20140423231348-a557574d6c02 // indirect
github.com/gosimple/slug v1.3.0

49
go.sum
View file

@ -92,6 +92,42 @@ github.com/drone/drone-ui v0.0.0-20200701170131-2b91a041998b h1:8VfphhR5arTUOFGf
github.com/drone/drone-ui v0.0.0-20200701170131-2b91a041998b/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI=
github.com/drone/drone-ui v0.0.0-20201110214517-ac1349fcc19c h1:RHVLOVo6vC/p3i64XMQ6lcURQCeqUbg+7vyhCAy8hq0=
github.com/drone/drone-ui v0.0.0-20201110214517-ac1349fcc19c/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI=
github.com/drone/drone-ui v0.0.0-20210318043635-d92124ef8c6b h1:SR3ORKKtSGLA2oGHBr6tP+KaHRB7JSveYreZVxnfQgA=
github.com/drone/drone-ui v0.0.0-20210318043635-d92124ef8c6b/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI=
github.com/drone/drone-ui v0.0.0-20210318051923-d744fdf178d7 h1:KpTwlg8z0xcT7UZXTzK8G0r5+1CeXKkglZwsg6wHuK4=
github.com/drone/drone-ui v0.0.0-20210318051923-d744fdf178d7/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI=
github.com/drone/drone-ui v0.0.0-20210318173335-90cdf440f023 h1:xy7suHSQ4wEuOa8EnCmBpuMEXcfG66QJxY0rdDG9w3c=
github.com/drone/drone-ui v0.0.0-20210318173335-90cdf440f023/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI=
github.com/drone/drone-ui v0.0.0-20210318184040-660cf374c83a h1:b36lw0bXDWRB8miPIA59SUgpV+mTvoUX7otADm8SwyQ=
github.com/drone/drone-ui v0.0.0-20210318184040-660cf374c83a/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI=
github.com/drone/drone-ui v0.0.0-20210318190358-0fd3dd080c5b h1:CfVtYTmPVUm5x2UTUvFF8NtQMYWwlVWz9OiRfxuiLQg=
github.com/drone/drone-ui v0.0.0-20210318190358-0fd3dd080c5b/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI=
github.com/drone/drone-ui v0.0.0-20210318211453-780e11c45d95 h1:8JsUeiL9slNUbMUYGINI0gi2owD/0JnzKgNrv6EmxHY=
github.com/drone/drone-ui v0.0.0-20210318211453-780e11c45d95/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI=
github.com/drone/drone-ui v0.0.0-20210319172440-b892bff542b5 h1:6mjZNWxqVKkBt1Pc2ChdMFOzwa8+7oTVX4dTLnEx4dA=
github.com/drone/drone-ui v0.0.0-20210319172440-b892bff542b5/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI=
github.com/drone/drone-ui v0.0.0-20210322195809-43fc5b050223 h1:lyWHV4jHKzF8kKBrEXjlZDTyuuXuAQ2BabeURGt5hLo=
github.com/drone/drone-ui v0.0.0-20210322195809-43fc5b050223/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI=
github.com/drone/drone-ui v0.0.0-20210324203842-aaae3627e294 h1:ifbOWfDF/4zNxXW69f4XL06D7aA7cdp5U0moNNh60nw=
github.com/drone/drone-ui v0.0.0-20210324203842-aaae3627e294/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI=
github.com/drone/drone-ui v0.0.0-20210331233327-772f24b7f519 h1:zEPxQwLA/dI6sQ9htLPWcDT+EKms09w4TSAYk1lOQiA=
github.com/drone/drone-ui v0.0.0-20210331233327-772f24b7f519/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI=
github.com/drone/drone-ui v0.0.0-20210427231613-96a9f6e17fb7 h1:EUvu5J51N2diuFqLjFTiu65m0EK0TlLD66qDyi43tVE=
github.com/drone/drone-ui v0.0.0-20210427231613-96a9f6e17fb7/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI=
github.com/drone/drone-ui v0.0.0-20210505020539-1b93fc25c7d8 h1:mIFBOdP8Tif/4li4bmiVjqp6vbxMpTHOuZR80eypm6A=
github.com/drone/drone-ui v0.0.0-20210505020539-1b93fc25c7d8/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI=
github.com/drone/drone-ui v0.0.0-20210512200715-d96f1e26d448 h1:skfTTwMRWSSi3Dv5NrpU8mJn7faccG5q+lqiBQikLiw=
github.com/drone/drone-ui v0.0.0-20210512200715-d96f1e26d448/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI=
github.com/drone/drone-ui v0.0.0-20210602131102-d9e6fc7e8e68 h1:MFiB2sySRfQfdGyzCw6ii45IVgNXS1Ho+YmKxdmp9eU=
github.com/drone/drone-ui v0.0.0-20210602131102-d9e6fc7e8e68/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI=
github.com/drone/drone-ui v0.0.0-20210616003421-c6bb64fb3c3b h1:w7zIfuX1sv491AC0hGQzbtGQ/IXfiQmLbBKVrIuEC+0=
github.com/drone/drone-ui v0.0.0-20210616003421-c6bb64fb3c3b/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI=
github.com/drone/drone-ui v0.0.0-20210616023721-d11e2b9d4c32 h1:pnrIQX6PDZCXs8uh6kvWrjXwhw3ZSg6D1Yp6W+Pe5Pw=
github.com/drone/drone-ui v0.0.0-20210616023721-d11e2b9d4c32/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI=
github.com/drone/drone-ui v0.0.0-20210623033012-76a6768a4050 h1:VjS2w8DtcdOajM11UYc2bzlqXONna5MnyToMWO6uCJk=
github.com/drone/drone-ui v0.0.0-20210623033012-76a6768a4050/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI=
github.com/drone/drone-ui v0.0.0-20210623224836-9a5c77ebfdb7 h1:gqEVIHEIdzBtoeL0fE33Ba+OD1QARTGRZTw4jrGOxCk=
github.com/drone/drone-ui v0.0.0-20210623224836-9a5c77ebfdb7/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI=
github.com/drone/drone-yaml v1.2.4-0.20200326192514-6f4d6dfb39e4 h1:XsstoCeXC2t8lA9OLTdoFwckaptqahxwjCWsenySfX8=
github.com/drone/drone-yaml v1.2.4-0.20200326192514-6f4d6dfb39e4/go.mod h1:QsqliFK8nG04AHFN9tTn9XJomRBQHD4wcejWW1uz/10=
github.com/drone/envsubst v1.0.3-0.20200709231038-aa43e1c1a629 h1:rIaZZalMGGPb2cU/+ypuggZ8aMlpa17RUlJUtsMv8pw=
@ -110,6 +146,12 @@ github.com/drone/go-scm v1.7.2-0.20201111225713-c0438b46084b h1:ivLeFPmHN+9sLMVA
github.com/drone/go-scm v1.7.2-0.20201111225713-c0438b46084b/go.mod h1:lXwfbyrIJwFFME5TpzavkwO2T5X8yBK6t6cve7g91x0=
github.com/drone/go-scm v1.8.0 h1:kDHu38a11loKf6uaBu75TmY1YPwsSaZdseET738Oy0o=
github.com/drone/go-scm v1.8.0/go.mod h1:lXwfbyrIJwFFME5TpzavkwO2T5X8yBK6t6cve7g91x0=
github.com/drone/go-scm v1.9.0 h1:KgaGREXA7Ncu4ccdnk7p93hJwE8B8GLaBHfRprwtUCE=
github.com/drone/go-scm v1.9.0/go.mod h1:lXwfbyrIJwFFME5TpzavkwO2T5X8yBK6t6cve7g91x0=
github.com/drone/go-scm v1.15.0 h1:yBO6lcCeegbEuEaH0QUvJmBVQS/RpYKzuzULHHMT2A4=
github.com/drone/go-scm v1.15.0/go.mod h1:lXwfbyrIJwFFME5TpzavkwO2T5X8yBK6t6cve7g91x0=
github.com/drone/go-scm v1.15.1 h1:35m/CcHkYjQ4BlOM7rIIwrki6uDUbUH+Kkb9rv6om3M=
github.com/drone/go-scm v1.15.1/go.mod h1:lXwfbyrIJwFFME5TpzavkwO2T5X8yBK6t6cve7g91x0=
github.com/drone/signal v1.0.0 h1:NrnM2M/4yAuU/tXs6RP1a1ZfxnaHwYkd0kJurA1p6uI=
github.com/drone/signal v1.0.0/go.mod h1:S8t92eFT0g4WUgEc/LxG+LCuiskpMNsG0ajAMGnyZpc=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
@ -158,6 +200,8 @@ github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-jsonnet v0.16.0 h1:Nb4EEOp+rdeGGyB1rQ5eisgSAqrTnhf9ip+X6lzZbY0=
github.com/google/go-jsonnet v0.16.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw=
github.com/google/go-jsonnet v0.17.0 h1:/9NIEfhK1NQRKl3sP2536b2+x5HnZMdql7x3yK/l8JY=
github.com/google/go-jsonnet v0.17.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/wire v0.2.1 h1:TYj4Z2qjqxa2ufb34UJqVeO9aznL+i0fLO6TqThKZ7Y=
@ -337,6 +381,7 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -396,6 +441,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -414,6 +460,7 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890 h1:uESlIz09WIHT2I+pasSXcpLYqYK8wHcdCetU3VuMBJE=
@ -444,11 +491,13 @@ golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSf
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642 h1:B6caxRw+hozq68X2MY7jEpZh/cr4/aHLv9xU8Kkadrw=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=

View file

@ -40,6 +40,7 @@ import (
"github.com/drone/drone/handler/api/repos/sign"
globalsecrets "github.com/drone/drone/handler/api/secrets"
"github.com/drone/drone/handler/api/system"
"github.com/drone/drone/handler/api/template"
"github.com/drone/drone/handler/api/user"
"github.com/drone/drone/handler/api/user/remote"
"github.com/drone/drone/handler/api/users"
@ -82,6 +83,7 @@ func New(
stream core.LogStream,
syncer core.Syncer,
system *core.System,
template core.TemplateStore,
transferer core.Transferer,
triggerer core.Triggerer,
users core.UserStore,
@ -111,6 +113,7 @@ func New(
Stream: stream,
Syncer: syncer,
System: system,
Template: template,
Transferer: transferer,
Triggerer: triggerer,
Users: users,
@ -143,6 +146,7 @@ type Server struct {
Stream core.LogStream
Syncer core.Syncer
System *core.System
Template core.TemplateStore
Transferer core.Transferer
Triggerer core.Triggerer
Users core.UserStore
@ -353,6 +357,16 @@ func (s Server) Handler() http.Handler {
r.With(acl.CheckMembership(s.Orgs, true)).Delete("/{namespace}/{name}", globalsecrets.HandleDelete(s.Globals))
})
r.Route("/templates", func(r chi.Router) {
r.With(acl.CheckMembership(s.Orgs, false)).Get("/", template.HandleListAll(s.Template))
r.With(acl.CheckMembership(s.Orgs, true)).Post("/", template.HandleCreate(s.Template))
r.With(acl.CheckMembership(s.Orgs, false)).Get("/{namespace}", template.HandleList(s.Template))
r.With(acl.CheckMembership(s.Orgs, false)).Get("/{namespace}/{name}", template.HandleFind(s.Template))
r.With(acl.CheckMembership(s.Orgs, true)).Put("/{namespace}/{name}", template.HandleUpdate(s.Template))
r.With(acl.CheckMembership(s.Orgs, true)).Patch("/{namespace}/{name}", template.HandleUpdate(s.Template))
r.With(acl.CheckMembership(s.Orgs, true)).Delete("/{namespace}/{name}", template.HandleDelete(s.Template))
})
r.Route("/system", func(r chi.Router) {
r.Use(acl.AuthorizeAdmin)
// r.Get("/license", system.HandleLicense())

View file

@ -0,0 +1,27 @@
// Copyright 2019 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by the Drone Non-Commercial License
// that can be found in the LICENSE file.
// +build !oss
package template
import (
"net/http"
"github.com/drone/drone/core"
"github.com/drone/drone/handler/api/render"
)
// HandleListAll returns an http.HandlerFunc that writes a json-encoded
// list of templates to the response body.
func HandleListAll(templateStore core.TemplateStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
list, err := templateStore.ListAll(r.Context())
if err != nil {
render.NotFound(w, err)
return
}
render.JSON(w, list, 200)
}
}

View file

@ -0,0 +1,84 @@
// Copyright 2019 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by the Drone Non-Commercial License
// that can be found in the LICENSE file.
// +build !oss
package template
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/drone/drone/core"
"github.com/drone/drone/handler/api/errors"
"github.com/drone/drone/mock"
"github.com/go-chi/chi"
"github.com/golang/mock/gomock"
"github.com/google/go-cmp/cmp"
)
var (
dummyTemplate = &core.Template{
Name: "my_template",
Data: "my_data",
Created: 1,
Updated: 2,
Namespace: "my_org",
}
dummyTemplateList = []*core.Template{
dummyTemplate,
}
)
func TestHandleAll(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
templates := mock.NewMockTemplateStore(controller)
templates.EXPECT().ListAll(gomock.Any()).Return(dummyTemplateList, nil)
c := new(chi.Context)
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)
r = r.WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, c),
)
HandleListAll(templates).ServeHTTP(w, r)
if got, want := w.Code, http.StatusOK; want != got {
t.Errorf("Want response code %d, got %d", want, got)
}
}
func TestHandleAll_TemplateListErr(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
templates := mock.NewMockTemplateStore(controller)
templates.EXPECT().ListAll(gomock.Any()).Return(nil, errors.ErrNotFound)
c := new(chi.Context)
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)
r = r.WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, c),
)
HandleListAll(templates).ServeHTTP(w, r)
if got, want := w.Code, http.StatusNotFound; want != got {
t.Errorf("Want response code %d, got %d", want, got)
}
got, want := new(errors.Error), errors.ErrNotFound
json.NewDecoder(w.Body).Decode(got)
if diff := cmp.Diff(got, want); len(diff) != 0 {
t.Errorf(diff)
}
}

View file

@ -0,0 +1,54 @@
// Copyright 2019 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by the Drone Non-Commercial License
// that can be found in the LICENSE file.
// +build !oss
package template
import (
"encoding/json"
"net/http"
"github.com/drone/drone/core"
"github.com/drone/drone/handler/api/render"
)
type templateInput struct {
Name string `json:"name"`
Data string `json:"data"`
Namespace string `json:"namespace"`
}
// HandleCreate returns an http.HandlerFunc that processes http
// requests to create a new template.
func HandleCreate(templateStore core.TemplateStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
in := new(templateInput)
err := json.NewDecoder(r.Body).Decode(in)
if err != nil {
render.BadRequest(w, err)
return
}
t := &core.Template{
Name: in.Name,
Data: in.Data,
Namespace: in.Namespace,
}
err = t.Validate()
if err != nil {
render.BadRequest(w, err)
return
}
err = templateStore.Create(r.Context(), t)
if err != nil {
render.InternalError(w, err)
return
}
render.JSON(w, t, 200)
}
}

View file

@ -0,0 +1,154 @@
// Copyright 2019 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by the Drone Non-Commercial License
// that can be found in the LICENSE file.
// +build !oss
package template
import (
"bytes"
"context"
"encoding/json"
"github.com/drone/drone/core"
"github.com/drone/drone/handler/api/errors"
"github.com/drone/drone/mock"
"net/http"
"net/http/httptest"
"testing"
"github.com/go-chi/chi"
"github.com/golang/mock/gomock"
"github.com/google/go-cmp/cmp"
)
func TestHandleCreate(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
templates := mock.NewMockTemplateStore(controller)
templates.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil)
c := new(chi.Context)
in := new(bytes.Buffer)
json.NewEncoder(in).Encode(dummyTemplate)
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", in)
r = r.WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, c),
)
HandleCreate(templates).ServeHTTP(w, r)
if got, want := w.Code, http.StatusOK; want != got {
t.Errorf("Want response code %d, got %d", want, got)
}
}
func TestHandleCreate_ValidationErrorName(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
c := new(chi.Context)
in := new(bytes.Buffer)
json.NewEncoder(in).Encode(&core.Template{Name: "", Data: "my_data"})
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", in)
r = r.WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, c),
)
HandleCreate(nil).ServeHTTP(w, r)
if got, want := w.Code, http.StatusBadRequest; want != got {
t.Errorf("Want response code %d, got %d", want, got)
}
got, want := &errors.Error{}, &errors.Error{Message: "No Template Name Provided"}
json.NewDecoder(w.Body).Decode(got)
if diff := cmp.Diff(got, want); len(diff) != 0 {
t.Errorf(diff)
}
}
func TestHandleCreate_ValidationErrorData(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
c := new(chi.Context)
in := new(bytes.Buffer)
json.NewEncoder(in).Encode(&core.Template{Name: "my_template", Data: ""})
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", in)
r = r.WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, c),
)
HandleCreate(nil).ServeHTTP(w, r)
if got, want := w.Code, http.StatusBadRequest; want != got {
t.Errorf("Want response code %d, got %d", want, got)
}
got, want := &errors.Error{}, &errors.Error{Message: "No Template Data Provided"}
json.NewDecoder(w.Body).Decode(got)
if diff := cmp.Diff(got, want); len(diff) != 0 {
t.Errorf(diff)
}
}
func TestHandleCreate_BadRequest(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
c := new(chi.Context)
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)
r = r.WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, c),
)
HandleCreate(nil).ServeHTTP(w, r)
if got, want := w.Code, http.StatusBadRequest; want != got {
t.Errorf("Want response code %d, got %d", want, got)
}
got, want := &errors.Error{}, &errors.Error{Message: "EOF"}
json.NewDecoder(w.Body).Decode(got)
if diff := cmp.Diff(got, want); len(diff) != 0 {
t.Errorf(diff)
}
}
func TestHandleCreate_CreateError(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
templates := mock.NewMockTemplateStore(controller)
templates.EXPECT().Create(gomock.Any(), gomock.Any()).Return(errors.ErrNotFound)
c := new(chi.Context)
in := new(bytes.Buffer)
json.NewEncoder(in).Encode(dummyTemplate)
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", in)
r = r.WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, c),
)
HandleCreate(templates).ServeHTTP(w, r)
if got, want := w.Code, http.StatusInternalServerError; want != got {
t.Errorf("Want response code %d, got %d", want, got)
}
got, want := new(errors.Error), errors.ErrNotFound
json.NewDecoder(w.Body).Decode(got)
if diff := cmp.Diff(got, want); len(diff) != 0 {
t.Errorf(diff)
}
}

View file

@ -0,0 +1,38 @@
// Copyright 2019 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by the Drone Non-Commercial License
// that can be found in the LICENSE file.
// +build !oss
package template
import (
"net/http"
"github.com/drone/drone/core"
"github.com/drone/drone/handler/api/render"
"github.com/go-chi/chi"
)
// HandleDelete returns an http.HandlerFunc that processes http
// requests to delete a template.
func HandleDelete(template core.TemplateStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var (
name = chi.URLParam(r, "name")
namespace = chi.URLParam(r, "namespace")
)
s, err := template.FindName(r.Context(), name, namespace)
if err != nil {
render.NotFound(w, err)
return
}
err = template.Delete(r.Context(), s)
if err != nil {
render.InternalError(w, err)
return
}
w.WriteHeader(http.StatusNoContent)
}
}

View file

@ -0,0 +1,105 @@
// Copyright 2019 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by the Drone Non-Commercial License
// that can be found in the LICENSE file.
// +build !oss
package template
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/drone/drone/handler/api/errors"
"github.com/drone/drone/mock"
"github.com/go-chi/chi"
"github.com/golang/mock/gomock"
"github.com/google/go-cmp/cmp"
)
func TestHandleDelete(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
template := mock.NewMockTemplateStore(controller)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(dummyTemplate, nil)
template.EXPECT().Delete(gomock.Any(), dummyTemplate).Return(nil)
c := new(chi.Context)
c.URLParams.Add("name", "my_template")
c.URLParams.Add("namespace", "my_org")
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)
r = r.WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, c),
)
HandleDelete(template).ServeHTTP(w, r)
if got, want := w.Code, http.StatusNoContent; want != got {
t.Errorf("Want response code %d, got %d", want, got)
}
}
func TestHandleDelete_TemplateNotFound(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
template := mock.NewMockTemplateStore(controller)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(nil, errors.ErrNotFound)
c := new(chi.Context)
c.URLParams.Add("name", "my_template")
c.URLParams.Add("namespace", "my_org")
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)
r = r.WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, c),
)
HandleDelete(template).ServeHTTP(w, r)
if got, want := w.Code, http.StatusNotFound; want != got {
t.Errorf("Want response code %d, got %d", want, got)
}
got, want := new(errors.Error), errors.ErrNotFound
json.NewDecoder(w.Body).Decode(got)
if diff := cmp.Diff(got, want); len(diff) != 0 {
t.Errorf(diff)
}
}
func TestHandleDelete_DeleteError(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
template := mock.NewMockTemplateStore(controller)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(dummyTemplate, nil)
template.EXPECT().Delete(gomock.Any(), dummyTemplate).Return(errors.ErrNotFound)
c := new(chi.Context)
c.URLParams.Add("name", "my_template")
c.URLParams.Add("namespace", "my_org")
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)
r = r.WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, c),
)
HandleDelete(template).ServeHTTP(w, r)
if got, want := w.Code, http.StatusInternalServerError; want != got {
t.Errorf("Want response code %d, got %d", want, got)
}
got, want := new(errors.Error), errors.ErrNotFound
json.NewDecoder(w.Body).Decode(got)
if diff := cmp.Diff(got, want); len(diff) != 0 {
t.Errorf(diff)
}
}

View file

@ -0,0 +1,33 @@
// Copyright 2019 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by the Drone Non-Commercial License
// that can be found in the LICENSE file.
// +build !oss
package template
import (
"net/http"
"github.com/drone/drone/core"
"github.com/drone/drone/handler/api/render"
"github.com/go-chi/chi"
)
// HandleFind returns an http.HandlerFunc that writes json-encoded
// template details to the the response body.
func HandleFind(templateStore core.TemplateStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var (
name = chi.URLParam(r, "name")
namespace = chi.URLParam(r, "namespace")
)
template, err := templateStore.FindName(r.Context(), name, namespace)
if err != nil {
render.NotFound(w, err)
return
}
render.JSON(w, template, 200)
}
}

View file

@ -0,0 +1,74 @@
// Copyright 2019 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by the Drone Non-Commercial License
// that can be found in the LICENSE file.
// +build !oss
package template
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/drone/drone/handler/api/errors"
"github.com/drone/drone/mock"
"github.com/go-chi/chi"
"github.com/golang/mock/gomock"
"github.com/google/go-cmp/cmp"
)
func TestHandleFind(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
template := mock.NewMockTemplateStore(controller)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(dummyTemplate, nil)
c := new(chi.Context)
c.URLParams.Add("name", "my_template")
c.URLParams.Add("namespace", "my_org")
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)
r = r.WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, c),
)
HandleFind(template).ServeHTTP(w, r)
if got, want := w.Code, http.StatusOK; want != got {
t.Errorf("Want response code %d, got %d", want, got)
}
}
func TestHandleFind_TemplateNotFound(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
template := mock.NewMockTemplateStore(controller)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(nil, errors.ErrNotFound)
c := new(chi.Context)
c.URLParams.Add("name", "my_template")
c.URLParams.Add("namespace", "my_org")
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)
r = r.WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, c),
)
HandleFind(template).ServeHTTP(w, r)
if got, want := w.Code, http.StatusNotFound; want != got {
t.Errorf("Want response code %d, got %d", want, got)
}
got, want := new(errors.Error), errors.ErrNotFound
json.NewDecoder(w.Body).Decode(got)
if diff := cmp.Diff(got, want); len(diff) != 0 {
t.Errorf(diff)
}
}

View file

@ -0,0 +1,30 @@
// Copyright 2019 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by the Drone Non-Commercial License
// that can be found in the LICENSE file.
// +build !oss
package template
import (
"net/http"
"github.com/drone/drone/core"
"github.com/drone/drone/handler/api/render"
"github.com/go-chi/chi"
)
// HandleList returns an http.HandlerFunc that writes a json-encoded
// list of templates to the response body by namespace
func HandleList(templateStore core.TemplateStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
namespace := chi.URLParam(r, "namespace")
list, err := templateStore.List(r.Context(), namespace)
if err != nil {
render.NotFound(w, err)
return
}
render.JSON(w, list, 200)
}
}

View file

@ -0,0 +1,70 @@
package template
// Copyright 2019 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by the Drone Non-Commercial License
// that can be found in the LICENSE file.
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/drone/drone/handler/api/errors"
"github.com/drone/drone/mock"
"github.com/go-chi/chi"
"github.com/golang/mock/gomock"
"github.com/google/go-cmp/cmp"
)
func TestHandleList(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
templates := mock.NewMockTemplateStore(controller)
templates.EXPECT().List(gomock.Any(), dummyTemplate.Namespace).Return(dummyTemplateList, nil)
c := new(chi.Context)
c.URLParams.Add("namespace", "my_org")
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)
r = r.WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, c),
)
HandleList(templates).ServeHTTP(w, r)
if got, want := w.Code, http.StatusOK; want != got {
t.Errorf("Want response code %d, got %d", want, got)
}
}
func TestHandleList_TemplateListErr(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
templates := mock.NewMockTemplateStore(controller)
templates.EXPECT().List(gomock.Any(), dummyTemplate.Namespace).Return(nil, errors.ErrNotFound)
c := new(chi.Context)
c.URLParams.Add("namespace", "my_org")
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)
r = r.WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, c),
)
HandleList(templates).ServeHTTP(w, r)
if got, want := w.Code, http.StatusNotFound; want != got {
t.Errorf("Want response code %d, got %d", want, got)
}
got, want := new(errors.Error), errors.ErrNotFound
json.NewDecoder(w.Body).Decode(got)
if diff := cmp.Diff(got, want); len(diff) != 0 {
t.Errorf(diff)
}
}

View file

@ -0,0 +1,56 @@
// Copyright 2019 Drone IO, Inc.
//
// 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.
// +build oss
package template
import (
"net/http"
"github.com/drone/drone/core"
"github.com/drone/drone/handler/api/render"
)
var notImplemented = func(w http.ResponseWriter, r *http.Request) {
render.NotImplemented(w, render.ErrNotImplemented)
}
func HandleCreate(store core.TemplateStore) http.HandlerFunc {
return notImplemented
}
func HandleUpdate(core.TemplateStore) http.HandlerFunc {
return notImplemented
}
func HandleDelete(core.TemplateStore) http.HandlerFunc {
return notImplemented
}
func HandleFind(core.TemplateStore) http.HandlerFunc {
return notImplemented
}
func HandleList(core.TemplateStore) http.HandlerFunc {
return notImplemented
}
func HandleListAll(core.TemplateStore) http.HandlerFunc {
return notImplemented
}
func HandleAll(core.TemplateStore) http.HandlerFunc {
return notImplemented
}

View file

@ -0,0 +1,67 @@
// Copyright 2019 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by the Drone Non-Commercial License
// that can be found in the LICENSE file.
// +build !oss
package template
import (
"encoding/json"
"net/http"
"github.com/drone/drone/core"
"github.com/drone/drone/handler/api/render"
"github.com/go-chi/chi"
)
type templateUpdate struct {
Data *string `json:"data"`
Namespace *string `json:"namespace"`
}
// HandleUpdate returns an http.HandlerFunc that processes http
// requests to update a template.
func HandleUpdate(templateStore core.TemplateStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var (
name = chi.URLParam(r, "name")
namespace = chi.URLParam(r, "namespace")
)
in := new(templateUpdate)
err := json.NewDecoder(r.Body).Decode(in)
if err != nil {
render.BadRequest(w, err)
return
}
s, err := templateStore.FindName(r.Context(), name, namespace)
if err != nil {
render.NotFound(w, err)
return
}
if in.Data != nil {
s.Data = *in.Data
}
if in.Namespace != nil {
s.Namespace = *in.Namespace
}
err = s.Validate()
if err != nil {
render.BadRequest(w, err)
return
}
err = templateStore.Update(r.Context(), s)
if err != nil {
render.InternalError(w, err)
return
}
render.JSON(w, s, 200)
}
}

View file

@ -0,0 +1,148 @@
// Copyright 2019 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by the Drone Non-Commercial License
// that can be found in the LICENSE file.
// +build !oss
package template
import (
"bytes"
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/drone/drone/core"
"github.com/drone/drone/handler/api/errors"
"github.com/drone/drone/mock"
"github.com/go-chi/chi"
"github.com/golang/mock/gomock"
"github.com/google/go-cmp/cmp"
)
func TestHandleUpdate(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
template := mock.NewMockTemplateStore(controller)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(dummyTemplate, nil)
template.EXPECT().Update(gomock.Any(), gomock.Any()).Return(nil)
c := new(chi.Context)
c.URLParams.Add("name", "my_template")
c.URLParams.Add("namespace", "my_org")
in := new(bytes.Buffer)
json.NewEncoder(in).Encode(dummyTemplate)
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", in)
r = r.WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, c),
)
HandleUpdate(template).ServeHTTP(w, r)
if got, want := w.Code, http.StatusOK; want != got {
t.Errorf("Want response code %d, got %d", want, got)
}
}
func TestHandleUpdate_ValidationErrorData(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
template := mock.NewMockTemplateStore(controller)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(&core.Template{Name: "my_template"}, nil)
c := new(chi.Context)
c.URLParams.Add("name", "my_template")
c.URLParams.Add("namespace", "my_org")
in := new(bytes.Buffer)
json.NewEncoder(in).Encode(&core.Secret{Data: ""})
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", in)
r = r.WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, c),
)
HandleUpdate(template).ServeHTTP(w, r)
if got, want := w.Code, http.StatusBadRequest; want != got {
t.Errorf("Want response code %d, got %d", want, got)
}
got, want := new(errors.Error), &errors.Error{Message: "No Template Data Provided"}
json.NewDecoder(w.Body).Decode(got)
if diff := cmp.Diff(got, want); len(diff) != 0 {
t.Errorf(diff)
}
}
func TestHandleUpdate_TemplateNotFound(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
template := mock.NewMockTemplateStore(controller)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(nil, errors.ErrNotFound)
c := new(chi.Context)
c.URLParams.Add("name", "my_template")
c.URLParams.Add("namespace", "my_org")
in := new(bytes.Buffer)
json.NewEncoder(in).Encode(&core.Secret{})
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", in)
r = r.WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, c),
)
HandleUpdate(template).ServeHTTP(w, r)
if got, want := w.Code, http.StatusNotFound; want != got {
t.Errorf("Want response code %d, got %d", want, got)
}
got, want := new(errors.Error), errors.ErrNotFound
json.NewDecoder(w.Body).Decode(got)
if diff := cmp.Diff(got, want); len(diff) != 0 {
t.Errorf(diff)
}
}
func TestHandleUpdate_UpdateError(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
template := mock.NewMockTemplateStore(controller)
template.EXPECT().FindName(gomock.Any(), dummyTemplate.Name, dummyTemplate.Namespace).Return(&core.Template{Name: "my_template"}, nil)
template.EXPECT().Update(gomock.Any(), gomock.Any()).Return(errors.ErrNotFound)
c := new(chi.Context)
c.URLParams.Add("name", "my_template")
c.URLParams.Add("namespace", "my_org")
in := new(bytes.Buffer)
json.NewEncoder(in).Encode(&core.Template{Data: "my_data"})
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", in)
r = r.WithContext(
context.WithValue(context.Background(), chi.RouteCtxKey, c),
)
HandleUpdate(template).ServeHTTP(w, r)
if got, want := w.Code, http.StatusInternalServerError; want != got {
t.Errorf("Want response code %d, got %d", want, got)
}
got, want := new(errors.Error), errors.ErrNotFound
json.NewDecoder(w.Body).Decode(got)
if diff := cmp.Diff(got, want); len(diff) != 0 {
t.Errorf(diff)
}
}

View file

@ -25,7 +25,7 @@ func init() {
logrus.SetOutput(ioutil.Discard)
}
func TestResitoryList(t *testing.T) {
func TestRepositoryList(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
@ -63,7 +63,7 @@ func TestResitoryList(t *testing.T) {
}
}
func TestResitoryListErr(t *testing.T) {
func TestRepositoryListErr(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()

View file

@ -30,7 +30,7 @@ func HandleSync(syncer core.Syncer, repos core.RepositoryStore) http.HandlerFunc
return func(w http.ResponseWriter, r *http.Request) {
viewer, _ := request.UserFrom(r.Context())
// performs asyncrhonous account synchronization.
// performs asynchronous account synchronization.
// this requires long polling to determine when the
// sync is complete.
if r.FormValue("async") == "true" {
@ -57,7 +57,7 @@ func HandleSync(syncer core.Syncer, repos core.RepositoryStore) http.HandlerFunc
if err != nil {
render.InternalError(w, err)
logger.FromRequest(r).WithError(err).
Warnln("api: cannot synchrnoize account")
Warnln("api: cannot synchronize account")
} else {
render.JSON(w, list, 200)
}

View file

@ -77,7 +77,7 @@ func HandleCreate(users core.UserStore, service core.UserService, sender core.We
if err != nil {
render.ErrorCode(w, err, 400)
logger.FromRequest(r).WithError(err).
Errorln("api: invlid username")
Errorln("api: invalid username")
return
}

View file

@ -30,7 +30,7 @@ func TestCreate(t *testing.T) {
t.Errorf("Want user login %s, got %s", want, got)
}
if in.Hash == "" {
t.Errorf("Expect user secert generated")
t.Errorf("Expect user secret generated")
}
return nil
})

View file

@ -35,7 +35,7 @@ func HandleFind(users core.UserStore) http.HandlerFunc {
if err != nil {
// the client can make a user request by providing
// the user id as opposed to the username. If a
// numberic user id is provided as input, attempt
// numeric user id is provided as input, attempt
// to lookup the user by id.
if id, _ := strconv.ParseInt(login, 10, 64); id != 0 {
user, err = users.Find(r.Context(), id)

File diff suppressed because it is too large Load diff

View file

@ -1,283 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Drone</title>
<link href="https://fonts.googleapis.com/css?family=Inconsolata|PT+Mono|Roboto+Mono|Source+Code+Pro|Ubuntu+Mono" rel="stylesheet">
<link href="/static2/style.css" rel="stylesheet" />
</head>
<body>
<div>
<nav>
<div class="center">
<svg class="logo" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 326.6 103.1" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<g>
<g>
<desc>Created with Sketch.</desc>
<g id="Landing_4_">
<g id="logos_4_" transform="translate(-151.000000, -3990.000000)">
<g id="drone-logo-back-with-text_4_" transform="translate(151.000000, 3986.000000)">
<g id="Group-4_4_">
<path id="DRONE_4_" class="st0" d="M109.6,28.6H126c5.8,0,10.3,1.5,13.3,4.4c3,2.9,4.5,7.7,4.5,14.1c0,6.2-1.5,10.7-4.5,13.7
c-3,2.9-7.4,4.4-13.3,4.4h-16.4V28.6z M124.6,57c1.9,0,3.4-0.3,4.6-0.8c1.2-0.5,2.1-1.5,2.7-3c0.6-1.4,0.9-3.5,0.9-6.1
c0-2.7-0.3-4.8-0.9-6.3s-1.5-2.6-2.6-3.2s-2.8-0.9-4.7-0.9h-4.3V57H124.6z M154.1,28.6h19.1c4.3,0,7.6,1.2,9.8,3.5
s3.3,5.5,3.3,9.5c0,2.4-0.6,4.6-1.8,6.5c-1.2,1.9-2.9,3.3-4.9,4.4c0.4,0.3,0.8,0.7,1.1,1.2c0.3,0.5,0.6,1.1,1,2l4.2,9.6
h-11.1l-3.9-8.8c-0.3-0.7-0.7-1.2-1.1-1.5c-0.4-0.3-1.1-0.4-1.8-0.4h-3v10.7h-10.7V28.6z M170.4,46.3c1.6,0,2.8-0.4,3.6-1.2
c0.9-0.8,1.3-2,1.3-3.5c0-3.2-1.5-4.8-4.6-4.8h-6v9.6H170.4z M213.3,65.8c-6.2,0-10.8-1.5-14-4.5c-3.1-3-4.7-7.8-4.7-14.2
c0-6.7,1.6-11.5,4.7-14.5s7.8-4.5,14-4.5c6.2,0,10.9,1.5,14,4.5c3.1,3,4.7,7.8,4.7,14.5c0,6.4-1.6,11.2-4.7,14.2
C224.1,64.3,219.5,65.8,213.3,65.8z M213.3,56.7c2.7,0,4.7-0.8,5.9-2.3s1.8-4,1.8-7.4c0-3.6-0.6-6.1-1.8-7.6
c-1.2-1.5-3.2-2.3-6-2.3s-4.8,0.8-6,2.3c-1.2,1.5-1.8,4.1-1.8,7.6c0,3.4,0.6,5.8,1.8,7.4S210.6,56.7,213.3,56.7z M242.3,28.6
h9.1L265.2,48V28.6h10.7v36.6h-9.2L253,45.9v19.3h-10.7V28.6z M287.4,28.6h27.8v8.2h-17.1v6h15v8.3h-15v6h17.1v8.2h-27.8
V28.6z"/>
<g id="Group-3_4_" transform="translate(0.000000, 4.000000)">
<path id="Combined-Shape_4_" class="st0" d="M16.7,9.3l-0.4,0.4L30.8,24c-2,3.2-3.2,7.1-3.2,11.5c0,12.3,9.1,20.5,20.7,20.5
c4.3,0,8.3-1.1,11.6-3.2l14.6,14.5c-7.5,9.9-19.5,16.1-33,16.1C18.5,83.4,0,65.6,0,42.4C0,28.5,6.5,16.6,16.7,9.3z
M24.8,3.9C30.2,1.4,36.2,0,42.5,0c22.7,0,41.2,17.8,41.2,41c0,6.4-1.4,12.4-3.9,17.7l-13-13c2-3.2,3.2-7.1,3.2-11.5
c0-12.3-9.1-20.5-20.6-20.5c-4.3,0-8.3,1.1-11.5,3.2L24.8,3.9z M49.6,46.3c-6.9,0-12.4-4.9-12.4-12.4s5.5-12.4,12.4-12.4
S62,26.6,62,34S56.5,46.3,49.6,46.3z"/>
</g>
</g>
</g>
</g>
</g>
</g>
<g>
<path class="st0" d="M167.1,92.8c-1.2,1.1-2.8,1.7-4.9,1.7c-2.5,0-5.1-1.4-6.1-3.7l-0.2,3.3h-1.6V67.3h1.9v11.3
c1-2.1,3.6-3.6,6.2-3.6c2.1,0,3.8,0.7,5,1.9c2,1.9,2.9,4.8,2.9,7.8C170.1,88,169.1,90.9,167.1,92.8z M166.1,78.5
c-1-1-2.2-1.6-4-1.6c-1.7,0-3,0.6-3.9,1.4c-1.7,1.6-2.3,3.7-2.3,6.5c0,2.6,0.7,4.8,2.4,6.4c0.9,0.9,2.1,1.5,3.8,1.5
c1.6,0,2.7-0.5,3.7-1.3c1.7-1.5,2.4-4,2.4-6.6C168.2,82.4,167.6,80,166.1,78.5z"/>
<path class="st0" d="M178.2,98c-0.9,2.3-2.4,3.8-5,3.8c-0.7,0-1.7-0.1-2.3-0.3v-1.6c0.7,0.1,1.5,0.2,2.3,0.2
c1.8,0,2.8-1.2,3.4-2.7l1.2-3l-7.6-18.8h2l6.5,16.4l6.2-16.4h2L178.2,98z"/>
<path class="st0" d="M205.9,94V82.1c0-2.2-0.7-3.6-3.2-3.6c-2.8,0-4,3-4,5.3V94h-4.6V67.3h4.6v9.5c1.1-1.6,3.3-2.5,5.3-2.5
c4.1,0,6.5,2.9,6.5,6.9V94H205.9z"/>
<path class="st0" d="M228.2,94.3c-2,0-3.1-1.3-3.3-2.7c-0.9,1.6-3.1,2.9-5.8,2.9c-4.6,0-7-2.9-7-6c0-3.7,2.8-5.8,6.5-6l5.7-0.4
v-1.2c0-1.8-0.6-3-3.3-3c-2.1,0-3.4,1-3.5,2.7h-4.5c0.3-4.3,3.7-6.3,8-6.3c3.3,0,6.1,1,7.2,3.9c0.5,1.2,0.6,2.6,0.6,3.9v7.1
c0,0.9,0.3,1.2,1.1,1.2c0.3,0,0.6-0.1,0.6-0.1v3.4C229.7,94.1,229.3,94.3,228.2,94.3z M224.3,85.5l-4.9,0.4
c-1.4,0.1-2.8,1-2.8,2.5c0,1.5,1.3,2.4,2.7,2.4c2.9,0,4.9-1.6,4.9-4.4V85.5z"/>
<path class="st0" d="M241.9,78.9c-3.3,0-5,2.3-5,5.4V94h-4.7V74.8h3.9l0.4,2.9c0.9-2.2,3.2-3.1,5.4-3.1c0.5,0,1,0,1.4,0.1v4.4
C242.9,79,242.4,78.9,241.9,78.9z"/>
<path class="st0" d="M256.3,94V82.2c0-2.3-0.7-3.6-3.2-3.6c-1.6,0-2.7,1-3.3,2.4c-0.7,1.5-0.6,3.5-0.6,5.1V94h-4.6V74.8h3.9
l0.3,2.5c1.1-2,3.4-3,5.7-3c4.1,0,6.5,2.9,6.5,6.9V94H256.3z"/>
<path class="st0" d="M280.3,85.8h-12.9c0,1.5,0.5,2.9,1.4,3.8c0.7,0.6,1.6,1.2,2.8,1.2c1.3,0,2.1-0.3,2.8-0.9
c0.4-0.4,0.7-0.9,0.9-1.6h4.4c-0.1,1.1-0.9,2.7-1.6,3.6c-1.6,1.9-4,2.7-6.5,2.7c-2.6,0-4.6-0.9-6.1-2.4c-1.9-1.8-3-4.6-3-7.7
c0-3.1,0.9-5.8,2.8-7.7c1.4-1.5,3.5-2.4,6.1-2.4c2.8,0,5.4,1.1,7,3.6c1.4,2.1,1.7,4.3,1.7,6.7C280.4,84.5,280.3,85.4,280.3,85.8z
M274.6,79.1c-0.6-0.7-1.6-1.1-2.9-1.1c-1.4,0-2.6,0.6-3.2,1.5c-0.7,0.9-1,1.7-1,3h8.3C275.7,81,275.3,79.9,274.6,79.1z"/>
<path class="st0" d="M295.8,92.3c-1.6,1.6-4,2.2-6.6,2.2c-2.4,0-4.6-0.7-6.2-2.2c-1.1-1.1-2-2.7-2-4.5h4.3c0,0.9,0.4,1.8,1,2.3
c0.7,0.6,1.6,0.9,2.9,0.9c1.6,0,3.9-0.3,3.9-2.4c0-1.1-0.7-1.8-1.9-2c-1.7-0.3-3.6-0.4-5.3-0.7c-2.6-0.6-4.4-2.7-4.4-5.1
c0-2,0.8-3.3,2-4.3c1.4-1.2,3.4-1.9,5.8-1.9c2.3,0,4.6,0.7,6,2.3c1,1.1,1.7,2.6,1.7,4h-4.4c0-0.7-0.3-1.3-0.8-1.8
c-0.6-0.6-1.6-0.9-2.5-0.9c-0.7,0-1.3,0-2,0.3c-0.8,0.3-1.5,1-1.5,2c0,1.4,1.2,1.8,2.2,1.9c1.7,0.2,2.2,0.3,4.1,0.6
c3,0.5,5.3,2.4,5.3,5.4C297.4,89.9,296.8,91.3,295.8,92.3z"/>
<path class="st0" d="M313.1,92.3c-1.6,1.6-4,2.2-6.6,2.2c-2.4,0-4.6-0.7-6.2-2.2c-1.1-1.1-2-2.7-2-4.5h4.3c0,0.9,0.4,1.8,1,2.3
c0.7,0.6,1.6,0.9,2.9,0.9c1.6,0,3.9-0.3,3.9-2.4c0-1.1-0.7-1.8-1.9-2c-1.7-0.3-3.6-0.4-5.3-0.7c-2.6-0.6-4.4-2.7-4.4-5.1
c0-2,0.8-3.3,2-4.3c1.4-1.2,3.4-1.9,5.8-1.9c2.3,0,4.6,0.7,6,2.3c1,1.1,1.7,2.6,1.7,4h-4.4c0-0.7-0.3-1.3-0.8-1.8
c-0.6-0.6-1.6-0.9-2.5-0.9c-0.7,0-1.3,0-2,0.3c-0.8,0.3-1.5,1-1.5,2c0,1.4,1.2,1.8,2.2,1.9c1.7,0.2,2.2,0.3,4.1,0.6
c3,0.5,5.3,2.4,5.3,5.4C314.8,89.9,314.1,91.3,313.1,92.3z"/>
</g>
</g>
<g>
<path class="st0" d="M321.2,25.2v3.5h-0.5v-3.5h-1.2v-0.5h2.9v0.5H321.2z"/>
<path class="st0" d="M326.1,28.7v-3.4l-1.2,3.3h-0.4l-1.2-3.3v3.4h-0.5v-4h0.7l1.2,3.3l1.2-3.3h0.7v4H326.1z"/>
</g>
</g>
</svg>
<ul>
<li>
<a href="https://cloud.drone.io">Cloud</a>
</li>
<li>
<a href="https://drone.io/enterprise">Enterprise</a>
</li>
<li>
<a href="https://docs.drone.io">Docs</a>
</li>
<li>
<a href="https://plugins.drone.io">Plugins</a>
</li>
<li>
<a href="https://discourse.drone.io">Support</a>
</li>
<li>
<a href="/login" class="login">Sign In</a>
</li>
</ul>
</div>
</nav>
<header>
<div class="center">
<div class="illustration"></div>
<h1>Continuous Integration,<br/>Free for the Open Source Community</h1>
<h2>Drone Cloud is a free Continuous Integration service for the Open Source community,
powered by blazing fast bare-metal servers.
</h2>
<a href="/login" class="button">Get Started</a>
<a href="https://docs.drone.io" class="button button-outline">Read the Docs</a>
</div>
</header>
<section style="padding-bottom: 0px;">
<div class="center header">
<h2>Accelerating Open Source Development</h2>
</div>
</section>
<section class="columns-3 cards">
<div class="center">
<div>
<h3>Multiple Architectures</h3>
<p>Our goal is to upstream all the things! In order to do that with the diverse Arm ecosystem, we're providing gobs of CI/CD infrastructure.</p>
</div>
<div>
<h3>Blazing Fast, Bare Metal Servers</h3>
<p>Drone Cloud runs your Continuous Integration workloads on blazing fast, bare metal infrastructure sponsored by Equinix.</p>
</div>
<div>
<h3>100% free for Open Source</h3>
<p>Drone Cloud would not be possible without our generous sponsors. If you are interested in becoming a sponsor please <a href="mailto:hello@drone.io">contact us.</a></p>
</div>
</div>
</section>
<section class="columns-2 thanks-packet">
<div class="center">
<div>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 103.4 94.2" xml:space="preserve">
<rect y="65.5" width="103.4" height="0.5"></rect>
<g>
<path d="M20.2,82.9h2.2l3.7,5.4l3.6-5.4H32v11.4h-2.1v-8.1l-3.8,5.4h-0.1l-3.7-5.3v8.1h-2.1V82.9z"></path>
<path d="M36.2,82.9H45v1.8h-6.7v3h6v1.8h-6v3h6.8v1.8h-8.9L36.2,82.9z"></path>
<path d="M52.2,84.7h-3.8v-1.9h9.7v1.9h-3.8v9.5h-2.1V84.7z"></path>
<path d="M64.1,82.8h2l5.2,11.4h-2.2l-1.2-2.7h-5.6L61,94.2h-2.1L64.1,82.8z M67.1,89.7L65,85.2l-2,4.5H67.1z"></path>
<path d="M74.8,82.9h2.1v9.5h6.3v1.8h-8.4L74.8,82.9z"></path>
</g>
<g>
<polygon points="0,38.6 0,49.2 9.4,49.2 9.4,47.8 1.5,47.8 1.5,44.4 6.6,44.4 6.6,43 1.5,43 1.5,40 9.1,40 9.1,38.6 "></polygon>
<path d="M41.8,45c0,1.9-1.2,3.1-3.1,3.1c-1.8,0-2.9-1.3-2.9-3.1v-6.3h-1.5V45c0,2.9,2.1,4.4,4.4,4.4c3.5,0,4.6-2.4,4.6-4.4v-6.4
h-1.5V45z"></path>
<rect x="51.8" y="38.6" width="1.5" height="10.5"></rect>
<polygon points="71.4,38.6 71.4,46.9 63.9,38.6 62.4,38.6 62.4,49.2 63.9,49.2 63.9,40.8 71.4,49.2 72.9,49.2 72.9,38.6 "></polygon>
<rect x="82" y="38.6" width="1.5" height="10.5"></rect>
<polygon points="100.8,38.6 97.3,42.4 93.9,38.6 91.9,38.6 96.2,43.5 90.9,49.2 92.8,49.2 97.2,44.5 101.3,49.2 103.4,49.2
98.3,43.5 102.7,38.6 "></polygon>
<path d="M20.9,38.2c-3.1,0-5.6,2.4-5.6,5.7v0c0,3.3,2.5,5.6,5.6,5.6c0.8,0,1.7-0.2,2.5-0.6l0.9,1l1.8,0l-1.5-1.7
c1.2-0.9,2-2.5,2-4.4v0C26.5,40.5,24.1,38.2,20.9,38.2 M25,43.9c0,1.5-0.5,2.5-1.4,3.3l-0.8-0.9l-1.8,0l1.4,1.7
c-0.4,0.2-1,0.3-1.5,0.3c-2.3,0-4.1-1.8-4.1-4.3l0,0c0-2.5,1.7-4.3,4-4.3C23.2,39.5,25,41.3,25,43.9L25,43.9z"></path>
</g>
<g>
<polygon points="52.5,0 48.2,1.5 48.2,24 45.4,23 45.4,2.5 36.8,5.5 36.8,20 33.9,19 33.9,6.5 31,7.5 31,21.1 39.6,24.1 39.6,7.5
42.5,6.5 42.5,25.1 51.1,28.1 51.1,3.5 52.5,3 54,3.5 54,28.1 62.5,25.1 62.5,6.5 65.4,7.5 65.4,24.1 74,21.1 74,7.5 71.1,6.5
71.1,19 68.3,20 68.3,5.5 59.7,2.5 59.7,23 56.8,24 56.8,1.5 "></polygon>
<path d="M79.5,20.1c1.4,0,2.6-1.2,2.6-2.6v0c0-1.4-1.1-2.6-2.6-2.6c-1.4,0-2.6,1.2-2.6,2.6v0C76.9,19,78,20.1,79.5,20.1 M79.5,19.8
c-1.3,0-2.3-1-2.3-2.3v0c0-1.3,1-2.3,2.3-2.3c1.3,0,2.3,1,2.3,2.3v0C81.8,18.8,80.8,19.8,79.5,19.8 M78.4,18.8h0.7V18h0.4l0.6,0.8
h0.8l-0.7-1c0.4-0.1,0.6-0.4,0.6-0.8c0-0.6-0.5-0.9-1.1-0.9h-1.3V18.8z M79.1,17.4v-0.7h0.6c0.3,0,0.5,0.1,0.5,0.4
c0,0.2-0.2,0.4-0.5,0.4H79.1z"></path>
</g>
</svg>
</div>
<div>
<h3>Thank you to our Infrastructure Sponsor</h3>
<p>Drone Cloud is powered by donated infrastructure from Equinix Metal (<a href="https://metal.equinix.com/">metal.equinix.com</a>), the leading bare metal cloud for developers. With datacenters around the world, and a powerful API driven experience, Equinix is well known for bringing the experience of the cloud to bare metal.</p>
<p>Want to run Drone on bare metal, but without the hassle? Equinix can help! <em>Use code "DRONE100" to get started with a $100 credit</em></p>
<div><a class="button" href="https://metal.equinix.com/">Learn more about Equinix Metal</a></div>
</div>
</div>
</section>
<section class="try-drone-panel">
<div class="center">
<div>
<h3>Interested in running Drone on your own, private infrastructure?</h3>
<div><a href="https://docs.drone.io/installation/">Download official Docker image</a></div>
<div><small>and install Drone in minutes</small></div>
</div>
</div>
</section>
<footer>
<div>
<div class="logo"></div>
<section>
<h3>Company</h3>
<ul>
<li>
<a href="mailto:hello@drone.io" target="_blank">Contact</a>
</li>
<li>
<a href="https://github.com/drone/brand">Branding</a>
</li>
<li>
<a href="https://drone.io/enterprise/license">License</a>
</li>
</ul>
</section>
<section>
<h3>Products</h3>
<ul>
<li>
<a href="https://drone.io/enterprise">Drone Enterprise</a>
</li>
<li>
<a href="https://cloud.drone.io">Drone Cloud</a>
</li>
<li>
<a href="https://autoscale.drone.io" target="_blank">Drone Autoscaler</a>
</li>
</ul>
</section>
<section>
<h3>Developers</h3>
<ul>
<li>
<a href="https://docs.drone.io" target="_blank">Documentation</a>
</li>
<li>
<a href="http://plugins.drone.io" target="_blank">Plugins</a>
</li>
<li>
<a href="https://discourse.drone.io" target="_blank">Support</a>
</li>
</ul>
</section>
<section>
<h3>Community</h3>
<ul>
<li>
<a href="https://github.com/drone" target="_blank">
<svg aria-labelledby="simpleicons-github-icon" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title id="simpleicons-github-icon">GitHub icon</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"></path></svg>
GitHub
</a>
</li>
<li>
<a href="https://twitter.com/droneio" target="_blank">
<svg aria-labelledby="simpleicons-twitter-icon" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title id="simpleicons-twitter-icon">Twitter icon</title><path d="M23.954 4.569c-.885.389-1.83.654-2.825.775 1.014-.611 1.794-1.574 2.163-2.723-.951.555-2.005.959-3.127 1.184-.896-.959-2.173-1.559-3.591-1.559-2.717 0-4.92 2.203-4.92 4.917 0 .39.045.765.127 1.124C7.691 8.094 4.066 6.13 1.64 3.161c-.427.722-.666 1.561-.666 2.475 0 1.71.87 3.213 2.188 4.096-.807-.026-1.566-.248-2.228-.616v.061c0 2.385 1.693 4.374 3.946 4.827-.413.111-.849.171-1.296.171-.314 0-.615-.03-.916-.086.631 1.953 2.445 3.377 4.604 3.417-1.68 1.319-3.809 2.105-6.102 2.105-.39 0-.779-.023-1.17-.067 2.189 1.394 4.768 2.209 7.557 2.209 9.054 0 13.999-7.496 13.999-13.986 0-.209 0-.42-.015-.63.961-.689 1.8-1.56 2.46-2.548l-.047-.02z"></path></svg>
Twitter
</a>
</li>
<li>
<a href="https://discourse.drone.io/" target="_blank">
<svg aria-labelledby="simpleicons-discourse-icon" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title id="simpleicons-discourse-icon">Discourse icon</title><path d="M12.103 0C18.666 0 24 5.485 24 11.997c0 6.51-5.33 11.99-11.9 11.99L0 24V11.79C0 5.28 5.532 0 12.103 0zm.116 4.563c-2.593-.003-4.996 1.352-6.337 3.57-1.33 2.208-1.387 4.957-.148 7.22L4.4 19.61l4.794-1.074c2.745 1.225 5.965.676 8.136-1.39 2.17-2.054 2.86-5.228 1.737-7.997-1.135-2.778-3.84-4.59-6.84-4.585h-.008z"></path></svg>
Discourse
</a>
</li>
<li>
<a href="https://gitter.im/drone/drone/" target="_blank">
<svg aria-labelledby="simpleicons-slack-icon" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title id="simpleicons-slack-icon">Slack icon</title><path d="M9.879 10.995l1.035 3.085 3.205-1.074-1.035-3.074-3.205 1.08v-.017z"></path><path d="M18.824 14.055l-1.555.521.54 1.61c.218.65-.135 1.355-.786 1.574-.15.045-.285.067-.435.063-.511-.015-.976-.338-1.155-.849l-.54-1.607-3.21 1.073.539 1.608c.211.652-.135 1.358-.794 1.575-.15.048-.285.067-.435.064-.51-.015-.976-.34-1.156-.85l-.539-1.619-1.561.524c-.15.045-.285.061-.435.061-.511-.016-.976-.345-1.155-.855-.225-.66.135-1.364.78-1.575l1.56-.525L7.5 11.76l-1.551.525c-.141.048-.285.066-.428.064-.495-.016-.975-.338-1.141-.848-.209-.65.135-1.354.796-1.574l1.56-.52-.54-1.605c-.21-.654.136-1.359.796-1.575.659-.22 1.363.136 1.574.783l.539 1.608L12.3 7.544l-.54-1.605c-.209-.645.135-1.35.789-1.574.652-.211 1.359.135 1.575.791l.54 1.621 1.555-.51c.651-.219 1.356.135 1.575.779.218.654-.135 1.359-.784 1.575l-1.557.524 1.035 3.086 1.551-.516c.652-.211 1.358.135 1.575.795.22.66-.135 1.365-.779 1.574l-.011-.029zm4.171-5.356C20.52.456 16.946-1.471 8.699 1.005.456 3.479-1.471 7.051 1.005 15.301c2.475 8.245 6.046 10.17 14.296 7.694 8.245-2.475 10.17-6.046 7.694-14.296z"></path></svg>
Chat
</a>
</li>
</ul>
</section>
</div>
</footer>
</div>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 KiB

View file

@ -1,615 +0,0 @@
html, body {
margin: 0px;
padding: 0px;
font-family: -apple-system, system-ui, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}
section {
padding: 60px;
}
footer {
padding: 60px 30px;
}
.center {
max-width:900px;
margin:0px auto;
}
/*
* Code
*/
code {
text-align:left;
background: #222;
color: #FFF;
margin: 0px auto;
font-family: 'Source Code Pro';
font-size: 13px;
line-height: 18px;
border-radius: 5px;
padding: 30px 0px;
}
code div {
white-space: pre;
padding: 0px 30px;
}
code span.group {
background: #333;
display: block;
padding: 5px 0px;
}
code span.c0 {
color: #00E5FF;
}
code span.c1 {
color: #FF8A80;
}
.code .center {
text-align: center;
}
.code p {
max-width:500px;
text-align:center;
margin:30px auto;
}
.code .grid {
display: grid;
grid-template-columns: 225px 450px 225px;
grid-template-rows: auto auto auto;
}
.code .grid code {
grid-column: 2;
grid-row-start: 1;
grid-row-end: 4;
margin: 0px;
}
.info {
background: #EEE;
margin-right: 20px;
border-radius: 5px;
padding: 15px;
text-align: left;
font-size: 14px;
}
.info:first-of-type {
grid-column: 1;
grid-row: 1;
margin-top: 15px;
}
.info:nth-of-type(2) {
grid-column: 3;
grid-row: 2;
margin-right: 0px;
margin-left: 20px;
}
.info:last-of-type {
grid-column: 1;
grid-row: 3;
margin-bottom: 15px;
}
/*
* Plugins
*/
.code.plugins .grid {
margin-top: 80px;
display: grid;
grid-template-columns: 112.5px 112.5px 112.5px 112.5px 450px;
grid-template-rows: auto auto auto auto;
}
.code.plugins .grid code {
grid-column: 5;
grid-row-start: 1;
grid-row-end: 5;
margin: 0px;
}
.code.plugins .logo {
margin-right: 20px;
padding: 15px;
border-radius: 50%;
}
/*
*
*/
nav{
text-align: right;
height: 60px;
background:#293a41;
}
nav .center {
display: flex;
align-items: center;
height: 60px;
}
nav ul {
margin: 0px;
padding: 0px;
flex: 1;
}
nav li {
display: inline;
}
nav li a {
font-size: 15px;
color: #FFF;
text-decoration: none;
margin-left: 15px;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
}
nav .login {
background:#00bfa6;
color: #FFF;
font-size: 13px;
text-transform: uppercase;
padding: 10px 20px;
border-radius: 3px;
-webkit-font-smoothing: unset;
-moz-font-smoothing: unset;
}
nav svg {
fill: #FFF;
height: 40px;
}
/*
* Header Section
*/
header {
background-color: #293a41;
color: #FFF;
padding: 50px 20px;
padding-bottom: 80px;
}
header .illustration {
background-image: url("/static2/city-cloud.png");
background-repeat: no-repeat;
background-size: contain;
float:right;
height: 250px;
padding: 20px;
width: 400px;
}
header h1,
header h2 {
max-width: 500px;
text-align: left;
font-size: 18px;
font-weight: normal;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
}
header h1 {
font-size: 28px;
line-height: 32px;
}
header h2 {
margin-bottom: 50px;
line-height: 20px;
font-weight: normal;
font-size: 16px;
max-width: 400px;
-webkit-font-smoothing: antialiased;
color: rgba(255,255,255,0.8);
}
header a {
color: #FFF;
}
header a.button {
background: #00bfa6;
color: #FFF;
text-decoration: none;
border-radius: 3px;
border: 1px solid #00bfa6;
padding: 10px 20px;
display: inline-block;
margin-bottom: 10px;
transition: all .15s ease;
text-transform: uppercase;
font-size: 13px;
margin-right: 10px;
}
header a.button.button-outline {
color: #00bfa6;
background: none;
border: 1px solid #00bfa6;
}
header a.button:hover {
transform: translateY(-1px);
}
/*
* Codeblock
*/
div.logos {
display: block;
border-top: none;
margin-top: 0px;
text-align: left;
white-space: unset;
overflow: unset;
}
.logos .logo {
display: inline-block;
width: 32px;
height: 32px;
border-radius: 50%;
margin: 10px;
padding: 10px;
}
.logos .logo img {
width: 32px;
height: 32px;
margin: 0px;
}
/*
* VCS Section
*/
.vcs .center {
text-align: center;
padding: 60px 0px;
}
.vcs strong {
font-size: 1.17em;
font-weight: bold;
text-align: center;
}
/*
* Logo Section
*/
.logos {
border-top: 1px solid #EEE;
text-align: center;
white-space: nowrap;
overflow: hidden;
margin-top: 100px;
}
.logos img {
margin: 0px 30px;
}
/*
* Quote Sections
*/
.quote .center {
display: grid;
grid-template-columns: 110px auto;
}
.quote blockquote {
margin: 0px;
padding: 15px;
font-size: 18px;
line-height: 26px;
grid-column: 2;
}
.quote img {
grid-column: 1;
padding-top: 5px;
max-width: 75px;
}
.quote cite {
grid-column: 2;
padding-top: 15px;
padding-left: 15px;
display: block;
}
/*
* 3-columns section
*/
.columns-2 .center {
display: grid;
grid-template-columns: auto 400px;
grid-gap: 30px;
}
.columns-3 .center {
display: grid;
grid-template-columns: auto auto auto;
grid-gap: 30px;
}
.placeholder {
border: 1px solid #CCC;
height: 100%;
}
/*
* Cards
*/
.center.header h2{
font-size: 26px;
font-weight: normal;
text-align: center;
}
.cards .center div {
font-size: 14px;
border: 1px solid #EEE;
border-radius: 5px;
padding: 15px 30px;
box-shadow: 0 5px 15px rgba(50,50,93,.05), 0 5px 5px rgba(0,0,0,.02);
}
.cards div h3 {
font-size: 15px;
line-height: 20px;
}
/*
* Try Drone Today
*/
.try-drone-panel {
background:#293a41;
color: #FFF;
}
.try-drone-panel h3 {
font-weight: normal;
}
.try-drone-panel a {
color: #FFF;
text-decoration: none;
border-radius: 3px;
background: #00bfa6;
padding: 10px 20px;
display: inline-block;
margin-bottom: 10px;
transition: all .15s ease;
text-transform: uppercase;
font-size: 14px;
}
.try-drone-panel a:hover {
transform: translateY(-1px);
}
.try-drone-panel small {
font-style: italic;
color: rgba(255,255,255,0.75);
}
/*
* Thanks to Packet
*/
.thanks-packet {
background-color: #eff3f5;
padding: 0px;
margin: 0px;
padding-top: 40px;
}
.thanks-packet .center > div:first-of-type {
height: 400px;
}
.thanks-packet svg {
max-width: 300px;
margin-top: 45px;
}
.thanks-packet .center > div:last-of-type {
margin-top: 40px;
}
.thanks-packet p {
font-size: 13px;
line-height: 20px;
}
.thanks-packet em {
font-weight: 600;
}
.thanks-packet a:visited,
.thanks-packet a {
color: #0564d7;
}
.thanks-packet a.button {
color: #00bfa6;
text-decoration: none;
border-radius: 3px;
border: 1px solid #00bfa6;
padding: 10px 20px;
display: inline-block;
margin-bottom: 10px;
transition: all .15s ease;
text-transform: uppercase;
font-size: 13px;
margin-top: 10px;
}
.thanks-packet a.button:hover {
transform: translateY(-1px);
}
footer {
padding: 0px;
}
footer > div {
max-width: 900px;
margin: 0px auto;
display: flex;
}
footer section {
margin-left: unset;
}
footer h3 {
text-transform: uppercase;
font-size: 13px;
color: #455A64;
}
footer ul {
margin: 0px;
padding: 0px;
margin-top: 40px;
list-style: none;
}
footer ul li {
}
footer a {
display: flex;
align-content: center;
font-size: 14px;
margin: 10px 0px;
color: #455A64;
text-decoration: none;
}
footer svg {
fill: #455A64;
width: 14px;
height: 14px;
margin-right: 10px;
}
footer .logo {
flex: 1;
}
@media (max-width: 920px) {
header {
padding: 30px 30px;
}
header .illustration {
display: none;
}
nav {
padding: 0px 30px;
}
}
@media (max-width: 720px) {
footer > div {
display: flex;
flex-direction: column;
}
footer .logo {
flex: 1;
}
footer section {
margin-left: 0px;
margin-bottom: 0px;
margin-top: 0px;
padding-top: 0px;
padding-bottom: 0px;
}
footer {
padding-top: 30px;
padding-bottom: 30px;
}
footer h3 {
margin-top: 30px;
}
footer ul {
margin-left: 30px;
margin-top: 0px;
}
nav a {
display: none;
}
nav a.login {
display: inline-block;
}
.thanks-packet.columns-2 {
padding-top: 0px;
margin-top: 0px;
}
.thanks-packet.columns-2 > .center {
grid-template-columns: none;
display: grid;
grid-template-rows: auto 400px;
}
.thanks-packet .center > div:first-of-type {
grid-row: 2;
}
.thanks-packet .center > div:last-of-type {
grid-row: 1;
padding: 0px 30px;
padding-bottom:30px;
}
.thanks-packet .center > div:last-of-type > div {
text-align: center;
padding-top:20px;
}
.thanks-packet svg {
display: none;
}
.columns-3.cards .center {
display: grid;
grid-template-rows: auto auto auto;
grid-template-columns: none !important;
grid-gap: 30px;
}
}

View file

@ -70,11 +70,13 @@ func HandleLogin(
logger := logrus.WithField("login", account.Login)
logger.Debugf("attempting authentication")
redirect := "/"
user, err := users.FindLogin(ctx, account.Login)
if err == sql.ErrNoRows {
redirect = "/register"
user = &core.User{
Login: account.Login,
Email: account.Email,
Avatar: account.Avatar,
Admin: false,
Machine: false,
@ -140,7 +142,6 @@ func HandleLogin(
}
user.Avatar = account.Avatar
user.Email = account.Email
user.Token = tok.Access
user.Refresh = tok.Refresh
user.LastLogin = time.Now().Unix()
@ -149,7 +150,7 @@ func HandleLogin(
}
// If the user account has never been synchronized we
// execute the synchonrization logic.
// execute the synchronization logic.
if time.Unix(user.Synced, 0).Add(syncPeriod).Before(time.Now()) {
user.Syncing = true
}
@ -162,17 +163,23 @@ func HandleLogin(
logger.Errorf("cannot update user: %s", err)
}
// launch the synchrnoization process in a go-routine,
// launch the synchronization process in a go-routine,
// since it is a long-running process and can take up
// to a few minutes.
if user.Syncing {
go synchronize(ctx, syncer, user)
}
// If the user account has not completed registration,
// redirect to the registration form.
if len(user.Email) == 0 && user.Created > 1619841600 {
redirect = "/register"
}
logger.Debugf("authentication successful")
session.Create(w, user)
http.Redirect(w, r, "/", http.StatusSeeOther)
http.Redirect(w, r, redirect, http.StatusSeeOther)
}
}

View file

@ -23,15 +23,13 @@ import (
"github.com/drone/drone-ui/dist"
"github.com/drone/drone/core"
"github.com/drone/drone/handler/web/landingpage"
)
func HandleIndex(host string, session core.Session, license core.LicenseService) http.HandlerFunc {
return func(rw http.ResponseWriter, r *http.Request) {
user, _ := session.Get(r)
if user == nil && host == "cloud.drone.io" && r.URL.Path == "/" {
rw.Header().Set("Content-Type", "text/html; charset=UTF-8")
rw.Write(landingpage.MustLookup("/index.html"))
if user == nil && r.URL.Path == "/" {
http.Redirect(rw, r, "/welcome", 303)
return
}

View file

@ -18,8 +18,8 @@ import (
"net/http"
"github.com/drone/drone-ui/dist"
"github.com/drone/drone/core"
"github.com/drone/drone/handler/web/landingpage"
"github.com/drone/drone/handler/web/link"
"github.com/drone/drone/logger"
"github.com/drone/go-login/login"
@ -128,15 +128,12 @@ func (s Server) Handler() http.Handler {
r.Get("/logout", HandleLogout())
r.Post("/logout", HandleLogout())
h2 := http.FileServer(landingpage.New())
h := http.FileServer(dist.New())
h = setupCache(h)
r.Handle("/favicon.png", h)
r.Handle("/js/*filepath", h)
r.Handle("/css/*filepath", h)
r.Handle("/img/*filepath", h)
r.Handle("/images/*filepath", h)
r.Handle("/static2/*filepath", h2)
r.Handle("/manifest.json", h)
r.Handle("/asset-manifest.json", h)
r.Handle("/static/*filepath", h)
r.NotFound(HandleIndex(s.Host, s.Session, s.Licenses))
return r

View file

@ -90,6 +90,11 @@ func (d *Datadog) do(ctx context.Context, unix int64) error {
if err != nil {
return err
}
userList, _ := d.users.ListRange(ctx, core.UserParams{
Sort: false,
Page: 1,
Size: 5,
})
tags := createTags(d.config)
data := new(payload)
data.Series = []series{
@ -98,7 +103,7 @@ func (d *Datadog) do(ctx context.Context, unix int64) error {
Points: [][]int64{[]int64{unix, users}},
Type: "gauge",
Host: d.system.Host,
Tags: tags,
Tags: append(tags, createInstallerTags(userList)...),
},
{
Metric: "drone.repos",

View file

@ -18,6 +18,7 @@ import (
"context"
"testing"
"github.com/drone/drone/core"
"github.com/drone/drone/mock"
"github.com/drone/drone/version"
"github.com/golang/mock/gomock"
@ -38,6 +39,7 @@ func TestDo(t *testing.T) {
users := mock.NewMockUserStore(controller)
users.EXPECT().Count(gomock.Any()).Return(int64(10), nil)
users.EXPECT().ListRange(gomock.Any(), gomock.Any()).Return([]*core.User{{Email: "jane@acme.com"}}, nil)
repos := mock.NewMockRepositoryStore(controller)
repos.EXPECT().Count(gomock.Any()).Return(int64(20), nil)
@ -73,7 +75,7 @@ var sample = `{
"points": [[915148800, 10]],
"type": "gauge",
"host": "test.example.com",
"tags": ["version:` + version.Version.String() + `","remote:github:cloud","scheduler:internal:agents","license:trial"]
"tags": ["version:` + version.Version.String() + `","remote:github:cloud","scheduler:internal:agents","license:trial","installer:jane@acme.com"]
},
{
"metric": "drone.repos",

View file

@ -17,6 +17,7 @@ package sink
import (
"fmt"
"github.com/drone/drone/core"
"github.com/drone/drone/version"
)
@ -70,3 +71,19 @@ func createTags(config Config) []string {
}
return tags
}
func createInstallerTags(users []*core.User) []string {
var tags []string
for _, user := range users {
if user.Machine {
continue
}
if len(user.Email) == 0 {
continue
}
tag := fmt.Sprintf("installer:%s", user.Email)
tags = append(tags, tag)
break
}
return tags
}

View file

@ -6,4 +6,4 @@
package mock
//go:generate mockgen -package=mock -destination=mock_gen.go github.com/drone/drone/core Pubsub,Canceler,ConvertService,ValidateService,NetrcService,Renewer,HookParser,UserService,RepositoryService,CommitService,StatusService,HookService,FileService,Batcher,BuildStore,CronStore,LogStore,PermStore,SecretStore,GlobalSecretStore,StageStore,StepStore,RepositoryStore,UserStore,Scheduler,Session,OrganizationService,SecretService,RegistryService,ConfigService,Transferer,Triggerer,Syncer,LogStream,WebhookSender,LicenseService
//go:generate mockgen -package=mock -destination=mock_gen.go github.com/drone/drone/core Pubsub,Canceler,ConvertService,ValidateService,NetrcService,Renewer,HookParser,UserService,RepositoryService,CommitService,StatusService,HookService,FileService,Batcher,BuildStore,CronStore,LogStore,PermStore,SecretStore,GlobalSecretStore,StageStore,StepStore,RepositoryStore,UserStore,Scheduler,Session,OrganizationService,SecretService,RegistryService,ConfigService,Transferer,Triggerer,Syncer,LogStream,WebhookSender,LicenseService,TemplateStore

File diff suppressed because it is too large Load diff

View file

@ -6,35 +6,36 @@ package mockscm
import (
context "context"
reflect "reflect"
scm "github.com/drone/go-scm/scm"
gomock "github.com/golang/mock/gomock"
reflect "reflect"
)
// MockContentService is a mock of ContentService interface
// MockContentService is a mock of ContentService interface.
type MockContentService struct {
ctrl *gomock.Controller
recorder *MockContentServiceMockRecorder
}
// MockContentServiceMockRecorder is the mock recorder for MockContentService
// MockContentServiceMockRecorder is the mock recorder for MockContentService.
type MockContentServiceMockRecorder struct {
mock *MockContentService
}
// NewMockContentService creates a new mock instance
// NewMockContentService creates a new mock instance.
func NewMockContentService(ctrl *gomock.Controller) *MockContentService {
mock := &MockContentService{ctrl: ctrl}
mock.recorder = &MockContentServiceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockContentService) EXPECT() *MockContentServiceMockRecorder {
return m.recorder
}
// Create mocks base method
// Create mocks base method.
func (m *MockContentService) Create(arg0 context.Context, arg1, arg2 string, arg3 *scm.ContentParams) (*scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Create", arg0, arg1, arg2, arg3)
@ -43,14 +44,14 @@ func (m *MockContentService) Create(arg0 context.Context, arg1, arg2 string, arg
return ret0, ret1
}
// Create indicates an expected call of Create
// Create indicates an expected call of Create.
func (mr *MockContentServiceMockRecorder) Create(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockContentService)(nil).Create), arg0, arg1, arg2, arg3)
}
// Delete mocks base method
func (m *MockContentService) Delete(arg0 context.Context, arg1, arg2, arg3 string) (*scm.Response, error) {
// Delete mocks base method.
func (m *MockContentService) Delete(arg0 context.Context, arg1, arg2 string, arg3 *scm.ContentParams) (*scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Delete", arg0, arg1, arg2, arg3)
ret0, _ := ret[0].(*scm.Response)
@ -58,13 +59,13 @@ func (m *MockContentService) Delete(arg0 context.Context, arg1, arg2, arg3 strin
return ret0, ret1
}
// Delete indicates an expected call of Delete
// Delete indicates an expected call of Delete.
func (mr *MockContentServiceMockRecorder) Delete(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockContentService)(nil).Delete), arg0, arg1, arg2, arg3)
}
// Find mocks base method
// Find mocks base method.
func (m *MockContentService) Find(arg0 context.Context, arg1, arg2, arg3 string) (*scm.Content, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Find", arg0, arg1, arg2, arg3)
@ -74,13 +75,13 @@ func (m *MockContentService) Find(arg0 context.Context, arg1, arg2, arg3 string)
return ret0, ret1, ret2
}
// Find indicates an expected call of Find
// Find indicates an expected call of Find.
func (mr *MockContentServiceMockRecorder) Find(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockContentService)(nil).Find), arg0, arg1, arg2, arg3)
}
// List mocks base method
// List mocks base method.
func (m *MockContentService) List(arg0 context.Context, arg1, arg2, arg3 string, arg4 scm.ListOptions) ([]*scm.ContentInfo, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "List", arg0, arg1, arg2, arg3, arg4)
@ -90,13 +91,13 @@ func (m *MockContentService) List(arg0 context.Context, arg1, arg2, arg3 string,
return ret0, ret1, ret2
}
// List indicates an expected call of List
// List indicates an expected call of List.
func (mr *MockContentServiceMockRecorder) List(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockContentService)(nil).List), arg0, arg1, arg2, arg3, arg4)
}
// Update mocks base method
// Update mocks base method.
func (m *MockContentService) Update(arg0 context.Context, arg1, arg2 string, arg3 *scm.ContentParams) (*scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Update", arg0, arg1, arg2, arg3)
@ -105,36 +106,36 @@ func (m *MockContentService) Update(arg0 context.Context, arg1, arg2 string, arg
return ret0, ret1
}
// Update indicates an expected call of Update
// Update indicates an expected call of Update.
func (mr *MockContentServiceMockRecorder) Update(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockContentService)(nil).Update), arg0, arg1, arg2, arg3)
}
// MockGitService is a mock of GitService interface
// MockGitService is a mock of GitService interface.
type MockGitService struct {
ctrl *gomock.Controller
recorder *MockGitServiceMockRecorder
}
// MockGitServiceMockRecorder is the mock recorder for MockGitService
// MockGitServiceMockRecorder is the mock recorder for MockGitService.
type MockGitServiceMockRecorder struct {
mock *MockGitService
}
// NewMockGitService creates a new mock instance
// NewMockGitService creates a new mock instance.
func NewMockGitService(ctrl *gomock.Controller) *MockGitService {
mock := &MockGitService{ctrl: ctrl}
mock.recorder = &MockGitServiceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockGitService) EXPECT() *MockGitServiceMockRecorder {
return m.recorder
}
// CompareChanges mocks base method
// CompareChanges mocks base method.
func (m *MockGitService) CompareChanges(arg0 context.Context, arg1, arg2, arg3 string, arg4 scm.ListOptions) ([]*scm.Change, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CompareChanges", arg0, arg1, arg2, arg3, arg4)
@ -144,13 +145,28 @@ func (m *MockGitService) CompareChanges(arg0 context.Context, arg1, arg2, arg3 s
return ret0, ret1, ret2
}
// CompareChanges indicates an expected call of CompareChanges
// CompareChanges indicates an expected call of CompareChanges.
func (mr *MockGitServiceMockRecorder) CompareChanges(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CompareChanges", reflect.TypeOf((*MockGitService)(nil).CompareChanges), arg0, arg1, arg2, arg3, arg4)
}
// FindBranch mocks base method
// CreateBranch mocks base method.
func (m *MockGitService) CreateBranch(arg0 context.Context, arg1 string, arg2 *scm.CreateBranch) (*scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateBranch", arg0, arg1, arg2)
ret0, _ := ret[0].(*scm.Response)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreateBranch indicates an expected call of CreateBranch.
func (mr *MockGitServiceMockRecorder) CreateBranch(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateBranch", reflect.TypeOf((*MockGitService)(nil).CreateBranch), arg0, arg1, arg2)
}
// FindBranch mocks base method.
func (m *MockGitService) FindBranch(arg0 context.Context, arg1, arg2 string) (*scm.Reference, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FindBranch", arg0, arg1, arg2)
@ -160,13 +176,13 @@ func (m *MockGitService) FindBranch(arg0 context.Context, arg1, arg2 string) (*s
return ret0, ret1, ret2
}
// FindBranch indicates an expected call of FindBranch
// FindBranch indicates an expected call of FindBranch.
func (mr *MockGitServiceMockRecorder) FindBranch(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindBranch", reflect.TypeOf((*MockGitService)(nil).FindBranch), arg0, arg1, arg2)
}
// FindCommit mocks base method
// FindCommit mocks base method.
func (m *MockGitService) FindCommit(arg0 context.Context, arg1, arg2 string) (*scm.Commit, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FindCommit", arg0, arg1, arg2)
@ -176,13 +192,13 @@ func (m *MockGitService) FindCommit(arg0 context.Context, arg1, arg2 string) (*s
return ret0, ret1, ret2
}
// FindCommit indicates an expected call of FindCommit
// FindCommit indicates an expected call of FindCommit.
func (mr *MockGitServiceMockRecorder) FindCommit(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindCommit", reflect.TypeOf((*MockGitService)(nil).FindCommit), arg0, arg1, arg2)
}
// FindTag mocks base method
// FindTag mocks base method.
func (m *MockGitService) FindTag(arg0 context.Context, arg1, arg2 string) (*scm.Reference, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FindTag", arg0, arg1, arg2)
@ -192,13 +208,13 @@ func (m *MockGitService) FindTag(arg0 context.Context, arg1, arg2 string) (*scm.
return ret0, ret1, ret2
}
// FindTag indicates an expected call of FindTag
// FindTag indicates an expected call of FindTag.
func (mr *MockGitServiceMockRecorder) FindTag(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindTag", reflect.TypeOf((*MockGitService)(nil).FindTag), arg0, arg1, arg2)
}
// ListBranches mocks base method
// ListBranches mocks base method.
func (m *MockGitService) ListBranches(arg0 context.Context, arg1 string, arg2 scm.ListOptions) ([]*scm.Reference, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListBranches", arg0, arg1, arg2)
@ -208,13 +224,13 @@ func (m *MockGitService) ListBranches(arg0 context.Context, arg1 string, arg2 sc
return ret0, ret1, ret2
}
// ListBranches indicates an expected call of ListBranches
// ListBranches indicates an expected call of ListBranches.
func (mr *MockGitServiceMockRecorder) ListBranches(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListBranches", reflect.TypeOf((*MockGitService)(nil).ListBranches), arg0, arg1, arg2)
}
// ListChanges mocks base method
// ListChanges mocks base method.
func (m *MockGitService) ListChanges(arg0 context.Context, arg1, arg2 string, arg3 scm.ListOptions) ([]*scm.Change, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListChanges", arg0, arg1, arg2, arg3)
@ -224,13 +240,13 @@ func (m *MockGitService) ListChanges(arg0 context.Context, arg1, arg2 string, ar
return ret0, ret1, ret2
}
// ListChanges indicates an expected call of ListChanges
// ListChanges indicates an expected call of ListChanges.
func (mr *MockGitServiceMockRecorder) ListChanges(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListChanges", reflect.TypeOf((*MockGitService)(nil).ListChanges), arg0, arg1, arg2, arg3)
}
// ListCommits mocks base method
// ListCommits mocks base method.
func (m *MockGitService) ListCommits(arg0 context.Context, arg1 string, arg2 scm.CommitListOptions) ([]*scm.Commit, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListCommits", arg0, arg1, arg2)
@ -240,13 +256,13 @@ func (m *MockGitService) ListCommits(arg0 context.Context, arg1 string, arg2 scm
return ret0, ret1, ret2
}
// ListCommits indicates an expected call of ListCommits
// ListCommits indicates an expected call of ListCommits.
func (mr *MockGitServiceMockRecorder) ListCommits(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListCommits", reflect.TypeOf((*MockGitService)(nil).ListCommits), arg0, arg1, arg2)
}
// ListTags mocks base method
// ListTags mocks base method.
func (m *MockGitService) ListTags(arg0 context.Context, arg1 string, arg2 scm.ListOptions) ([]*scm.Reference, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListTags", arg0, arg1, arg2)
@ -256,36 +272,36 @@ func (m *MockGitService) ListTags(arg0 context.Context, arg1 string, arg2 scm.Li
return ret0, ret1, ret2
}
// ListTags indicates an expected call of ListTags
// ListTags indicates an expected call of ListTags.
func (mr *MockGitServiceMockRecorder) ListTags(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTags", reflect.TypeOf((*MockGitService)(nil).ListTags), arg0, arg1, arg2)
}
// MockOrganizationService is a mock of OrganizationService interface
// MockOrganizationService is a mock of OrganizationService interface.
type MockOrganizationService struct {
ctrl *gomock.Controller
recorder *MockOrganizationServiceMockRecorder
}
// MockOrganizationServiceMockRecorder is the mock recorder for MockOrganizationService
// MockOrganizationServiceMockRecorder is the mock recorder for MockOrganizationService.
type MockOrganizationServiceMockRecorder struct {
mock *MockOrganizationService
}
// NewMockOrganizationService creates a new mock instance
// NewMockOrganizationService creates a new mock instance.
func NewMockOrganizationService(ctrl *gomock.Controller) *MockOrganizationService {
mock := &MockOrganizationService{ctrl: ctrl}
mock.recorder = &MockOrganizationServiceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockOrganizationService) EXPECT() *MockOrganizationServiceMockRecorder {
return m.recorder
}
// Find mocks base method
// Find mocks base method.
func (m *MockOrganizationService) Find(arg0 context.Context, arg1 string) (*scm.Organization, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Find", arg0, arg1)
@ -295,13 +311,13 @@ func (m *MockOrganizationService) Find(arg0 context.Context, arg1 string) (*scm.
return ret0, ret1, ret2
}
// Find indicates an expected call of Find
// Find indicates an expected call of Find.
func (mr *MockOrganizationServiceMockRecorder) Find(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockOrganizationService)(nil).Find), arg0, arg1)
}
// FindMembership mocks base method
// FindMembership mocks base method.
func (m *MockOrganizationService) FindMembership(arg0 context.Context, arg1, arg2 string) (*scm.Membership, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FindMembership", arg0, arg1, arg2)
@ -311,13 +327,13 @@ func (m *MockOrganizationService) FindMembership(arg0 context.Context, arg1, arg
return ret0, ret1, ret2
}
// FindMembership indicates an expected call of FindMembership
// FindMembership indicates an expected call of FindMembership.
func (mr *MockOrganizationServiceMockRecorder) FindMembership(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindMembership", reflect.TypeOf((*MockOrganizationService)(nil).FindMembership), arg0, arg1, arg2)
}
// List mocks base method
// List mocks base method.
func (m *MockOrganizationService) List(arg0 context.Context, arg1 scm.ListOptions) ([]*scm.Organization, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "List", arg0, arg1)
@ -327,36 +343,36 @@ func (m *MockOrganizationService) List(arg0 context.Context, arg1 scm.ListOption
return ret0, ret1, ret2
}
// List indicates an expected call of List
// List indicates an expected call of List.
func (mr *MockOrganizationServiceMockRecorder) List(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockOrganizationService)(nil).List), arg0, arg1)
}
// MockPullRequestService is a mock of PullRequestService interface
// MockPullRequestService is a mock of PullRequestService interface.
type MockPullRequestService struct {
ctrl *gomock.Controller
recorder *MockPullRequestServiceMockRecorder
}
// MockPullRequestServiceMockRecorder is the mock recorder for MockPullRequestService
// MockPullRequestServiceMockRecorder is the mock recorder for MockPullRequestService.
type MockPullRequestServiceMockRecorder struct {
mock *MockPullRequestService
}
// NewMockPullRequestService creates a new mock instance
// NewMockPullRequestService creates a new mock instance.
func NewMockPullRequestService(ctrl *gomock.Controller) *MockPullRequestService {
mock := &MockPullRequestService{ctrl: ctrl}
mock.recorder = &MockPullRequestServiceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockPullRequestService) EXPECT() *MockPullRequestServiceMockRecorder {
return m.recorder
}
// Close mocks base method
// Close mocks base method.
func (m *MockPullRequestService) Close(arg0 context.Context, arg1 string, arg2 int) (*scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Close", arg0, arg1, arg2)
@ -365,13 +381,13 @@ func (m *MockPullRequestService) Close(arg0 context.Context, arg1 string, arg2 i
return ret0, ret1
}
// Close indicates an expected call of Close
// Close indicates an expected call of Close.
func (mr *MockPullRequestServiceMockRecorder) Close(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockPullRequestService)(nil).Close), arg0, arg1, arg2)
}
// Create mocks base method
// Create mocks base method.
func (m *MockPullRequestService) Create(arg0 context.Context, arg1 string, arg2 *scm.PullRequestInput) (*scm.PullRequest, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Create", arg0, arg1, arg2)
@ -381,13 +397,13 @@ func (m *MockPullRequestService) Create(arg0 context.Context, arg1 string, arg2
return ret0, ret1, ret2
}
// Create indicates an expected call of Create
// Create indicates an expected call of Create.
func (mr *MockPullRequestServiceMockRecorder) Create(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockPullRequestService)(nil).Create), arg0, arg1, arg2)
}
// CreateComment mocks base method
// CreateComment mocks base method.
func (m *MockPullRequestService) CreateComment(arg0 context.Context, arg1 string, arg2 int, arg3 *scm.CommentInput) (*scm.Comment, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateComment", arg0, arg1, arg2, arg3)
@ -397,13 +413,13 @@ func (m *MockPullRequestService) CreateComment(arg0 context.Context, arg1 string
return ret0, ret1, ret2
}
// CreateComment indicates an expected call of CreateComment
// CreateComment indicates an expected call of CreateComment.
func (mr *MockPullRequestServiceMockRecorder) CreateComment(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateComment", reflect.TypeOf((*MockPullRequestService)(nil).CreateComment), arg0, arg1, arg2, arg3)
}
// DeleteComment mocks base method
// DeleteComment mocks base method.
func (m *MockPullRequestService) DeleteComment(arg0 context.Context, arg1 string, arg2, arg3 int) (*scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteComment", arg0, arg1, arg2, arg3)
@ -412,13 +428,13 @@ func (m *MockPullRequestService) DeleteComment(arg0 context.Context, arg1 string
return ret0, ret1
}
// DeleteComment indicates an expected call of DeleteComment
// DeleteComment indicates an expected call of DeleteComment.
func (mr *MockPullRequestServiceMockRecorder) DeleteComment(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteComment", reflect.TypeOf((*MockPullRequestService)(nil).DeleteComment), arg0, arg1, arg2, arg3)
}
// Find mocks base method
// Find mocks base method.
func (m *MockPullRequestService) Find(arg0 context.Context, arg1 string, arg2 int) (*scm.PullRequest, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Find", arg0, arg1, arg2)
@ -428,13 +444,13 @@ func (m *MockPullRequestService) Find(arg0 context.Context, arg1 string, arg2 in
return ret0, ret1, ret2
}
// Find indicates an expected call of Find
// Find indicates an expected call of Find.
func (mr *MockPullRequestServiceMockRecorder) Find(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockPullRequestService)(nil).Find), arg0, arg1, arg2)
}
// FindComment mocks base method
// FindComment mocks base method.
func (m *MockPullRequestService) FindComment(arg0 context.Context, arg1 string, arg2, arg3 int) (*scm.Comment, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FindComment", arg0, arg1, arg2, arg3)
@ -444,13 +460,13 @@ func (m *MockPullRequestService) FindComment(arg0 context.Context, arg1 string,
return ret0, ret1, ret2
}
// FindComment indicates an expected call of FindComment
// FindComment indicates an expected call of FindComment.
func (mr *MockPullRequestServiceMockRecorder) FindComment(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindComment", reflect.TypeOf((*MockPullRequestService)(nil).FindComment), arg0, arg1, arg2, arg3)
}
// List mocks base method
// List mocks base method.
func (m *MockPullRequestService) List(arg0 context.Context, arg1 string, arg2 scm.PullRequestListOptions) ([]*scm.PullRequest, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "List", arg0, arg1, arg2)
@ -460,13 +476,13 @@ func (m *MockPullRequestService) List(arg0 context.Context, arg1 string, arg2 sc
return ret0, ret1, ret2
}
// List indicates an expected call of List
// List indicates an expected call of List.
func (mr *MockPullRequestServiceMockRecorder) List(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockPullRequestService)(nil).List), arg0, arg1, arg2)
}
// ListChanges mocks base method
// ListChanges mocks base method.
func (m *MockPullRequestService) ListChanges(arg0 context.Context, arg1 string, arg2 int, arg3 scm.ListOptions) ([]*scm.Change, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListChanges", arg0, arg1, arg2, arg3)
@ -476,13 +492,13 @@ func (m *MockPullRequestService) ListChanges(arg0 context.Context, arg1 string,
return ret0, ret1, ret2
}
// ListChanges indicates an expected call of ListChanges
// ListChanges indicates an expected call of ListChanges.
func (mr *MockPullRequestServiceMockRecorder) ListChanges(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListChanges", reflect.TypeOf((*MockPullRequestService)(nil).ListChanges), arg0, arg1, arg2, arg3)
}
// ListComments mocks base method
// ListComments mocks base method.
func (m *MockPullRequestService) ListComments(arg0 context.Context, arg1 string, arg2 int, arg3 scm.ListOptions) ([]*scm.Comment, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListComments", arg0, arg1, arg2, arg3)
@ -492,13 +508,29 @@ func (m *MockPullRequestService) ListComments(arg0 context.Context, arg1 string,
return ret0, ret1, ret2
}
// ListComments indicates an expected call of ListComments
// ListComments indicates an expected call of ListComments.
func (mr *MockPullRequestServiceMockRecorder) ListComments(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListComments", reflect.TypeOf((*MockPullRequestService)(nil).ListComments), arg0, arg1, arg2, arg3)
}
// Merge mocks base method
// ListCommits mocks base method.
func (m *MockPullRequestService) ListCommits(arg0 context.Context, arg1 string, arg2 int, arg3 scm.ListOptions) ([]*scm.Commit, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListCommits", arg0, arg1, arg2, arg3)
ret0, _ := ret[0].([]*scm.Commit)
ret1, _ := ret[1].(*scm.Response)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
// ListCommits indicates an expected call of ListCommits.
func (mr *MockPullRequestServiceMockRecorder) ListCommits(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListCommits", reflect.TypeOf((*MockPullRequestService)(nil).ListCommits), arg0, arg1, arg2, arg3)
}
// Merge mocks base method.
func (m *MockPullRequestService) Merge(arg0 context.Context, arg1 string, arg2 int) (*scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Merge", arg0, arg1, arg2)
@ -507,36 +539,36 @@ func (m *MockPullRequestService) Merge(arg0 context.Context, arg1 string, arg2 i
return ret0, ret1
}
// Merge indicates an expected call of Merge
// Merge indicates an expected call of Merge.
func (mr *MockPullRequestServiceMockRecorder) Merge(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Merge", reflect.TypeOf((*MockPullRequestService)(nil).Merge), arg0, arg1, arg2)
}
// MockRepositoryService is a mock of RepositoryService interface
// MockRepositoryService is a mock of RepositoryService interface.
type MockRepositoryService struct {
ctrl *gomock.Controller
recorder *MockRepositoryServiceMockRecorder
}
// MockRepositoryServiceMockRecorder is the mock recorder for MockRepositoryService
// MockRepositoryServiceMockRecorder is the mock recorder for MockRepositoryService.
type MockRepositoryServiceMockRecorder struct {
mock *MockRepositoryService
}
// NewMockRepositoryService creates a new mock instance
// NewMockRepositoryService creates a new mock instance.
func NewMockRepositoryService(ctrl *gomock.Controller) *MockRepositoryService {
mock := &MockRepositoryService{ctrl: ctrl}
mock.recorder = &MockRepositoryServiceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockRepositoryService) EXPECT() *MockRepositoryServiceMockRecorder {
return m.recorder
}
// CreateHook mocks base method
// CreateHook mocks base method.
func (m *MockRepositoryService) CreateHook(arg0 context.Context, arg1 string, arg2 *scm.HookInput) (*scm.Hook, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateHook", arg0, arg1, arg2)
@ -546,13 +578,13 @@ func (m *MockRepositoryService) CreateHook(arg0 context.Context, arg1 string, ar
return ret0, ret1, ret2
}
// CreateHook indicates an expected call of CreateHook
// CreateHook indicates an expected call of CreateHook.
func (mr *MockRepositoryServiceMockRecorder) CreateHook(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateHook", reflect.TypeOf((*MockRepositoryService)(nil).CreateHook), arg0, arg1, arg2)
}
// CreateStatus mocks base method
// CreateStatus mocks base method.
func (m *MockRepositoryService) CreateStatus(arg0 context.Context, arg1, arg2 string, arg3 *scm.StatusInput) (*scm.Status, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateStatus", arg0, arg1, arg2, arg3)
@ -562,13 +594,13 @@ func (m *MockRepositoryService) CreateStatus(arg0 context.Context, arg1, arg2 st
return ret0, ret1, ret2
}
// CreateStatus indicates an expected call of CreateStatus
// CreateStatus indicates an expected call of CreateStatus.
func (mr *MockRepositoryServiceMockRecorder) CreateStatus(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateStatus", reflect.TypeOf((*MockRepositoryService)(nil).CreateStatus), arg0, arg1, arg2, arg3)
}
// DeleteHook mocks base method
// DeleteHook mocks base method.
func (m *MockRepositoryService) DeleteHook(arg0 context.Context, arg1, arg2 string) (*scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteHook", arg0, arg1, arg2)
@ -577,13 +609,13 @@ func (m *MockRepositoryService) DeleteHook(arg0 context.Context, arg1, arg2 stri
return ret0, ret1
}
// DeleteHook indicates an expected call of DeleteHook
// DeleteHook indicates an expected call of DeleteHook.
func (mr *MockRepositoryServiceMockRecorder) DeleteHook(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteHook", reflect.TypeOf((*MockRepositoryService)(nil).DeleteHook), arg0, arg1, arg2)
}
// Find mocks base method
// Find mocks base method.
func (m *MockRepositoryService) Find(arg0 context.Context, arg1 string) (*scm.Repository, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Find", arg0, arg1)
@ -593,13 +625,13 @@ func (m *MockRepositoryService) Find(arg0 context.Context, arg1 string) (*scm.Re
return ret0, ret1, ret2
}
// Find indicates an expected call of Find
// Find indicates an expected call of Find.
func (mr *MockRepositoryServiceMockRecorder) Find(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockRepositoryService)(nil).Find), arg0, arg1)
}
// FindHook mocks base method
// FindHook mocks base method.
func (m *MockRepositoryService) FindHook(arg0 context.Context, arg1, arg2 string) (*scm.Hook, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FindHook", arg0, arg1, arg2)
@ -609,13 +641,13 @@ func (m *MockRepositoryService) FindHook(arg0 context.Context, arg1, arg2 string
return ret0, ret1, ret2
}
// FindHook indicates an expected call of FindHook
// FindHook indicates an expected call of FindHook.
func (mr *MockRepositoryServiceMockRecorder) FindHook(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindHook", reflect.TypeOf((*MockRepositoryService)(nil).FindHook), arg0, arg1, arg2)
}
// FindPerms mocks base method
// FindPerms mocks base method.
func (m *MockRepositoryService) FindPerms(arg0 context.Context, arg1 string) (*scm.Perm, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FindPerms", arg0, arg1)
@ -625,13 +657,13 @@ func (m *MockRepositoryService) FindPerms(arg0 context.Context, arg1 string) (*s
return ret0, ret1, ret2
}
// FindPerms indicates an expected call of FindPerms
// FindPerms indicates an expected call of FindPerms.
func (mr *MockRepositoryServiceMockRecorder) FindPerms(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindPerms", reflect.TypeOf((*MockRepositoryService)(nil).FindPerms), arg0, arg1)
}
// List mocks base method
// List mocks base method.
func (m *MockRepositoryService) List(arg0 context.Context, arg1 scm.ListOptions) ([]*scm.Repository, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "List", arg0, arg1)
@ -641,13 +673,13 @@ func (m *MockRepositoryService) List(arg0 context.Context, arg1 scm.ListOptions)
return ret0, ret1, ret2
}
// List indicates an expected call of List
// List indicates an expected call of List.
func (mr *MockRepositoryServiceMockRecorder) List(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockRepositoryService)(nil).List), arg0, arg1)
}
// ListHooks mocks base method
// ListHooks mocks base method.
func (m *MockRepositoryService) ListHooks(arg0 context.Context, arg1 string, arg2 scm.ListOptions) ([]*scm.Hook, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListHooks", arg0, arg1, arg2)
@ -657,13 +689,13 @@ func (m *MockRepositoryService) ListHooks(arg0 context.Context, arg1 string, arg
return ret0, ret1, ret2
}
// ListHooks indicates an expected call of ListHooks
// ListHooks indicates an expected call of ListHooks.
func (mr *MockRepositoryServiceMockRecorder) ListHooks(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListHooks", reflect.TypeOf((*MockRepositoryService)(nil).ListHooks), arg0, arg1, arg2)
}
// ListStatus mocks base method
// ListStatus mocks base method.
func (m *MockRepositoryService) ListStatus(arg0 context.Context, arg1, arg2 string, arg3 scm.ListOptions) ([]*scm.Status, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListStatus", arg0, arg1, arg2, arg3)
@ -673,13 +705,13 @@ func (m *MockRepositoryService) ListStatus(arg0 context.Context, arg1, arg2 stri
return ret0, ret1, ret2
}
// ListStatus indicates an expected call of ListStatus
// ListStatus indicates an expected call of ListStatus.
func (mr *MockRepositoryServiceMockRecorder) ListStatus(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListStatus", reflect.TypeOf((*MockRepositoryService)(nil).ListStatus), arg0, arg1, arg2, arg3)
}
// UpdateHook mocks base method
// UpdateHook mocks base method.
func (m *MockRepositoryService) UpdateHook(arg0 context.Context, arg1, arg2 string, arg3 *scm.HookInput) (*scm.Hook, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateHook", arg0, arg1, arg2, arg3)
@ -689,36 +721,36 @@ func (m *MockRepositoryService) UpdateHook(arg0 context.Context, arg1, arg2 stri
return ret0, ret1, ret2
}
// UpdateHook indicates an expected call of UpdateHook
// UpdateHook indicates an expected call of UpdateHook.
func (mr *MockRepositoryServiceMockRecorder) UpdateHook(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHook", reflect.TypeOf((*MockRepositoryService)(nil).UpdateHook), arg0, arg1, arg2, arg3)
}
// MockUserService is a mock of UserService interface
// MockUserService is a mock of UserService interface.
type MockUserService struct {
ctrl *gomock.Controller
recorder *MockUserServiceMockRecorder
}
// MockUserServiceMockRecorder is the mock recorder for MockUserService
// MockUserServiceMockRecorder is the mock recorder for MockUserService.
type MockUserServiceMockRecorder struct {
mock *MockUserService
}
// NewMockUserService creates a new mock instance
// NewMockUserService creates a new mock instance.
func NewMockUserService(ctrl *gomock.Controller) *MockUserService {
mock := &MockUserService{ctrl: ctrl}
mock.recorder = &MockUserServiceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockUserService) EXPECT() *MockUserServiceMockRecorder {
return m.recorder
}
// Find mocks base method
// Find mocks base method.
func (m *MockUserService) Find(arg0 context.Context) (*scm.User, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Find", arg0)
@ -728,13 +760,13 @@ func (m *MockUserService) Find(arg0 context.Context) (*scm.User, *scm.Response,
return ret0, ret1, ret2
}
// Find indicates an expected call of Find
// Find indicates an expected call of Find.
func (mr *MockUserServiceMockRecorder) Find(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockUserService)(nil).Find), arg0)
}
// FindEmail mocks base method
// FindEmail mocks base method.
func (m *MockUserService) FindEmail(arg0 context.Context) (string, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FindEmail", arg0)
@ -744,13 +776,13 @@ func (m *MockUserService) FindEmail(arg0 context.Context) (string, *scm.Response
return ret0, ret1, ret2
}
// FindEmail indicates an expected call of FindEmail
// FindEmail indicates an expected call of FindEmail.
func (mr *MockUserServiceMockRecorder) FindEmail(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindEmail", reflect.TypeOf((*MockUserService)(nil).FindEmail), arg0)
}
// FindLogin mocks base method
// FindLogin mocks base method.
func (m *MockUserService) FindLogin(arg0 context.Context, arg1 string) (*scm.User, *scm.Response, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FindLogin", arg0, arg1)
@ -760,7 +792,7 @@ func (m *MockUserService) FindLogin(arg0 context.Context, arg1 string) (*scm.Use
return ret0, ret1, ret2
}
// FindLogin indicates an expected call of FindLogin
// FindLogin indicates an expected call of FindLogin.
func (mr *MockUserServiceMockRecorder) FindLogin(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindLogin", reflect.TypeOf((*MockUserService)(nil).FindLogin), arg0, arg1)

View file

@ -44,7 +44,7 @@ type (
System *core.System `json:"system"`
}
// BuildManager encapsulets complex build operations and provides
// BuildManager encapsulates complex build operations and provides
// a simplified interface for build runners.
BuildManager interface {
// Request requests the next available build stage for execution.
@ -60,13 +60,13 @@ type (
Details(ctx context.Context, stage int64) (*Context, error)
// Before signals the build step is about to start.
Before(ctxt context.Context, step *core.Step) error
Before(ctx context.Context, step *core.Step) error
// After signals the build step is complete.
After(ctx context.Context, step *core.Step) error
// Before signals the build stage is about to start.
BeforeAll(ctxt context.Context, stage *core.Stage) error
BeforeAll(ctx context.Context, stage *core.Stage) error
// After signals the build stage is complete.
AfterAll(ctx context.Context, stage *core.Stage) error
@ -84,7 +84,7 @@ type (
UploadBytes(ctx context.Context, step int64, b []byte) error
}
// Request provildes filters when requesting a pending
// Request provides filters when requesting a pending
// build from the queue. This allows an agent, for example,
// to request a build that matches its architecture and kernel.
Request struct {

View file

@ -58,7 +58,7 @@ func (Server) Details(ctx context.Context, stage int64) (*manager.Context, error
}
// Before signals the build step is about to start.
func (Server) Before(ctxt context.Context, step *core.Step) error {
func (Server) Before(ctx context.Context, step *core.Step) error {
return errors.New("not implemented")
}
@ -68,7 +68,7 @@ func (Server) After(ctx context.Context, step *core.Step) error {
}
// Before signals the build stage is about to start.
func (Server) BeforeAll(ctxt context.Context, stage *core.Stage) error {
func (Server) BeforeAll(ctx context.Context, stage *core.Stage) error {
return errors.New("not implemented")
}

View file

@ -69,7 +69,7 @@ func HandlePing() http.HandlerFunc {
}
// HandleRequest returns an http.HandlerFunc that processes an
// http.Request to reqeust a stage from the queue for execution.
// http.Request to request a stage from the queue for execution.
//
// POST /rpc/v2/stage
func HandleRequest(m manager.BuildManager) http.HandlerFunc {
@ -135,7 +135,7 @@ func HandleInfo(m manager.BuildManager) http.HandlerFunc {
writeJSON(w, &details{
Context: res,
Netrc: netrc,
Repo: &repositroy{
Repo: &repository{
Repository: res.Repo,
Secret: res.Repo.Secret,
},

View file

@ -16,12 +16,12 @@ import (
type details struct {
*manager.Context
Netrc *core.Netrc `json:"netrc"`
Repo *repositroy `json:"repository"`
Repo *repository `json:"repository"`
}
// repository wraps a repository object to include the secret
// when the repository is marshaled to json.
type repositroy struct {
type repository struct {
*core.Repository
Secret string `json:"secret"`
}

View file

@ -38,7 +38,7 @@ type Config struct {
}
}
// heper function reads and unmarshales the docker-machine
// helper function reads and unmarshalls the docker-machine
// configuration from a reader.
func parseReader(r io.Reader) (*Config, error) {
out := new(Config)

View file

@ -37,7 +37,7 @@ func Load(home, match string) ([]*Config, error) {
if err != nil {
return nil, err
}
// If no match logic is defined, the matchine is
// If no match logic is defined, the machine is
// automatically used as a build machine.
if match == "" {
machines = append(machines, conf)

View file

@ -539,7 +539,7 @@ func (r *Runner) start(ctx context.Context) error {
for {
select {
case <-ctx.Done():
return ctx.Err()
return nil
default:
// This error is ignored on purpose. The system
// should not exit the runner on error. The run

View file

@ -71,7 +71,7 @@ func TestGlobalErr(t *testing.T) {
false, time.Minute)
_, err := service.Find(noContext, args)
if err == nil {
t.Errorf("Expect http.Reponse error")
t.Errorf("Expect http.Response error")
} else if err.Error() != "Not Found" {
t.Errorf("Expect Not Found error")
}

View file

@ -7,13 +7,11 @@
package converter
import (
"bytes"
"context"
"strings"
"github.com/drone/drone/core"
"github.com/google/go-jsonnet"
"github.com/drone/drone/plugin/converter/jsonnet"
)
// TODO(bradrydzewski) handle jsonnet imports
@ -42,32 +40,12 @@ func (p *jsonnetPlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*co
return nil, nil
}
// create the jsonnet vm
vm := jsonnet.MakeVM()
vm.MaxStack = 500
vm.StringOutput = false
vm.ErrorFormatter.SetMaxStackTraceSize(20)
file, err := jsonnet.Parse(req, nil, nil)
// convert the jsonnet file to yaml
buf := new(bytes.Buffer)
docs, err := vm.EvaluateSnippetStream(req.Repo.Config, req.Config.Data)
if err != nil {
doc, err2 := vm.EvaluateSnippet(req.Repo.Config, req.Config.Data)
if err2 != nil {
return nil, err
}
docs = append(docs, doc)
return nil, err
}
// the jsonnet vm returns a stream of yaml documents
// that need to be combined into a single yaml file.
for _, doc := range docs {
buf.WriteString("---")
buf.WriteString("\n")
buf.WriteString(doc)
}
return &core.Config{
Data: buf.String(),
Data: file,
}, nil
}

View file

@ -0,0 +1,118 @@
package jsonnet
import (
"bytes"
"fmt"
"strconv"
"github.com/drone/drone/core"
"github.com/google/go-jsonnet"
)
const repo = "repo."
const build = "build."
const param = "param."
func Parse(req *core.ConvertArgs, template *core.Template, templateData map[string]interface{}) (string, error) {
vm := jsonnet.MakeVM()
vm.MaxStack = 500
vm.StringOutput = false
vm.ErrorFormatter.SetMaxStackTraceSize(20)
//map build/repo parameters
if req.Build != nil {
mapBuild(req.Build, vm)
}
if req.Repo != nil {
mapRepo(req.Repo, vm)
}
var jsonnetFile string
var jsonnetFileName string
if template != nil {
jsonnetFile = template.Data
jsonnetFileName = template.Name
} else {
jsonnetFile = req.Config.Data
jsonnetFileName = req.Repo.Config
}
// map external inputs
if len(templateData) != 0 {
for k, v := range templateData {
key := fmt.Sprintf("input." + k)
val := fmt.Sprint(v)
vm.ExtVar(key, val)
}
}
// convert the jsonnet file to yaml
buf := new(bytes.Buffer)
docs, err := vm.EvaluateSnippetStream(jsonnetFileName, jsonnetFile)
if err != nil {
doc, err2 := vm.EvaluateSnippet(jsonnetFileName, jsonnetFile)
if err2 != nil {
return "", err
}
docs = append(docs, doc)
}
// the jsonnet vm returns a stream of yaml documents
// that need to be combined into a single yaml file.
for _, doc := range docs {
buf.WriteString("---")
buf.WriteString("\n")
buf.WriteString(doc)
}
return buf.String(), nil
}
func mapBuild(v *core.Build, vm *jsonnet.VM) {
vm.ExtVar(build+"event", v.Event)
vm.ExtVar(build+"action", v.Action)
vm.ExtVar(build+"environment", v.Deploy)
vm.ExtVar(build+"link", v.Link)
vm.ExtVar(build+"branch", v.Target)
vm.ExtVar(build+"source", v.Source)
vm.ExtVar(build+"before", v.Before)
vm.ExtVar(build+"after", v.After)
vm.ExtVar(build+"target", v.Target)
vm.ExtVar(build+"ref", v.Ref)
vm.ExtVar(build+"commit", v.After)
vm.ExtVar(build+"ref", v.Ref)
vm.ExtVar(build+"title", v.Title)
vm.ExtVar(build+"message", v.Message)
vm.ExtVar(build+"source_repo", v.Fork)
vm.ExtVar(build+"author_login", v.Author)
vm.ExtVar(build+"author_name", v.AuthorName)
vm.ExtVar(build+"author_email", v.AuthorEmail)
vm.ExtVar(build+"author_avatar", v.AuthorAvatar)
vm.ExtVar(build+"sender", v.Sender)
fromMap(v.Params, vm)
}
func mapRepo(v *core.Repository, vm *jsonnet.VM) {
vm.ExtVar(repo+"uid", v.UID)
vm.ExtVar(repo+"name", v.Name)
vm.ExtVar(repo+"namespace", v.Namespace)
vm.ExtVar(repo+"slug", v.Slug)
vm.ExtVar(repo+"git_http_url", v.HTTPURL)
vm.ExtVar(repo+"git_ssh_url", v.SSHURL)
vm.ExtVar(repo+"link", v.Link)
vm.ExtVar(repo+"branch", v.Branch)
vm.ExtVar(repo+"config", v.Config)
vm.ExtVar(repo+"private", strconv.FormatBool(v.Private))
vm.ExtVar(repo+"visibility", v.Visibility)
vm.ExtVar(repo+"active", strconv.FormatBool(v.Active))
vm.ExtVar(repo+"trusted", strconv.FormatBool(v.Trusted))
vm.ExtVar(repo+"protected", strconv.FormatBool(v.Protected))
vm.ExtVar(repo+"ignore_forks", strconv.FormatBool(v.IgnoreForks))
vm.ExtVar(repo+"ignore_pull_requests", strconv.FormatBool(v.IgnorePulls))
}
func fromMap(m map[string]string, vm *jsonnet.VM) {
for k, v := range m {
vm.ExtVar(build+param+k, v)
}
}

View file

@ -0,0 +1,94 @@
package jsonnet
import (
"io/ioutil"
"testing"
"github.com/drone/drone/core"
)
func TestParse(t *testing.T) {
before, err := ioutil.ReadFile("../testdata/input.jsonnet")
if err != nil {
t.Error(err)
return
}
after, err := ioutil.ReadFile("../testdata/input.jsonnet.golden")
if err != nil {
t.Error(err)
return
}
req := &core.ConvertArgs{
Build: &core.Build{
After: "3d21ec53a331a6f037a91c368710b99387d012c1",
},
Repo: &core.Repository{
Slug: "octocat/hello-world",
Config: ".drone.yml",
},
Config: &core.Config{},
}
template := &core.Template{
Name: "my_template.jsonnet",
Data: string(before),
}
templateData := map[string]interface{}{
"stepName": "my_step",
"image": "my_image",
"commands": "my_command",
}
req.Config.Data = string(before)
parsedFile, err := Parse(req, template, templateData)
if err != nil {
t.Error(err)
return
}
if want, got := parsedFile, string(after); want != got {
t.Errorf("Want %q got %q", want, got)
}
}
func TestParseJsonnetNotTemplateFile(t *testing.T) {
before, err := ioutil.ReadFile("../testdata/single.jsonnet")
if err != nil {
t.Error(err)
return
}
after, err := ioutil.ReadFile("../testdata/input.jsonnet.golden")
if err != nil {
t.Error(err)
return
}
req := &core.ConvertArgs{
Build: &core.Build{
After: "3d21ec53a331a6f037a91c368710b99387d012c1",
},
Repo: &core.Repository{
Slug: "octocat/hello-world",
Config: ".drone.jsonnet",
},
Config: &core.Config{},
}
req.Repo.Config = "plugin.jsonnet"
req.Config.Data = string(before)
parsedFile, err := Parse(req, nil, nil)
if err != nil {
t.Error(err)
return
}
if want, got := parsedFile, string(after); want != got {
t.Errorf("Want %q got %q", want, got)
}
}

View file

@ -34,16 +34,17 @@ const keyf = "%d|%s|%s|%s|%s|%s"
// This micro-optimization is intended for multi-pipeline
// projects that would otherwise covert the file for each
// pipeline execution.
func Memoize(base core.ConvertService) core.ConvertService {
func Memoize(base core.ConvertService, size int) core.ConvertService {
// simple cache prevents the same yaml file from being
// requested multiple times in a short period.
cache, _ := lru.New(10)
return &memoize{base: base, cache: cache}
return &memoize{base: base, cache: cache, size: size}
}
type memoize struct {
base core.ConvertService
cache *lru.Cache
size int
}
func (c *memoize) Convert(ctx context.Context, req *core.ConvertArgs) (*core.Config, error) {
@ -53,6 +54,11 @@ func (c *memoize) Convert(ctx context.Context, req *core.ConvertArgs) (*core.Con
return nil, nil
}
// the client can optionally disable cacheing.
if c.size == 0 {
return c.base.Convert(ctx, req)
}
// generate the key used to cache the converted file.
key := fmt.Sprintf(keyf,
req.Repo.ID,

View file

@ -24,6 +24,6 @@ import (
// This micro-optimization is intended for multi-pipeline
// projects that would otherwise covert the file for each
// pipeline execution.
func Memoize(base core.ConvertService) core.ConvertService {
func Memoize(base core.ConvertService, size int) core.ConvertService {
return new(noop)
}

View file

@ -30,7 +30,7 @@ func TestMemoize(t *testing.T) {
base := mock.NewMockConvertService(controller)
base.EXPECT().Convert(gomock.Any(), gomock.Any()).Return(args.Config, nil)
service := Memoize(base).(*memoize)
service := Memoize(base, 10).(*memoize)
_, err := service.Convert(noContext, args)
if err != nil {
t.Error(err)
@ -69,7 +69,7 @@ func TestMemoize_Tag(t *testing.T) {
base := mock.NewMockConvertService(controller)
base.EXPECT().Convert(gomock.Any(), gomock.Any()).Return(args.Config, nil)
service := Memoize(base).(*memoize)
service := Memoize(base, 10).(*memoize)
res, err := service.Convert(noContext, args)
if err != nil {
t.Error(err)
@ -93,7 +93,7 @@ func TestMemoize_Empty(t *testing.T) {
base := mock.NewMockConvertService(controller)
base.EXPECT().Convert(gomock.Any(), gomock.Any()).Return(args.Config, nil)
service := Memoize(base).(*memoize)
service := Memoize(base, 10).(*memoize)
res, err := service.Convert(noContext, args)
if err != nil {
t.Error(err)
@ -120,7 +120,7 @@ func TestMemoize_Nil(t *testing.T) {
base := mock.NewMockConvertService(controller)
base.EXPECT().Convert(gomock.Any(), gomock.Any()).Return(args.Config, nil)
service := Memoize(base).(*memoize)
service := Memoize(base, 10).(*memoize)
res, err := service.Convert(noContext, args)
if err != nil {
t.Error(err)
@ -147,7 +147,7 @@ func TestMemoize_Error(t *testing.T) {
base := mock.NewMockConvertService(controller)
base.EXPECT().Convert(gomock.Any(), gomock.Any()).Return(nil, want)
service := Memoize(base).(*memoize)
service := Memoize(base, 10).(*memoize)
_, err := service.Convert(noContext, args)
if err == nil {
t.Errorf("Expect error from base returned to caller")

View file

@ -0,0 +1,61 @@
// Copyright 2019 Drone IO, Inc.
//
// 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.
// +build !oss
package converter
import (
"context"
"strings"
"github.com/drone/drone/core"
"github.com/drone/drone/plugin/converter/starlark"
)
// Starlark returns a conversion service that converts the
// starlark file to a yaml file.
func Starlark(enabled bool) core.ConvertService {
return &starlarkPlugin{
enabled: enabled,
}
}
type starlarkPlugin struct {
enabled bool
}
func (p *starlarkPlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*core.Config, error) {
if p.enabled == false {
return nil, nil
}
// if the file extension is not jsonnet we can
// skip this plugin by returning zero values.
switch {
case strings.HasSuffix(req.Repo.Config, ".script"):
case strings.HasSuffix(req.Repo.Config, ".star"):
case strings.HasSuffix(req.Repo.Config, ".starlark"):
default:
return nil, nil
}
file, err := starlark.Parse(req, nil, nil)
if err != nil {
return nil, err
}
return &core.Config{
Data: file,
}, nil
}

View file

@ -38,18 +38,29 @@ import (
// TODO(bradrydzewski) add build parent
// TODO(bradrydzewski) add build timestamp
func createArgs(repo *core.Repository, build *core.Build) []starlark.Value {
func createArgs(repo *core.Repository, build *core.Build, input map[string]interface{}) []starlark.Value {
return []starlark.Value{
starlarkstruct.FromStringDict(
starlark.String("context"),
starlark.StringDict{
"repo": starlarkstruct.FromStringDict(starlark.String("repo"), fromRepo(repo)),
"build": starlarkstruct.FromStringDict(starlark.String("build"), fromBuild(build)),
"input": starlarkstruct.FromStringDict(starlark.String("input"), fromInput(input)),
},
),
}
}
func fromInput(input map[string]interface{}) starlark.StringDict {
out := map[string]starlark.Value{}
for k, v := range input {
if s, ok := v.(string); ok {
out[k] = starlark.String(s)
}
}
return out
}
func fromBuild(v *core.Build) starlark.StringDict {
return starlark.StringDict{
"event": starlark.String(v.Event),
@ -72,6 +83,7 @@ func fromBuild(v *core.Build) starlark.StringDict {
"author_email": starlark.String(v.AuthorEmail),
"author_avatar": starlark.String(v.AuthorAvatar),
"sender": starlark.String(v.Sender),
"debug": starlark.Bool(v.Debug),
"params": fromMap(v.Params),
}
}

View file

@ -16,11 +16,10 @@ package starlark
import (
"bytes"
"context"
"errors"
"strings"
"github.com/drone/drone/core"
"github.com/drone/drone/handler/api/errors"
"github.com/sirupsen/logrus"
"go.starlark.net/starlark"
)
@ -55,33 +54,7 @@ var (
ErrCannotLoad = errors.New("starlark: cannot load external scripts")
)
// New returns a conversion service that converts the
// starlark file to a yaml file.
func New(enabled bool) core.ConvertService {
return &starlarkPlugin{
enabled: enabled,
}
}
type starlarkPlugin struct {
enabled bool
}
func (p *starlarkPlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*core.Config, error) {
if p.enabled == false {
return nil, nil
}
// if the file extension is not jsonnet we can
// skip this plugin by returning zero values.
switch {
case strings.HasSuffix(req.Repo.Config, ".script"):
case strings.HasSuffix(req.Repo.Config, ".star"):
case strings.HasSuffix(req.Repo.Config, ".starlark"):
default:
return nil, nil
}
func Parse(req *core.ConvertArgs, template *core.Template, templateData map[string]interface{}) (string, error) {
thread := &starlark.Thread{
Name: "drone",
Load: noLoad,
@ -92,9 +65,19 @@ func (p *starlarkPlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*c
}).Traceln(msg)
},
}
globals, err := starlark.ExecFile(thread, req.Repo.Config, []byte(req.Config.Data), nil)
var starlarkFile string
var starlarkFileName string
if template != nil {
starlarkFile = template.Data
starlarkFileName = template.Name
} else {
starlarkFile = req.Config.Data
starlarkFileName = req.Repo.Config
}
globals, err := starlark.ExecFile(thread, starlarkFileName, starlarkFile, nil)
if err != nil {
return nil, err
return "", err
}
// find the main method in the starlark script and
@ -102,16 +85,16 @@ func (p *starlarkPlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*c
// is invalid.
mainVal, ok := globals["main"]
if !ok {
return nil, ErrMainMissing
return "", ErrMainMissing
}
main, ok := mainVal.(starlark.Callable)
if !ok {
return nil, ErrMainInvalid
return "", ErrMainInvalid
}
// create the input args and invoke the main method
// using the input args.
args := createArgs(req.Repo, req.Build)
args := createArgs(req.Repo, req.Build, templateData)
// set the maximum number of operations in the script. this
// mitigates long running scripts.
@ -120,7 +103,7 @@ func (p *starlarkPlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*c
// execute the main method in the script.
mainVal, err = starlark.Call(thread, main, args, nil)
if err != nil {
return nil, err
return "", err
}
buf := new(bytes.Buffer)
@ -131,27 +114,24 @@ func (p *starlarkPlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*c
buf.WriteString(separator)
buf.WriteString(newline)
if err := write(buf, item); err != nil {
return nil, err
return "", err
}
buf.WriteString(newline)
}
case *starlark.Dict:
if err := write(buf, v); err != nil {
return nil, err
return "", err
}
default:
return nil, ErrMainReturn
return "", ErrMainReturn
}
// this is a temporary workaround until we
// implement a LimitWriter.
if b := buf.Bytes(); len(b) > limit {
return nil, ErrMaximumSize
return "", ErrMaximumSize
}
return &core.Config{
Data: buf.String(),
}, nil
return buf.String(), nil
}
func noLoad(_ *starlark.Thread, _ string) (starlark.StringDict, error) {

View file

@ -15,17 +15,24 @@
package starlark
import (
"context"
"io/ioutil"
"testing"
"github.com/drone/drone/core"
)
var noContext = context.Background()
func TestParseStarlark(t *testing.T) {
before, err := ioutil.ReadFile("../testdata/starlark.input.star")
if err != nil {
t.Error(err)
return
}
func TestConvert(t *testing.T) {
plugin := New(true)
after, err := ioutil.ReadFile("../testdata/starlark.input.star.golden")
if err != nil {
t.Error(err)
return
}
req := &core.ConvertArgs{
Build: &core.Build{
@ -37,54 +44,38 @@ func TestConvert(t *testing.T) {
},
Config: &core.Config{},
}
config, err := plugin.Convert(noContext, req)
if err != nil {
t.Error(err)
return
}
if config != nil {
t.Error("Want nil config when configuration is not starlark file")
return
template := &core.Template{
Name: "my_template.star",
Data: string(before),
}
before, err := ioutil.ReadFile("testdata/single.star")
if err != nil {
t.Error(err)
return
}
after, err := ioutil.ReadFile("testdata/single.star.golden")
if err != nil {
t.Error(err)
return
templateData := map[string]interface{}{
"stepName": "my_step",
"image": "my_image",
"commands": "my_command",
}
req.Repo.Config = "single.star"
req.Config.Data = string(before)
config, err = plugin.Convert(noContext, req)
parsedFile, err := Parse(req, template, templateData)
if err != nil {
t.Error(err)
return
}
if config == nil {
t.Error("Want non-nil configuration")
return
}
if want, got := config.Data, string(after); want != got {
if want, got := parsedFile, string(after); want != got {
t.Errorf("Want %q got %q", want, got)
}
}
// this test verifies the starlark file can generate a multi-document
// yaml file that defines multiple pipelines.
func TestConvert_Multi(t *testing.T) {
before, err := ioutil.ReadFile("testdata/multi.star")
func TestParseStarlarkNotTemplateFile(t *testing.T) {
before, err := ioutil.ReadFile("../testdata/single.star")
if err != nil {
t.Error(err)
return
}
after, err := ioutil.ReadFile("testdata/multi.star.golden")
after, err := ioutil.ReadFile("../testdata/single.star.golden")
if err != nil {
t.Error(err)
return
@ -98,63 +89,19 @@ func TestConvert_Multi(t *testing.T) {
Slug: "octocat/hello-world",
Config: ".drone.star",
},
Config: &core.Config{
Data: string(before),
},
Config: &core.Config{},
}
plugin := New(true)
config, err := plugin.Convert(noContext, req)
req.Repo.Config = "plugin.starlark.star"
req.Config.Data = string(before)
parsedFile, err := Parse(req, nil, nil)
if err != nil {
t.Error(err)
return
}
config, err = plugin.Convert(noContext, req)
if err != nil {
t.Error(err)
return
}
if config == nil {
t.Error("Want non-nil configuration")
return
}
if want, got := config.Data, string(after); want != got {
if want, got := parsedFile, string(after); want != got {
t.Errorf("Want %q got %q", want, got)
}
}
// this test verifies the plugin is skipped when it has
// not been explicitly enabled.
func TestConvert_Skip(t *testing.T) {
plugin := New(false)
config, err := plugin.Convert(noContext, nil)
if err != nil {
t.Error(err)
return
}
if config != nil {
t.Errorf("Expect nil config returned when plugin disabled")
}
}
// this test verifies the plugin is skipped when the config
// file extension is not a starlark extension.
func TestConvert_SkipYaml(t *testing.T) {
req := &core.ConvertArgs{
Repo: &core.Repository{
Config: ".drone.yaml",
},
}
plugin := New(true)
config, err := plugin.Convert(noContext, req)
if err != nil {
t.Error(err)
return
}
if config != nil {
t.Errorf("Expect nil config returned for non-starlark files")
}
}

View file

@ -12,6 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package landingpage
// +build oss
//go:generate togo http -package landingpage -output dist_gen.go
package converter
import "github.com/drone/drone/core"
func Starlark(enabled bool) core.ConvertService {
return new(noop)
}

View file

@ -0,0 +1,157 @@
// Copyright 2019 Drone IO, Inc.
//
// 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 converter
import (
"io/ioutil"
"testing"
"github.com/drone/drone/core"
)
func TestStarlarkConvert(t *testing.T) {
plugin := Starlark(true)
req := &core.ConvertArgs{
Build: &core.Build{
After: "3d21ec53a331a6f037a91c368710b99387d012c1",
},
Repo: &core.Repository{
Slug: "octocat/hello-world",
Config: ".drone.yml",
},
Config: &core.Config{},
}
config, err := plugin.Convert(noContext, req)
if err != nil {
t.Error(err)
return
}
if config != nil {
t.Error("Want nil config when configuration is not starlark file")
return
}
before, err := ioutil.ReadFile("testdata/single.star")
if err != nil {
t.Error(err)
return
}
after, err := ioutil.ReadFile("testdata/single.star.golden")
if err != nil {
t.Error(err)
return
}
req.Repo.Config = "single.star"
req.Config.Data = string(before)
config, err = plugin.Convert(noContext, req)
if err != nil {
t.Error(err)
return
}
if config == nil {
t.Error("Want non-nil configuration")
return
}
if want, got := config.Data, string(after); want != got {
t.Errorf("Want %q got %q", want, got)
}
}
// this test verifies the starlark file can generate a multi-document
// yaml file that defines multiple pipelines.
func TestConvert_Multi(t *testing.T) {
before, err := ioutil.ReadFile("testdata/multi.star")
if err != nil {
t.Error(err)
return
}
after, err := ioutil.ReadFile("testdata/multi.star.golden")
if err != nil {
t.Error(err)
return
}
req := &core.ConvertArgs{
Build: &core.Build{
After: "3d21ec53a331a6f037a91c368710b99387d012c1",
},
Repo: &core.Repository{
Slug: "octocat/hello-world",
Config: ".drone.star",
},
Config: &core.Config{
Data: string(before),
},
}
plugin := Starlark(true)
config, err := plugin.Convert(noContext, req)
if err != nil {
t.Error(err)
return
}
config, err = plugin.Convert(noContext, req)
if err != nil {
t.Error(err)
return
}
if config == nil {
t.Error("Want non-nil configuration")
return
}
if want, got := config.Data, string(after); want != got {
t.Errorf("Want %q got %q", want, got)
}
}
// this test verifies the plugin is skipped when it has
// not been explicitly enabled.
func TestConvert_Skip(t *testing.T) {
plugin := Starlark(false)
config, err := plugin.Convert(noContext, nil)
if err != nil {
t.Error(err)
return
}
if config != nil {
t.Errorf("Expect nil config returned when plugin disabled")
}
}
// this test verifies the plugin is skipped when the config
// file extension is not a starlark extension.
func TestConvert_SkipYaml(t *testing.T) {
req := &core.ConvertArgs{
Repo: &core.Repository{
Config: ".drone.yaml",
},
}
plugin := Starlark(true)
config, err := plugin.Convert(noContext, req)
if err != nil {
t.Error(err)
return
}
if config != nil {
t.Errorf("Expect nil config returned for non-starlark files")
}
}

View file

@ -0,0 +1,98 @@
// Copyright 2019 Drone IO, Inc.
//
// 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.
// +build !oss
package converter
import (
"context"
"database/sql"
"errors"
"regexp"
"strings"
"github.com/drone/drone/core"
"github.com/drone/drone/plugin/converter/jsonnet"
"github.com/drone/drone/plugin/converter/starlark"
"gopkg.in/yaml.v2"
)
var (
// templateFileRE regex to verifying kind is template.
templateFileRE = regexp.MustCompile("^kind:\\s+template+\\n")
ErrTemplateNotFound = errors.New("template converter: template name given not found")
ErrTemplateSyntaxErrors = errors.New("template converter: there is a problem with the yaml file provided")
)
func Template(templateStore core.TemplateStore) core.ConvertService {
return &templatePlugin{
templateStore: templateStore,
}
}
type templatePlugin struct {
templateStore core.TemplateStore
}
func (p *templatePlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*core.Config, error) {
// check type is yaml
if strings.HasSuffix(req.Repo.Config, ".yml") == false {
return nil, nil
}
// check kind is template
if templateFileRE.MatchString(req.Config.Data) == false {
return nil, nil
}
// map to templateArgs
var templateArgs core.TemplateArgs
err := yaml.Unmarshal([]byte(req.Config.Data), &templateArgs)
if err != nil {
return nil, ErrTemplateSyntaxErrors
}
// get template from db
template, err := p.templateStore.FindName(ctx, templateArgs.Load, req.Repo.Namespace)
if err == sql.ErrNoRows {
return nil, ErrTemplateNotFound
}
if err != nil {
return nil, err
}
// Check if file is of type Starlark
if strings.HasSuffix(templateArgs.Load, ".script") ||
strings.HasSuffix(templateArgs.Load, ".star") ||
strings.HasSuffix(templateArgs.Load, ".starlark") {
file, err := starlark.Parse(req, template, templateArgs.Data)
if err != nil {
return nil, err
}
return &core.Config{
Data: file,
}, nil
}
// Check if the file is of type Jsonnet
if strings.HasSuffix(templateArgs.Load, ".jsonnet") {
file, err := jsonnet.Parse(req, template, templateArgs.Data)
if err != nil {
return nil, err
}
return &core.Config{
Data: file,
}, nil
}
return nil, nil
}

View file

@ -0,0 +1,37 @@
// Copyright 2019 Drone IO, Inc.
//
// 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.
// +build oss
package converter
import (
"context"
"github.com/drone/drone/core"
)
func Template(templateStore core.TemplateStore) core.ConvertService {
return &templatePlugin{
templateStore: templateStore,
}
}
type templatePlugin struct {
templateStore core.TemplateStore
}
func (p *templatePlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*core.Config, error) {
return nil, nil
}

View file

@ -0,0 +1,239 @@
// Copyright 2019 Drone IO, Inc.
//
// 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 converter
import (
"io/ioutil"
"testing"
"github.com/drone/drone/core"
"github.com/drone/drone/mock"
"github.com/golang/mock/gomock"
)
func TestTemplatePluginConvertStarlark(t *testing.T) {
templateArgs, err := ioutil.ReadFile("testdata/starlark.template.yml")
if err != nil {
t.Error(err)
return
}
req := &core.ConvertArgs{
Build: &core.Build{
After: "3d21ec53a331a6f037a91c368710b99387d012c1",
},
Repo: &core.Repository{
Slug: "octocat/hello-world",
Config: ".drone.yml",
Namespace: "octocat",
},
Config: &core.Config{
Data: string(templateArgs),
},
}
beforeInput, err := ioutil.ReadFile("testdata/starlark.input.star")
if err != nil {
t.Error(err)
return
}
after, err := ioutil.ReadFile("testdata/starlark.input.star.golden")
if err != nil {
t.Error(err)
return
}
template := &core.Template{
Name: "plugin.starlark",
Data: string(beforeInput),
Namespace: "octocat",
}
controller := gomock.NewController(t)
defer controller.Finish()
templates := mock.NewMockTemplateStore(controller)
templates.EXPECT().FindName(gomock.Any(), template.Name, req.Repo.Namespace).Return(template, nil)
plugin := Template(templates)
config, err := plugin.Convert(noContext, req)
if err != nil {
t.Error(err)
return
}
if config == nil {
t.Error("Want non-nil configuration")
return
}
if want, got := config.Data, string(after); want != got {
t.Errorf("Want %q got %q", want, got)
}
}
func TestTemplatePluginConvertNotYamlFile(t *testing.T) {
plugin := Template(nil)
req := &core.ConvertArgs{
Build: &core.Build{
After: "3d21ec53a331a6f037a91c368710b99387d012c1",
},
Repo: &core.Repository{
Slug: "octocat/hello-world",
Config: ".drone.star",
},
Config: &core.Config{},
}
config, err := plugin.Convert(noContext, req)
if err != nil {
t.Error(err)
return
}
if config != nil {
t.Errorf("Expect nil config returned for non-starlark files")
}
}
func TestTemplatePluginConvertDroneFileTypePipeline(t *testing.T) {
args, err := ioutil.ReadFile("testdata/drone.yml")
if err != nil {
t.Error(err)
return
}
plugin := Template(nil)
req := &core.ConvertArgs{
Build: &core.Build{
After: "3d21ec53a331a6f037a91c368710b99387d012c1",
},
Repo: &core.Repository{
Slug: "octocat/hello-world",
Config: ".drone.yml",
},
Config: &core.Config{Data: string(args)},
}
config, err := plugin.Convert(noContext, req)
if err != nil {
t.Error(err)
return
}
if config != nil {
t.Errorf("Expect nil config returned for non-starlark files")
}
}
func TestTemplatePluginConvertTemplateNotFound(t *testing.T) {
templateArgs, err := ioutil.ReadFile("testdata/starlark.template.yml")
if err != nil {
t.Error(err)
return
}
req := &core.ConvertArgs{
Build: &core.Build{
After: "3d21ec53a331a6f037a91c368710b99387d012c1",
},
Repo: &core.Repository{
Slug: "octocat/hello-world",
Config: ".drone.yml",
Namespace: "octocat",
},
Config: &core.Config{Data: string(templateArgs)},
}
controller := gomock.NewController(t)
defer controller.Finish()
template := &core.Template{
Name: "plugin.starlark",
Data: "",
}
templates := mock.NewMockTemplateStore(controller)
templates.EXPECT().FindName(gomock.Any(), template.Name, req.Repo.Namespace).Return(nil, nil)
plugin := Template(templates)
config, err := plugin.Convert(noContext, req)
if config != nil {
t.Errorf("template converter: template name given not found")
}
}
func TestTemplatePluginConvertJsonnet(t *testing.T) {
templateArgs, err := ioutil.ReadFile("testdata/jsonnet.template.yml")
if err != nil {
t.Error(err)
return
}
req := &core.ConvertArgs{
Build: &core.Build{
After: "3d21ec53a331a6f037a91c368710b99387d012c1",
},
Repo: &core.Repository{
Slug: "octocat/hello-world",
Config: ".drone.yml",
Namespace: "octocat",
},
Config: &core.Config{
Data: string(templateArgs),
},
}
beforeInput, err := ioutil.ReadFile("testdata/input.jsonnet")
if err != nil {
t.Error(err)
return
}
after, err := ioutil.ReadFile("testdata/input.jsonnet.golden")
if err != nil {
t.Error(err)
return
}
template := &core.Template{
Name: "plugin.jsonnet",
Data: string(beforeInput),
Namespace: "octocat",
}
controller := gomock.NewController(t)
defer controller.Finish()
templates := mock.NewMockTemplateStore(controller)
templates.EXPECT().FindName(gomock.Any(), template.Name, req.Repo.Namespace).Return(template, nil)
plugin := Template(templates)
config, err := plugin.Convert(noContext, req)
if err != nil {
t.Error(err)
return
}
if config == nil {
t.Error("Want non-nil configuration")
return
}
if want, got := config.Data, string(after); want != got {
t.Errorf("Want %q got %q", want, got)
}
}

6
plugin/converter/testdata/drone.yml vendored Normal file
View file

@ -0,0 +1,6 @@
kind: pipeline
load: plugin.starlark
data:
stepName: my_step
image: my_image
commands: my_command

18
plugin/converter/testdata/input.jsonnet vendored Normal file
View file

@ -0,0 +1,18 @@
local stepName = std.extVar("input.stepName");
local image = std.extVar("input.image");
local commands = std.extVar("input.commands");
{
"kind": "pipeline",
"type": "docker",
"name": "default",
"steps": [
{
"name": stepName,
"image": image,
"commands": [
commands
]
}
]
}

View file

@ -0,0 +1,15 @@
---
{
"kind": "pipeline",
"name": "default",
"steps": [
{
"commands": [
"my_command"
],
"image": "my_image",
"name": "my_step"
}
],
"type": "docker"
}

View file

@ -0,0 +1,6 @@
kind: template
load: plugin.jsonnet
data:
stepName: my_step
image: my_image
commands: my_command

View file

@ -0,0 +1,14 @@
{
"kind": "pipeline",
"name": "default",
"steps": [
{
"commands": [
"my_command"
],
"image": "my_image",
"name": "my_step"
}
],
"type": "docker"
}

View file

@ -0,0 +1,14 @@
def main(ctx):
return {
"kind": "pipeline",
"name": "build",
"steps": [
{
"name": ctx.input.stepName,
"image": ctx.input.image,
"commands": [
ctx.input.commands
]
}
]
}

View file

@ -0,0 +1 @@
{"kind": "pipeline", "name": "build", "steps": [{"name": "my_step", "image": "my_image", "commands": ["my_command"]}]}

View file

@ -0,0 +1,6 @@
kind: template
load: plugin.starlark
data:
stepName: my_step
image: my_image
commands: my_command

View file

@ -28,7 +28,7 @@ import (
)
// Encrypted returns a new encrypted registry credentials
// provider that sournces credentials from the encrypted strings
// provider that sources credentials from the encrypted strings
// in the yaml file.
func Encrypted() core.RegistryService {
return new(encrypted)

View file

@ -67,7 +67,7 @@ func TestEndpointSource_Err(t *testing.T) {
service := EndpointSource("https://company.com/auths", "GMEuUHQfmrMRsseWxi9YlIeBtn9lm6im", false)
_, err := service.List(noContext, &core.RegistryArgs{Repo: &core.Repository{}, Build: &core.Build{}})
if err == nil {
t.Errorf("Expect http.Reponse error")
t.Errorf("Expect http.Response error")
} else if err.Error() != "Not Found" {
t.Errorf("Expect Not Found error")
}

View file

@ -147,7 +147,7 @@ func (q *queue) signal(ctx context.Context) error {
continue
}
// if the system defines concurrencly limits
// if the system defines concurrency limits
// per repository we need to make sure those limits
// are not exceeded before proceeding.
if shouldThrottle(item, items, item.LimitRepo) == true {
@ -266,7 +266,8 @@ func withinLimits(stage *core.Stage, siblings []*core.Stage) bool {
if sibling.Name != stage.Name {
continue
}
if sibling.ID < stage.ID {
if sibling.ID < stage.ID ||
sibling.Status == core.StatusRunning {
count++
}
}
@ -274,7 +275,7 @@ func withinLimits(stage *core.Stage, siblings []*core.Stage) bool {
}
func shouldThrottle(stage *core.Stage, siblings []*core.Stage, limit int) bool {
// if no throttle limit is defined (defualt) then
// if no throttle limit is defined (default) then
// return false to indicate no throttling is needed.
if limit == 0 {
return false

View file

@ -115,42 +115,6 @@ func TestQueuePush(t *testing.T) {
}
}
func TestWithinLimits(t *testing.T) {
tests := []struct {
ID int64
RepoID int64
Name string
Limit int
Want bool
}{
{Want: true, ID: 1, RepoID: 1, Name: "foo"},
{Want: true, ID: 2, RepoID: 2, Name: "bar", Limit: 1},
{Want: true, ID: 3, RepoID: 1, Name: "bar", Limit: 1},
{Want: false, ID: 4, RepoID: 1, Name: "bar", Limit: 1},
{Want: false, ID: 5, RepoID: 1, Name: "bar", Limit: 1},
{Want: true, ID: 6, RepoID: 1, Name: "baz", Limit: 2},
{Want: true, ID: 7, RepoID: 1, Name: "baz", Limit: 2},
{Want: false, ID: 8, RepoID: 1, Name: "baz", Limit: 2},
{Want: false, ID: 9, RepoID: 1, Name: "baz", Limit: 2},
{Want: true, ID: 10, RepoID: 1, Name: "baz", Limit: 0},
}
var stages []*core.Stage
for _, test := range tests {
stages = append(stages, &core.Stage{
ID: test.ID,
RepoID: test.RepoID,
Name: test.Name,
Limit: test.Limit,
})
}
for i, test := range tests {
stage := stages[i]
if got, want := withinLimits(stage, stages), test.Want; got != want {
t.Errorf("Unexpectd results at index %d", i)
}
}
}
func TestMatchResource(t *testing.T) {
tests := []struct {
kinda, typea, kindb, typeb string
@ -184,7 +148,7 @@ func TestMatchResource(t *testing.T) {
for i, test := range tests {
got, want := matchResource(test.kinda, test.typea, test.kindb, test.typeb), test.want
if got != want {
t.Errorf("Unexpectd results at index %d", i)
t.Errorf("Unexpected results at index %d", i)
}
}
}
@ -229,7 +193,166 @@ func TestShouldThrottle(t *testing.T) {
for i, test := range tests {
stage := stages[i]
if got, want := shouldThrottle(stage, stages, stage.LimitRepo), test.Want; got != want {
t.Errorf("Unexpectd results at index %d", i)
t.Errorf("Unexpected results at index %d", i)
}
}
}
func TestWithinLimits(t *testing.T) {
tests := []struct {
result bool
stage *core.Stage
stages []*core.Stage
}{
// multiple stages executing for same repository and with same
// name, but no concurrency limits exist. expect true.
{
result: true,
stage: &core.Stage{
ID: 3, RepoID: 1, Name: "build", Limit: 0,
},
stages: []*core.Stage{
{ID: 1, RepoID: 1, Name: "build", Status: "running"},
{ID: 2, RepoID: 1, Name: "build", Status: "running"},
{ID: 3, RepoID: 1, Name: "build", Status: "pending"},
},
},
// stage with concurrency 1, no existing stages
// exist for same repository id. expect true.
{
result: true,
stage: &core.Stage{
ID: 3, RepoID: 2, Name: "build", Limit: 0,
},
stages: []*core.Stage{
{ID: 1, RepoID: 1, Name: "build", Status: "running"},
{ID: 2, RepoID: 1, Name: "build", Status: "running"},
{ID: 3, RepoID: 2, Name: "build", Status: "pending"},
},
},
// stage with concurrency 1, no existing stages
// exist for same stage name. expect true.
{
result: true,
stage: &core.Stage{
ID: 3, RepoID: 1, Name: "build", Limit: 0,
},
stages: []*core.Stage{
{ID: 1, RepoID: 1, Name: "test", Status: "running"},
{ID: 2, RepoID: 1, Name: "test", Status: "running"},
{ID: 3, RepoID: 1, Name: "build", Status: "pending"},
},
},
// single stage with concurrency 1, no existing stages
// exist. expect true.
{
result: true,
stage: &core.Stage{
ID: 1, RepoID: 1, Name: "build", Limit: 1,
},
stages: []*core.Stage{
{ID: 1, RepoID: 1, Name: "build", Status: "pending"},
},
},
// stage with concurrency 1, other named stages
// exist in the queue, but they come after this stage.
// expect true.
{
result: true,
stage: &core.Stage{
ID: 1, RepoID: 1, Name: "build", Limit: 1,
},
stages: []*core.Stage{
{ID: 1, RepoID: 1, Name: "build", Status: "pending"},
{ID: 2, RepoID: 1, Name: "build", Status: "pending"},
},
},
// stage with concurrency 1, however, stage with same
// repository and name is already executing. expect false.
{
result: false,
stage: &core.Stage{
ID: 2, RepoID: 1, Name: "build", Limit: 1,
},
stages: []*core.Stage{
{ID: 1, RepoID: 1, Name: "build", Status: "running"},
{ID: 2, RepoID: 1, Name: "build", Status: "pending"},
},
},
// stage with concurrency 2. one existing stage in the
// queue before this stage. expect true.
{
result: true,
stage: &core.Stage{
ID: 2, RepoID: 1, Name: "build", Limit: 2,
},
stages: []*core.Stage{
{ID: 1, RepoID: 1, Name: "build", Status: "running"},
{ID: 2, RepoID: 1, Name: "build", Status: "pending"},
{ID: 3, RepoID: 1, Name: "build", Status: "pending"},
},
},
// stage with concurrency 1. stages start out of order, and the
// second named stage starts before its predecessor. Its predecessor
// should not execute. expect false.
{
result: false,
stage: &core.Stage{
ID: 1, RepoID: 1, Name: "build", Limit: 1,
},
stages: []*core.Stage{
{ID: 1, RepoID: 1, Name: "build", Status: "pending"},
{ID: 2, RepoID: 1, Name: "build", Status: "running"},
},
},
}
for i, test := range tests {
if got, want := withinLimits(test.stage, test.stages), test.result; got != want {
t.Errorf("Unexpected results at index %d", i)
}
}
}
func TestWithinLimits_Old(t *testing.T) {
tests := []struct {
ID int64
RepoID int64
Name string
Limit int
Want bool
}{
{Want: true, ID: 1, RepoID: 1, Name: "foo"},
{Want: true, ID: 2, RepoID: 2, Name: "bar", Limit: 1},
{Want: true, ID: 3, RepoID: 1, Name: "bar", Limit: 1},
{Want: false, ID: 4, RepoID: 1, Name: "bar", Limit: 1},
{Want: false, ID: 5, RepoID: 1, Name: "bar", Limit: 1},
{Want: true, ID: 6, RepoID: 1, Name: "baz", Limit: 2},
{Want: true, ID: 7, RepoID: 1, Name: "baz", Limit: 2},
{Want: false, ID: 8, RepoID: 1, Name: "baz", Limit: 2},
{Want: false, ID: 9, RepoID: 1, Name: "baz", Limit: 2},
{Want: true, ID: 10, RepoID: 1, Name: "baz", Limit: 0},
}
var stages []*core.Stage
for _, test := range tests {
stages = append(stages, &core.Stage{
ID: test.ID,
RepoID: test.RepoID,
Name: test.Name,
Limit: test.Limit,
})
}
for i, test := range tests {
stage := stages[i]
if got, want := withinLimits(stage, stages), test.Want; got != want {
t.Errorf("Unexpected results at index %d", i)
}
}
}

0
scripts/build.sh Normal file → Executable file
View file

View file

@ -20,6 +20,7 @@ import (
"net/http"
"os"
"path/filepath"
"time"
"golang.org/x/crypto/acme/autocert"
"golang.org/x/sync/errgroup"
@ -36,6 +37,8 @@ type Server struct {
Handler http.Handler
}
const timeoutGracefulShutdown = 5 * time.Second
// ListenAndServe initializes a server to respond to HTTP network requests.
func (s Server) ListenAndServe(ctx context.Context) error {
if s.Acme {
@ -43,7 +46,11 @@ func (s Server) ListenAndServe(ctx context.Context) error {
} else if s.Key != "" {
return s.listenAndServeTLS(ctx)
}
return s.listenAndServe(ctx)
err := s.listenAndServe(ctx)
if err == http.ErrServerClosed {
err = nil
}
return err
}
func (s Server) listenAndServe(ctx context.Context) error {
@ -53,10 +60,12 @@ func (s Server) listenAndServe(ctx context.Context) error {
Handler: s.Handler,
}
g.Go(func() error {
select {
case <-ctx.Done():
return s1.Shutdown(ctx)
}
<-ctx.Done()
ctxShutdown, cancelFunc := context.WithTimeout(context.Background(), timeoutGracefulShutdown)
defer cancelFunc()
return s1.Shutdown(ctxShutdown)
})
g.Go(s1.ListenAndServe)
return g.Wait()
@ -80,12 +89,20 @@ func (s Server) listenAndServeTLS(ctx context.Context) error {
)
})
g.Go(func() error {
select {
case <-ctx.Done():
s1.Shutdown(ctx)
s2.Shutdown(ctx)
return nil
}
<-ctx.Done()
var gShutdown errgroup.Group
ctxShutdown, cancelFunc := context.WithTimeout(context.Background(), timeoutGracefulShutdown)
defer cancelFunc()
gShutdown.Go(func() error {
return s1.Shutdown(ctxShutdown)
})
gShutdown.Go(func() error {
return s2.Shutdown(ctxShutdown)
})
return gShutdown.Wait()
})
return g.Wait()
}
@ -118,12 +135,20 @@ func (s Server) listenAndServeAcme(ctx context.Context) error {
return s2.ListenAndServeTLS("", "")
})
g.Go(func() error {
select {
case <-ctx.Done():
s1.Shutdown(ctx)
s2.Shutdown(ctx)
return nil
}
<-ctx.Done()
var gShutdown errgroup.Group
ctxShutdown, cancelFunc := context.WithTimeout(context.Background(), timeoutGracefulShutdown)
defer cancelFunc()
gShutdown.Go(func() error {
return s1.Shutdown(ctxShutdown)
})
gShutdown.Go(func() error {
return s2.Shutdown(ctxShutdown)
})
return gShutdown.Wait()
})
return g.Wait()
}

View file

@ -69,7 +69,7 @@ func (r *Reaper) Start(ctx context.Context, dur time.Duration) error {
for {
select {
case <-ctx.Done():
return ctx.Err()
return nil
case <-ticker.C:
r.reap(ctx)
}

View file

@ -79,6 +79,7 @@ func (s *service) FindRef(ctx context.Context, user *core.User, repo, ref string
switch s.client.Driver {
case scm.DriverBitbucket:
case scm.DriverStash:
ref = scm.TrimRef(ref)
branch, _, err := s.client.Git.FindBranch(ctx, repo, ref) // wont work for a Tag
if err != nil {

View file

@ -49,7 +49,7 @@ type service struct {
func (s *service) Find(ctx context.Context, user *core.User, repo, commit, ref, path string) (*core.File, error) {
// TODO(gogs) ability to fetch a yaml by pull request ref.
// it is not currently possible to fetch the yaml
// configuation file from a pull request sha. This
// configuration file from a pull request sha. This
// workaround defaults to master.
if s.client.Driver == scm.DriverGogs &&
strings.HasPrefix(ref, "refs/pull") {

View file

@ -19,7 +19,7 @@ import (
"github.com/drone/go-scm/scm"
)
// merge is a helper function that mergest a subset of
// merge is a helper function that merges a subset of
// values from the source to the destination repository.
func merge(dst, src *core.Repository) {
dst.Namespace = src.Namespace

View file

@ -45,7 +45,7 @@ func TestGet_Token_QueryParam(t *testing.T) {
}
// This test verifies that a user is returned when a valid
// authorization token included in the Authorzation header.
// authorization token included in the Authorization header.
func TestGet_Token_Header(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()

View file

@ -14,6 +14,7 @@ import (
"github.com/drone/drone/store/repos"
"github.com/drone/drone/store/shared/db"
"github.com/drone/drone/store/shared/db/dbtest"
"github.com/drone/drone/store/shared/encrypt"
"github.com/drone/drone/store/user"
)
@ -330,7 +331,8 @@ func testBatchDuplicateRename(
}
func seedUser(db *db.DB) (*core.User, error) {
enc, _ := encrypt.New("")
out := &core.User{Login: "octocat"}
err := user.New(db).Create(noContext, out)
err := user.New(db, enc).Create(noContext, out)
return out, err
}

View file

@ -14,6 +14,7 @@ import (
"github.com/drone/drone/store/repos"
"github.com/drone/drone/store/shared/db"
"github.com/drone/drone/store/shared/db/dbtest"
"github.com/drone/drone/store/shared/encrypt"
"github.com/drone/drone/store/user"
)
@ -389,7 +390,8 @@ func testBatchDuplicateRename(
}
func seedUser(db *db.DB) (*core.User, error) {
enc, _ := encrypt.New("")
out := &core.User{Login: "octocat"}
err := user.New(db).Create(noContext, out)
err := user.New(db, enc).Create(noContext, out)
return out, err
}

View file

@ -9,9 +9,10 @@ import (
"database/sql"
"testing"
"github.com/drone/drone/store/shared/db/dbtest"
"github.com/drone/drone/core"
"github.com/drone/drone/store/repos"
"github.com/drone/drone/store/shared/db/dbtest"
"github.com/drone/drone/store/shared/encrypt"
"github.com/drone/drone/store/user"
)
@ -28,9 +29,12 @@ func TestPerms(t *testing.T) {
dbtest.Disconnect(conn)
}()
// no-op encrypter
enc, _ := encrypt.New("")
// seeds the database with a dummy user account.
auser := &core.User{Login: "spaceghost"}
users := user.New(conn)
users := user.New(conn, enc)
err = users.Create(noContext, auser)
if err != nil {
t.Error(err)

View file

@ -298,7 +298,7 @@ SELECT
,repo_secret
`
const queryColsBulds = queryCols + `
const queryColsBuilds = queryCols + `
,build_id
,build_repo_id
,build_trigger
@ -488,7 +488,7 @@ WHERE repo_id = :repo_id
// INNER JOIN perms ON perms.perm_repo_uid = repos.repo_uid
//
const queryRepoWithBuild = queryColsBulds + `
const queryRepoWithBuild = queryColsBuilds + `
FROM repos LEFT OUTER JOIN builds ON build_id = (
SELECT build_id FROM builds
WHERE builds.build_repo_id = repos.repo_id
@ -500,7 +500,7 @@ WHERE perms.perm_user_id = :user_id
ORDER BY repo_slug ASC
`
const queryRepoWithBuildPostgres = queryColsBulds + `
const queryRepoWithBuildPostgres = queryColsBuilds + `
FROM repos LEFT OUTER JOIN builds ON build_id = (
SELECT DISTINCT ON (build_repo_id) build_id FROM builds
WHERE builds.build_repo_id = repos.repo_id
@ -511,7 +511,7 @@ WHERE perms.perm_user_id = :user_id
ORDER BY repo_slug ASC
`
const queryRepoWithBuildAll = queryColsBulds + `
const queryRepoWithBuildAll = queryColsBuilds + `
FROM repos
INNER JOIN perms ON perms.perm_repo_uid = repos.repo_uid
INNER JOIN builds ON builds.build_repo_id = repos.repo_id
@ -520,7 +520,7 @@ ORDER BY build_id DESC
LIMIT 25;
`
const queryRepoWithBuildIncomplete = queryColsBulds + `
const queryRepoWithBuildIncomplete = queryColsBuilds + `
FROM repos
INNER JOIN builds ON builds.build_repo_id = repos.repo_id
WHERE EXISTS (

Some files were not shown because too many files have changed in this diff Show more