enable simple starlark scripts in core

This commit is contained in:
Brad Rydzewski 2020-11-20 08:56:47 -05:00
parent 4d6ad33e9b
commit 276f984db3
15 changed files with 573 additions and 79 deletions

View file

@ -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

View file

@ -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"`

View file

@ -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
View file

@ -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
View file

@ -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=

View file

@ -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
}

View 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
}

View 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
}

View 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")
}
}

View file

@ -0,0 +1,6 @@
def main(ctx):
return [{
'kind': 'pipeline',
'type': 'docker',
'name': 'default'
}]

View file

@ -0,0 +1,2 @@
---
{"kind": "pipeline", "type": "docker", "name": "default"}

View 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'
}

View file

@ -0,0 +1 @@
{"kind": "pipeline", "type": "docker", "name": "default"}

View 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
}

View file

@ -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)
}