enable simple starlark scripts in core
This commit is contained in:
parent
4d6ad33e9b
commit
276f984db3
15 changed files with 573 additions and 79 deletions
|
@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
|
|||
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]
|
||||
### Added
|
||||
- support for starlark scripts in core.
|
||||
- support for executing pipelines in debug mode.
|
||||
|
||||
## [1.9.2]
|
||||
### Added
|
||||
- update go-scm dependency to fix
|
||||
|
|
|
@ -59,6 +59,7 @@ type (
|
|||
Docker Docker
|
||||
HTTP HTTP
|
||||
Jsonnet Jsonnet
|
||||
Starlark Starlark
|
||||
Logging Logging
|
||||
Prometheus Prometheus
|
||||
Proxy Proxy
|
||||
|
@ -137,6 +138,11 @@ type (
|
|||
Enabled bool `envconfig:"DRONE_JSONNET_ENABLED"`
|
||||
}
|
||||
|
||||
// Starlark configures the starlark plugin
|
||||
Starlark struct {
|
||||
Enabled bool `envconfig:"DRONE_STARLARK_ENABLED"`
|
||||
}
|
||||
|
||||
// Kubernetes provides kubernetes configuration
|
||||
Kubernetes struct {
|
||||
Enabled bool `envconfig:"DRONE_KUBERNETES_ENABLED"`
|
||||
|
|
|
@ -20,6 +20,7 @@ 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"
|
||||
|
@ -79,7 +80,9 @@ func provideConfigPlugin(client *scm.Client, contents core.FileService, conf spe
|
|||
func provideConvertPlugin(client *scm.Client, conf spec.Config) core.ConvertService {
|
||||
return converter.Combine(
|
||||
converter.Legacy(false),
|
||||
converter.Starlark(false),
|
||||
starlark.New(
|
||||
conf.Starlark.Enabled,
|
||||
),
|
||||
converter.Jsonnet(
|
||||
conf.Jsonnet.Enabled,
|
||||
),
|
||||
|
|
3
go.mod
3
go.mod
|
@ -20,7 +20,7 @@ require (
|
|||
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.7.2-0.20201028160627-427b8a85897c
|
||||
github.com/drone/go-scm v1.7.2-0.20201111225713-c0438b46084b
|
||||
github.com/drone/signal v1.0.0
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/go-chi/chi v3.3.3+incompatible
|
||||
|
@ -62,6 +62,7 @@ require (
|
|||
github.com/smartystreets/goconvey v1.6.4 // indirect
|
||||
github.com/ugorji/go v1.1.7 // indirect
|
||||
github.com/unrolled/secure v0.0.0-20181022170031-4b6b7cf51606
|
||||
go.starlark.net v0.0.0-20201118183435-e55f603d8c79
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58
|
||||
gopkg.in/ini.v1 v1.57.0 // indirect
|
||||
|
|
9
go.sum
9
go.sum
|
@ -48,6 +48,9 @@ github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx2
|
|||
github.com/buildkite/yaml v2.1.0+incompatible h1:xirI+ql5GzfikVNDmt+yeiXpf/v1Gt03qXTtT5WXdr8=
|
||||
github.com/buildkite/yaml v2.1.0+incompatible/go.mod h1:UoU8vbcwu1+vjZq01+KrpSeLBgQQIjL/H7Y6KwikUrI=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
|
@ -101,6 +104,8 @@ github.com/drone/go-scm v1.7.2-0.20201015134822-a014bb19b1df h1:CzwzLwv6h9HMJSph
|
|||
github.com/drone/go-scm v1.7.2-0.20201015134822-a014bb19b1df/go.mod h1:lXwfbyrIJwFFME5TpzavkwO2T5X8yBK6t6cve7g91x0=
|
||||
github.com/drone/go-scm v1.7.2-0.20201028160627-427b8a85897c h1:3Dv6guONE4nry6fvwEvFz/+pnC0tsMudkrz6CwRO3KM=
|
||||
github.com/drone/go-scm v1.7.2-0.20201028160627-427b8a85897c/go.mod h1:lXwfbyrIJwFFME5TpzavkwO2T5X8yBK6t6cve7g91x0=
|
||||
github.com/drone/go-scm v1.7.2-0.20201111225713-c0438b46084b h1:ivLeFPmHN+9sLMVAF7HvgvEglU5tzoqlzePLY5zKPo8=
|
||||
github.com/drone/go-scm v1.7.2-0.20201111225713-c0438b46084b/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=
|
||||
|
@ -374,6 +379,8 @@ github.com/unrolled/secure v0.0.0-20181022170031-4b6b7cf51606 h1:dU9yXzNi9rl6Mou
|
|||
github.com/unrolled/secure v0.0.0-20181022170031-4b6b7cf51606/go.mod h1:mnPT77IAdsi/kV7+Es7y+pXALeV3h7G6dQF6mNYjcLA=
|
||||
github.com/vinzenz/yaml v0.0.0-20170920082545-91409cdd725d h1:3wDi6J5APMqaHBVPuVd7RmHD2gRTfqbdcVSpCNoUWtk=
|
||||
github.com/vinzenz/yaml v0.0.0-20170920082545-91409cdd725d/go.mod h1:mb5taDqMnJiZNRQ3+02W2IFG+oEz1+dTuCXkp4jpkfo=
|
||||
go.starlark.net v0.0.0-20201118183435-e55f603d8c79 h1:JPjLPz44y2N9mkzh2N344kTk1Y4/V4yJAjTrXGmzv8I=
|
||||
go.starlark.net v0.0.0-20201118183435-e55f603d8c79/go.mod h1:5YFcFnRptTN+41758c2bMPiqpGg4zBfYji1IQz8wNFk=
|
||||
golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
|
@ -425,6 +432,8 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk=
|
||||
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/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=
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
// 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 converter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// convert the starlark file to yaml
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
return &core.Config{
|
||||
Data: buf.String(),
|
||||
}, nil
|
||||
}
|
109
plugin/converter/starlark/args.go
Normal file
109
plugin/converter/starlark/args.go
Normal file
|
@ -0,0 +1,109 @@
|
|||
// 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 starlark
|
||||
|
||||
import (
|
||||
"github.com/drone/drone/core"
|
||||
|
||||
"go.starlark.net/starlark"
|
||||
"go.starlark.net/starlarkstruct"
|
||||
)
|
||||
|
||||
// TODO(bradrydzewski) add repository id
|
||||
// TODO(bradrydzewski) add repository timeout
|
||||
// TODO(bradrydzewski) add repository counter
|
||||
// TODO(bradrydzewski) add repository created
|
||||
// TODO(bradrydzewski) add repository updated
|
||||
// TODO(bradrydzewski) add repository synced
|
||||
// TODO(bradrydzewski) add repository version
|
||||
|
||||
// TODO(bradrydzewski) add build id, will always be zero value
|
||||
// TODO(bradrydzewski) add build number, will always be zero value
|
||||
// TODO(bradrydzewski) add build started, will always be zero value
|
||||
// TODO(bradrydzewski) add build finished, will always be zero value
|
||||
// TODO(bradrydzewski) add build created, will always be zero value
|
||||
// TODO(bradrydzewski) add build updated, will always be zero value
|
||||
// TODO(bradrydzewski) add build parent
|
||||
// TODO(bradrydzewski) add build timestamp
|
||||
|
||||
func createArgs(repo *core.Repository, build *core.Build) []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)),
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func fromBuild(v *core.Build) starlark.StringDict {
|
||||
return starlark.StringDict{
|
||||
"event": starlark.String(v.Event),
|
||||
"action": starlark.String(v.Action),
|
||||
"cron": starlark.String(v.Cron),
|
||||
"environment": starlark.String(v.Deploy),
|
||||
"link": starlark.String(v.Link),
|
||||
"branch": starlark.String(v.Target),
|
||||
"source": starlark.String(v.Source),
|
||||
"before": starlark.String(v.Before),
|
||||
"after": starlark.String(v.After),
|
||||
"target": starlark.String(v.Target),
|
||||
"ref": starlark.String(v.Ref),
|
||||
"commit": starlark.String(v.After),
|
||||
"title": starlark.String(v.Title),
|
||||
"message": starlark.String(v.Message),
|
||||
"source_repo": starlark.String(v.Fork),
|
||||
"author_login": starlark.String(v.Author),
|
||||
"author_name": starlark.String(v.AuthorName),
|
||||
"author_email": starlark.String(v.AuthorEmail),
|
||||
"author_avatar": starlark.String(v.AuthorAvatar),
|
||||
"sender": starlark.String(v.Sender),
|
||||
"params": fromMap(v.Params),
|
||||
}
|
||||
}
|
||||
|
||||
func fromRepo(v *core.Repository) starlark.StringDict {
|
||||
return starlark.StringDict{
|
||||
"uid": starlark.String(v.UID),
|
||||
"name": starlark.String(v.Name),
|
||||
"namespace": starlark.String(v.Namespace),
|
||||
"slug": starlark.String(v.Slug),
|
||||
"git_http_url": starlark.String(v.HTTPURL),
|
||||
"git_ssh_url": starlark.String(v.SSHURL),
|
||||
"link": starlark.String(v.Link),
|
||||
"branch": starlark.String(v.Branch),
|
||||
"config": starlark.String(v.Config),
|
||||
"private": starlark.Bool(v.Private),
|
||||
"visibility": starlark.String(v.Visibility),
|
||||
"active": starlark.Bool(v.Active),
|
||||
"trusted": starlark.Bool(v.Trusted),
|
||||
"protected": starlark.Bool(v.Protected),
|
||||
"ignore_forks": starlark.Bool(v.IgnoreForks),
|
||||
"ignore_pull_requests": starlark.Bool(v.IgnorePulls),
|
||||
}
|
||||
}
|
||||
|
||||
func fromMap(m map[string]string) *starlark.Dict {
|
||||
dict := new(starlark.Dict)
|
||||
for k, v := range m {
|
||||
dict.SetKey(
|
||||
starlark.String(k),
|
||||
starlark.String(v),
|
||||
)
|
||||
}
|
||||
return dict
|
||||
}
|
159
plugin/converter/starlark/starlark.go
Normal file
159
plugin/converter/starlark/starlark.go
Normal file
|
@ -0,0 +1,159 @@
|
|||
// 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 starlark
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.starlark.net/starlark"
|
||||
)
|
||||
|
||||
const (
|
||||
separator = "---"
|
||||
newline = "\n"
|
||||
)
|
||||
|
||||
// limits generated configuration file size.
|
||||
const limit = 1000000
|
||||
|
||||
var (
|
||||
// ErrMainMissing indicates the starlark script is missing
|
||||
// the main method.
|
||||
ErrMainMissing = errors.New("starlark: missing main function")
|
||||
|
||||
// ErrMainInvalid indicates the starlark script defines a
|
||||
// global variable named main, however, it is not callable.
|
||||
ErrMainInvalid = errors.New("starlark: main must be a function")
|
||||
|
||||
// ErrMainReturn indicates the starlark script's main method
|
||||
// returns an invalid or unexpected type.
|
||||
ErrMainReturn = errors.New("starlark: main returns an invalid type")
|
||||
|
||||
// ErrMaximumSize indicates the starlark script generated a
|
||||
// file that exceeds the maximum allowed file size.
|
||||
ErrMaximumSize = errors.New("starlark: maximum file size exceeded")
|
||||
|
||||
// ErrCannotLoad indicates the starlark script is attempting to
|
||||
// load an external file which is currently restricted.
|
||||
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
|
||||
}
|
||||
|
||||
thread := &starlark.Thread{
|
||||
Name: "drone",
|
||||
Load: noLoad,
|
||||
Print: func(_ *starlark.Thread, msg string) {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"namespace": req.Repo.Namespace,
|
||||
"name": req.Repo.Name,
|
||||
}).Traceln(msg)
|
||||
},
|
||||
}
|
||||
globals, err := starlark.ExecFile(thread, req.Repo.Config, []byte(req.Config.Data), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// find the main method in the starlark script and
|
||||
// cast to a callable type. If not callable the script
|
||||
// is invalid.
|
||||
mainVal, ok := globals["main"]
|
||||
if !ok {
|
||||
return nil, ErrMainMissing
|
||||
}
|
||||
main, ok := mainVal.(starlark.Callable)
|
||||
if !ok {
|
||||
return nil, ErrMainInvalid
|
||||
}
|
||||
|
||||
// create the input args and invoke the main method
|
||||
// using the input args.
|
||||
args := createArgs(req.Repo, req.Build)
|
||||
|
||||
// set the maximum number of operations in the script. this
|
||||
// mitigates long running scripts.
|
||||
thread.SetMaxExecutionSteps(50000)
|
||||
|
||||
// execute the main method in the script.
|
||||
mainVal, err = starlark.Call(thread, main, args, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
switch v := mainVal.(type) {
|
||||
case *starlark.List:
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
item := v.Index(i)
|
||||
buf.WriteString(separator)
|
||||
buf.WriteString(newline)
|
||||
if err := write(buf, item); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.WriteString(newline)
|
||||
}
|
||||
case *starlark.Dict:
|
||||
if err := write(buf, v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, ErrMainReturn
|
||||
}
|
||||
|
||||
// this is a temporary workaround until we
|
||||
// implement a LimitWriter.
|
||||
if b := buf.Bytes(); len(b) > limit {
|
||||
return nil, ErrMaximumSize
|
||||
}
|
||||
|
||||
return &core.Config{
|
||||
Data: buf.String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func noLoad(_ *starlark.Thread, _ string) (starlark.StringDict, error) {
|
||||
return nil, ErrCannotLoad
|
||||
}
|
160
plugin/converter/starlark/starlark_test.go
Normal file
160
plugin/converter/starlark/starlark_test.go
Normal file
|
@ -0,0 +1,160 @@
|
|||
// 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 starlark
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
)
|
||||
|
||||
var noContext = context.Background()
|
||||
|
||||
func TestConvert(t *testing.T) {
|
||||
plugin := New(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 := New(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 := 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")
|
||||
}
|
||||
}
|
6
plugin/converter/starlark/testdata/multi.star
vendored
Normal file
6
plugin/converter/starlark/testdata/multi.star
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
def main(ctx):
|
||||
return [{
|
||||
'kind': 'pipeline',
|
||||
'type': 'docker',
|
||||
'name': 'default'
|
||||
}]
|
2
plugin/converter/starlark/testdata/multi.star.golden
vendored
Normal file
2
plugin/converter/starlark/testdata/multi.star.golden
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
---
|
||||
{"kind": "pipeline", "type": "docker", "name": "default"}
|
11
plugin/converter/starlark/testdata/single.star
vendored
Normal file
11
plugin/converter/starlark/testdata/single.star
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
def main(ctx):
|
||||
print(ctx.build)
|
||||
print(ctx.build.commit)
|
||||
print(ctx.repo)
|
||||
print(ctx.repo.namespace)
|
||||
print(ctx.repo.name)
|
||||
return {
|
||||
'kind': 'pipeline',
|
||||
'type': 'docker',
|
||||
'name': 'default'
|
||||
}
|
1
plugin/converter/starlark/testdata/single.star.golden
vendored
Normal file
1
plugin/converter/starlark/testdata/single.star.golden
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{"kind": "pipeline", "type": "docker", "name": "default"}
|
99
plugin/converter/starlark/write.go
Normal file
99
plugin/converter/starlark/write.go
Normal file
|
@ -0,0 +1,99 @@
|
|||
// 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 starlark
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"go.starlark.net/starlark"
|
||||
)
|
||||
|
||||
type writer interface {
|
||||
io.Writer
|
||||
io.ByteWriter
|
||||
io.StringWriter
|
||||
}
|
||||
|
||||
func write(out writer, v starlark.Value) error {
|
||||
if marshaler, ok := v.(json.Marshaler); ok {
|
||||
jsonData, err := marshaler.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Write(jsonData)
|
||||
return nil
|
||||
}
|
||||
|
||||
switch v := v.(type) {
|
||||
case starlark.NoneType:
|
||||
out.WriteString("null")
|
||||
case starlark.Bool:
|
||||
fmt.Fprintf(out, "%t", v)
|
||||
case starlark.Int:
|
||||
out.WriteString(v.String())
|
||||
case starlark.Float:
|
||||
fmt.Fprintf(out, "%g", v)
|
||||
case starlark.String:
|
||||
s := string(v)
|
||||
if isQuoteSafe(s) {
|
||||
fmt.Fprintf(out, "%q", s)
|
||||
} else {
|
||||
data, _ := json.Marshal(s)
|
||||
out.Write(data)
|
||||
}
|
||||
case starlark.Indexable:
|
||||
out.WriteByte('[')
|
||||
for i, n := 0, starlark.Len(v); i < n; i++ {
|
||||
if i > 0 {
|
||||
out.WriteString(", ")
|
||||
}
|
||||
if err := write(out, v.Index(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
out.WriteByte(']')
|
||||
case *starlark.Dict:
|
||||
out.WriteByte('{')
|
||||
for i, itemPair := range v.Items() {
|
||||
key := itemPair[0]
|
||||
value := itemPair[1]
|
||||
if i > 0 {
|
||||
out.WriteString(", ")
|
||||
}
|
||||
if err := write(out, key); err != nil {
|
||||
return err
|
||||
}
|
||||
out.WriteString(": ")
|
||||
if err := write(out, value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
out.WriteByte('}')
|
||||
default:
|
||||
return fmt.Errorf("value %s (type `%s') can't be converted to JSON", v.String(), v.Type())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isQuoteSafe(s string) bool {
|
||||
for _, r := range s {
|
||||
if r < 0x20 || r >= 0x10000 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// 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 (
|
||||
"github.com/drone/drone/core"
|
||||
)
|
||||
|
||||
// Starlark returns a conversion service that converts the
|
||||
// starlark file to a yaml file.
|
||||
func Starlark(enabled bool) core.ConvertService {
|
||||
return new(noop)
|
||||
}
|
Loading…
Reference in a new issue