├── .drone.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── NOTICE ├── README.md ├── go.mod ├── go.sum ├── main.go ├── samples ├── complex.yml └── simple.yml └── yaml ├── build.go ├── build_test.go ├── compiler ├── clone.go ├── clone_test.go ├── compiler.go ├── convert.go ├── convert_test.go ├── dind.go ├── dind_test.go ├── encode.go ├── encode_test.go ├── image │ ├── image.go │ └── image_test.go ├── internal │ └── rand │ │ └── rand.go ├── script_posix.go ├── script_posix_test.go ├── script_win.go ├── script_win_test.go ├── skip.go ├── skip_test.go ├── step.go ├── transform │ ├── auths.go │ ├── auths_test.go │ ├── combine.go │ ├── combine_test.go │ ├── env.go │ ├── env_test.go │ ├── filter.go │ ├── filter_test.go │ ├── labels.go │ ├── labels_test.go │ ├── limits.go │ ├── limits_test.go │ ├── netrc.go │ ├── netrc_test.go │ ├── network.go │ ├── network_test.go │ ├── proxy.go │ ├── proxy_test.go │ ├── secret.go │ ├── secret_test.go │ ├── volume.go │ └── volume_test.go ├── workspace.go └── workspace_test.go ├── cond.go ├── cond_test.go ├── converter ├── bitbucket │ ├── config.go │ ├── config_test.go │ ├── convert.go │ ├── convert_test.go │ └── testdata │ │ ├── sample1.yaml │ │ ├── sample1.yaml.golden │ │ ├── sample2.yaml │ │ └── sample2.yaml.golden ├── circleci │ ├── config.go │ ├── docker.go │ ├── docker_test.go │ ├── run.go │ ├── run_test.go │ └── testdata │ │ ├── sample1.yml │ │ └── sample1.yml.golden ├── convert.go ├── convert_oss.go ├── convert_oss_test.go ├── convert_test.go ├── gitlab │ ├── config.go │ ├── convert.go │ ├── convert_test.go │ └── testdata │ │ ├── example1.yml │ │ ├── example1.yml.golden │ │ ├── example2.yml │ │ ├── example2.yml.golden │ │ ├── example3.yml │ │ ├── example3.yml.golden │ │ ├── example4.yml │ │ └── example4.yml.golden ├── internal │ ├── string_slice.go │ └── string_slice_test.go ├── legacy │ ├── convert.go │ ├── internal │ │ ├── config.go │ │ ├── config_test.go │ │ ├── constraint.go │ │ ├── container.go │ │ ├── container_test.go │ │ ├── secret.go │ │ ├── secret_test.go │ │ ├── slice_map.go │ │ ├── slice_map_test.go │ │ ├── string_slice.go │ │ ├── string_slice_test.go │ │ ├── testdata │ │ │ ├── branches.yml │ │ │ ├── branches.yml.golden │ │ │ ├── matrix_1.yml │ │ │ ├── matrix_1.yml.golden │ │ │ ├── matrix_2.yml │ │ │ ├── matrix_2.yml.golden │ │ │ ├── simple.yml │ │ │ ├── simple.yml.golden │ │ │ ├── tags.yml │ │ │ ├── tags.yml.golden │ │ │ ├── vault_1.yml │ │ │ ├── vault_1.yml.golden │ │ │ ├── vault_2.yml │ │ │ ├── vault_2.yml.golden │ │ │ ├── vault_3.yml │ │ │ └── vault_3.yml.golden │ │ ├── volume.go │ │ ├── volume_test.go │ │ └── yaml.go │ ├── match.go │ ├── match_test.go │ ├── matrix │ │ └── matrix.go │ └── testdata │ │ └── legacy.yml └── metadata.go ├── cron.go ├── cron_test.go ├── env.go ├── env_test.go ├── linter ├── config.go ├── config_test.go ├── linter.go ├── linter_test.go └── testdata │ ├── duplicate_name.yml │ ├── duplicate_step.yml │ ├── duplicate_step_service.yml │ ├── invalid_arch.yml │ ├── invalid_os.yml │ ├── missing_build_image.yml │ ├── missing_dep.yml │ ├── missing_image.yml │ ├── missing_name.yml │ ├── pipeline_device.yml │ ├── pipeline_dns.yml │ ├── pipeline_dns_search.yml │ ├── pipeline_extra_hosts.yml │ ├── pipeline_network_mode.yml │ ├── pipeline_port_host.yml │ ├── pipeline_privileged.yml │ ├── pipeline_volume_invalid_name.yml │ ├── service_device.yml │ ├── service_port_host.yml │ ├── simple.yml │ ├── volume_empty_dir.yml │ ├── volume_empty_dir_memory.yml │ ├── volume_host_path.yml │ └── volume_invalid_name.yml ├── manifest.go ├── manifest_test.go ├── param.go ├── param_test.go ├── parse.go ├── parse_test.go ├── pipeline.go ├── pipeline_test.go ├── port.go ├── port_test.go ├── pretty ├── container.go ├── container_test.go ├── cron.go ├── cron_test.go ├── pipeline.go ├── pipeline_test.go ├── pretty.go ├── pretty_test.go ├── secret.go ├── secret_test.go ├── signature.go ├── signature_test.go ├── testdata │ ├── cron.yml │ ├── cron.yml.golden │ ├── manifest.yml │ ├── manifest.yml.golden │ ├── pipeline.yml │ ├── pipeline.yml.golden │ ├── pipeline_build_long.yml │ ├── pipeline_build_long.yml.golden │ ├── pipeline_build_short.yml │ ├── pipeline_build_short.yml.golden │ ├── pipeline_clone_depth.yml │ ├── pipeline_clone_depth.yml.golden │ ├── pipeline_clone_disable.yml │ ├── pipeline_clone_disable.yml.golden │ ├── pipeline_clone_skip_verify.yml │ ├── pipeline_clone_skip_verify.yml.golden │ ├── pipeline_concurrency.yml │ ├── pipeline_concurrency.yml.golden │ ├── pipeline_depends.yml │ ├── pipeline_depends.yml.golden │ ├── pipeline_image_pull_secrets.yml │ ├── pipeline_image_pull_secrets.yml.golden │ ├── pipeline_node.yml │ ├── pipeline_node.yml.golden │ ├── pipeline_ports.yml │ ├── pipeline_ports.yml.golden │ ├── pipeline_push.yml │ ├── pipeline_push.yml.golden │ ├── pipeline_resources.yml │ ├── pipeline_resources.yml.golden │ ├── pipeline_settings.yml │ ├── pipeline_settings.yml.golden │ ├── pipeline_trigger.yml │ ├── pipeline_trigger.yml.golden │ ├── pipeline_volumes.yml │ ├── pipeline_volumes.yml.golden │ ├── pipeline_workspace.yml │ ├── pipeline_workspace.yml.golden │ ├── registry.yml │ ├── registry.yml.golden │ ├── secret.yml │ ├── secret.yml.golden │ ├── secret_get.yml │ ├── secret_get.yml.golden │ ├── services.yml │ ├── services.yml.golden │ ├── signature.yml │ └── signature.yml.golden ├── util.go ├── util_test.go ├── writer.go └── writer_test.go ├── push.go ├── push_test.go ├── registry.go ├── registry_test.go ├── secret.go ├── secret_test.go ├── signature.go ├── signature_test.go ├── signer ├── signer.go ├── signer_test.go └── testdata │ ├── invalid_signature.yml │ ├── missing_signature.yml │ └── signed.yml ├── testdata ├── cron.yml ├── cron.yml.golden ├── manifest.yml ├── manifest.yml.golden ├── pipeline.yml ├── pipeline.yml.golden ├── registry.yml ├── registry.yml.golden ├── secret.yml ├── secret.yml.golden ├── signature.yml └── signature.yml.golden ├── unit.go └── unit_test.go /.drone.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: test 11 | image: golang:1.11 12 | commands: 13 | - go vet ./... 14 | - go test ./... 15 | 16 | ... 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | NOTES* 2 | *.out 3 | *.env 4 | drone-yaml 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Drone.IO, Inc. 2 | 3 | The Drone Community Edition is licensed under the Apache License, 4 | Version 2.0 (the "Apache License"). You may obtain a copy of the 5 | Apache License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | The Drone Enterprise Edition is licensed under the Drone 10 | Non-Commercial License (the "Non-Commercial License"). A copy of 11 | the Non-Commercial License is provided below. 12 | 13 | ----------------------------------------------------------------- 14 | 15 | Drone Non-Commercial License 16 | 17 | Contributor: Drone.IO, Inc. 18 | 19 | Source Code: https://github.com/drone/drone-yaml 20 | 21 | This license lets you use and share this software for free, 22 | with a trial-length time limit on commercial use. Specifically: 23 | 24 | If you follow the rules below, you may do everything with this 25 | software that would otherwise infringe either the contributor's 26 | copyright in it, any patent claim the contributor can license 27 | that covers this software as of the contributor's latest 28 | contribution, or both. 29 | 30 | 1. You must limit use of this software in any manner primarily 31 | intended for or directed toward commercial advantage or 32 | private monetary compensation to a trial period of 32 33 | consecutive calendar days. This limit does not apply to use in 34 | developing feedback, modifications, or extensions that you 35 | contribute back to those giving this license. 36 | 37 | 2. Ensure everyone who gets a copy of this software from you, in 38 | source code or any other form, gets the text of this license 39 | and the contributor and source code lines above. 40 | 41 | 3. Do not make any legal claim against anyone for infringing any 42 | patent claim they would infringe by using this software alone, 43 | accusing this software, with or without changes, alone or as 44 | part of a larger application. 45 | 46 | You are excused for unknowingly breaking rule 1 if you stop 47 | doing anything requiring this license within 30 days of 48 | learning you broke the rule. 49 | 50 | **This software comes as is, without any warranty at all. As far 51 | as the law allows, the contributor will not be liable for any 52 | damages related to this software or this license, for any kind of 53 | legal claim.** 54 | 55 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Drone 2 | Copyright 2019 Drone.IO, Inc 3 | 4 | This product includes software developed at Drone.IO, Inc. 5 | (http://drone.io/). 6 | 7 | This product includes software developed by Docker, Inc. 8 | (https://www.docker.com/). 9 | 10 | This product includes software developed by Canonical Ltd. 11 | (https://www.canonical.com/). 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Package yaml provides a parser, linter, formatter and compiler for the [drone](https://github.com/drone/drone) configuration file format. 2 | 3 | Lint the yaml file: 4 | 5 | ```text 6 | $ drone-yaml lint samples/simple.yml 7 | ``` 8 | 9 | Format the yaml file: 10 | 11 | ```text 12 | $ drone-yaml fmt samples/simple.yml 13 | $ drone-yaml fmt samples/simple.yml --save 14 | ``` 15 | 16 | Sign the yaml file using a 32-bit secret key: 17 | 18 | ```text 19 | $ drone-yaml sign 642909eb4c3d47e33999235c0598353c samples/simple.yml 20 | $ drone-yaml sign 642909eb4c3d47e33999235c0598353c samples/simple.yml --save 21 | ``` 22 | 23 | Verify the yaml file signature: 24 | 25 | ```text 26 | $ drone-yaml verify 642909eb4c3d47e33999235c0598353c samples/simple.yml 27 | ``` 28 | 29 | Compile the yaml file: 30 | 31 | ```text 32 | $ drone-yaml compile samples/simple.yml samples/simple.json 33 | ``` 34 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/drone/drone-yaml 2 | 3 | require ( 4 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc // indirect 5 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect 6 | github.com/bmatcuk/doublestar v1.1.1 7 | github.com/buildkite/yaml v2.1.0+incompatible 8 | github.com/docker/distribution v2.7.1+incompatible 9 | github.com/docker/go-units v0.3.3 10 | github.com/drone/drone-runtime v1.0.7-0.20190729202838-87c84080f4a1 11 | github.com/ghodss/yaml v1.0.0 12 | github.com/google/go-cmp v0.2.0 13 | github.com/kr/pretty v0.1.0 // indirect 14 | github.com/sergi/go-diff v1.0.0 15 | github.com/stretchr/testify v1.3.0 // indirect 16 | github.com/vinzenz/yaml v0.0.0-20170920082545-91409cdd725d 17 | gopkg.in/alecthomas/kingpin.v2 v2.2.6 18 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /samples/complex.yml: -------------------------------------------------------------------------------- 1 | kind: pipeline 2 | name: build 3 | 4 | steps: 5 | - name: backend 6 | image: golang:1.11 7 | commands: 8 | - go build 9 | - go test -v 10 | 11 | - name: frontend 12 | image: node 13 | commands: 14 | - npm install 15 | - npm run test 16 | - npm run lint 17 | 18 | services: 19 | - name: redis 20 | image: redis:latest 21 | ports: 22 | - 6379 23 | volumes: 24 | - name: foo 25 | path: /bar 26 | 27 | volumes: 28 | - name: foo 29 | temp: {} 30 | 31 | --- 32 | kind: pipeline 33 | name: notify 34 | 35 | steps: 36 | - name: notify 37 | image: plugins/slack 38 | settings: 39 | room: general 40 | token: 41 | from_secret: token 42 | 43 | node: 44 | disk: ssd 45 | 46 | depends_on: 47 | - build 48 | -------------------------------------------------------------------------------- /samples/simple.yml: -------------------------------------------------------------------------------- 1 | kind: pipeline 2 | name: default 3 | 4 | steps: 5 | - name: backend 6 | image: golang:1.11 7 | commands: 8 | - go build 9 | - go test -v 10 | 11 | - name: frontend 12 | image: node 13 | commands: 14 | - npm install 15 | - npm run test 16 | - npm run lint 17 | 18 | services: 19 | - name: redis 20 | image: redis:latest 21 | ports: 22 | - 6379 23 | -------------------------------------------------------------------------------- /yaml/build.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package yaml 16 | 17 | type ( 18 | // Build configures a Docker build. 19 | Build struct { 20 | Args map[string]string `json:"args,omitempty"` 21 | CacheFrom []string `json:"cache_from,omitempty" yaml:"cache_from"` 22 | Context string `json:"context,omitempty"` 23 | Dockerfile string `json:"dockerfile,omitempty"` 24 | Image string `json:"image,omitempty"` 25 | Labels map[string]string `json:"labels,omitempty"` 26 | } 27 | 28 | // build is a tempoary type used to unmarshal 29 | // the Build struct when long format is used. 30 | build struct { 31 | Args map[string]string 32 | CacheFrom []string `yaml:"cache_from"` 33 | Context string 34 | Dockerfile string 35 | Image string 36 | Labels map[string]string 37 | } 38 | ) 39 | 40 | // UnmarshalYAML implements yaml unmarshalling. 41 | func (b *Build) UnmarshalYAML(unmarshal func(interface{}) error) error { 42 | d := new(build) 43 | err := unmarshal(&d.Image) 44 | if err != nil { 45 | err = unmarshal(d) 46 | } 47 | b.Args = d.Args 48 | b.CacheFrom = d.CacheFrom 49 | b.Context = d.Context 50 | b.Dockerfile = d.Dockerfile 51 | b.Labels = d.Labels 52 | b.Image = d.Image 53 | return err 54 | } 55 | -------------------------------------------------------------------------------- /yaml/build_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/buildkite/yaml" 11 | ) 12 | 13 | func TestBuild(t *testing.T) { 14 | tests := []struct { 15 | yaml string 16 | image string 17 | }{ 18 | { 19 | yaml: "bar", 20 | image: "bar", 21 | }, 22 | { 23 | yaml: "{ image: foo }", 24 | image: "foo", 25 | }, 26 | } 27 | for _, test := range tests { 28 | in := []byte(test.yaml) 29 | out := new(Build) 30 | err := yaml.Unmarshal(in, out) 31 | if err != nil { 32 | t.Error(err) 33 | return 34 | } 35 | if got, want := out.Image, test.image; got != want { 36 | t.Errorf("Want image %q, got %q", want, got) 37 | } 38 | } 39 | } 40 | 41 | func TestBuildError(t *testing.T) { 42 | in := []byte("[]") 43 | out := new(Build) 44 | err := yaml.Unmarshal(in, out) 45 | if err == nil { 46 | t.Errorf("Expect unmarshal error") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /yaml/compiler/convert.go: -------------------------------------------------------------------------------- 1 | // Copyright the Drone Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package compiler 16 | 17 | import ( 18 | "strings" 19 | 20 | "github.com/drone/drone-runtime/engine" 21 | "github.com/drone/drone-yaml/yaml" 22 | ) 23 | 24 | func toIgnoreErr(from *yaml.Container) bool { 25 | return strings.EqualFold(from.Failure, "ignore") 26 | } 27 | 28 | func toPorts(from *yaml.Container) []*engine.Port { 29 | var ports []*engine.Port 30 | for _, port := range from.Ports { 31 | ports = append(ports, toPort(port)) 32 | } 33 | return ports 34 | } 35 | 36 | func toPort(from *yaml.Port) *engine.Port { 37 | return &engine.Port{ 38 | Port: from.Port, 39 | Host: from.Host, 40 | Protocol: from.Protocol, 41 | } 42 | } 43 | 44 | func toPullPolicy(from *yaml.Container) engine.PullPolicy { 45 | switch strings.ToLower(from.Pull) { 46 | case "always": 47 | return engine.PullAlways 48 | case "if-not-exists": 49 | return engine.PullIfNotExists 50 | case "never": 51 | return engine.PullNever 52 | default: 53 | return engine.PullDefault 54 | } 55 | } 56 | 57 | func toRunPolicy(from *yaml.Container) engine.RunPolicy { 58 | onFailure := from.When.Status.Match("failure") && len(from.When.Status.Include) > 0 59 | onSuccess := from.When.Status.Match("success") 60 | switch { 61 | case onFailure && onSuccess: 62 | return engine.RunAlways 63 | case onFailure: 64 | return engine.RunOnFailure 65 | case onSuccess: 66 | return engine.RunOnSuccess 67 | default: 68 | return engine.RunNever 69 | } 70 | } 71 | 72 | func toResources(from *yaml.Container) *engine.Resources { 73 | if from.Resources == nil { 74 | return nil 75 | } 76 | return &engine.Resources{ 77 | Limits: toResourceObject(from.Resources.Limits), 78 | Requests: toResourceObject(from.Resources.Requests), 79 | } 80 | } 81 | 82 | func toResourceObject(from *yaml.ResourceObject) *engine.ResourceObject { 83 | if from == nil { 84 | return nil 85 | } 86 | return &engine.ResourceObject{ 87 | CPU: toCPUMillis(from.CPU), 88 | Memory: int64(from.Memory), 89 | } 90 | } 91 | 92 | func toCPUMillis(f float64) int64 { 93 | if f > 0 { 94 | f *= 1000 95 | } 96 | return int64(f) 97 | } 98 | -------------------------------------------------------------------------------- /yaml/compiler/dind.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package compiler 16 | 17 | import ( 18 | "github.com/drone/drone-yaml/yaml" 19 | "github.com/drone/drone-yaml/yaml/compiler/image" 20 | ) 21 | 22 | // DindFunc is a helper function that returns true 23 | // if a container image (specifically a plugin) is 24 | // a whitelisted dind container and should be executed 25 | // in privileged mode. 26 | func DindFunc(images []string) func(*yaml.Container) bool { 27 | return func(container *yaml.Container) bool { 28 | // privileged-by-default containers are only 29 | // enabled for plugins steps that do not define 30 | // commands, command, or entrypoint. 31 | if len(container.Commands) > 0 { 32 | return false 33 | } 34 | if len(container.Command) > 0 { 35 | return false 36 | } 37 | if len(container.Entrypoint) > 0 { 38 | return false 39 | } 40 | // if the container image matches any image 41 | // in the whitelist, return true. 42 | for _, img := range images { 43 | a := img 44 | b := container.Image 45 | if image.Match(a, b) { 46 | return true 47 | } 48 | } 49 | return false 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /yaml/compiler/dind_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package compiler 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/drone/drone-yaml/yaml" 11 | ) 12 | 13 | func TestDind(t *testing.T) { 14 | tests := []struct { 15 | container *yaml.Container 16 | privileged bool 17 | }{ 18 | { 19 | container: &yaml.Container{Image: "plugins/docker"}, 20 | privileged: true, 21 | }, 22 | { 23 | container: &yaml.Container{Image: "plugins/docker:latest"}, 24 | privileged: true, 25 | }, 26 | { 27 | container: &yaml.Container{Image: "plugins/docker:1"}, 28 | privileged: true, 29 | }, 30 | // no match 31 | { 32 | container: &yaml.Container{Image: "golang"}, 33 | privileged: false, 34 | }, 35 | // dind containers cannot set entrypoint or command 36 | { 37 | container: &yaml.Container{ 38 | Image: "plugins/docker", 39 | Command: []string{"docker run ..."}, 40 | }, 41 | privileged: false, 42 | }, 43 | { 44 | container: &yaml.Container{ 45 | Image: "plugins/docker", 46 | Entrypoint: []string{"docker run ..."}, 47 | }, 48 | privileged: false, 49 | }, 50 | // dind containers cannot set commands 51 | { 52 | container: &yaml.Container{ 53 | Image: "plugins/docker", 54 | Commands: []string{"docker run ..."}, 55 | }, 56 | privileged: false, 57 | }, 58 | } 59 | for i, test := range tests { 60 | images := []string{"plugins/docker", "plugins/ecr"} 61 | privileged := DindFunc(images)(test.container) 62 | if privileged != test.privileged { 63 | t.Errorf("Want privileged %v at index %d", test.privileged, i) 64 | } 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /yaml/compiler/encode.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package compiler 16 | 17 | import ( 18 | "encoding/base64" 19 | "strconv" 20 | "strings" 21 | 22 | "github.com/buildkite/yaml" 23 | json "github.com/ghodss/yaml" 24 | ) 25 | 26 | // helper funciton encodes an interface value as a string. 27 | // this function assumes all types were unmarshaled by the 28 | // yaml.v2 library. The yaml.v2 package only supports a 29 | // subset of primative types. 30 | func encode(v interface{}) string { 31 | switch v := v.(type) { 32 | case string: 33 | return v 34 | case bool: 35 | return strconv.FormatBool(v) 36 | case int: 37 | return strconv.Itoa(v) 38 | case float64: 39 | return strconv.FormatFloat(v, 'g', -1, 64) 40 | case []byte: 41 | return base64.StdEncoding.EncodeToString(v) 42 | case []interface{}: 43 | return encodeSlice(v) 44 | default: 45 | return encodeMap(v) 46 | } 47 | } 48 | 49 | // helper function encodes a parameter in map format. 50 | func encodeMap(v interface{}) string { 51 | yml, _ := yaml.Marshal(v) 52 | out, _ := json.YAMLToJSON(yml) 53 | return string(out) 54 | } 55 | 56 | // helper function encodes a parameter in slice format. 57 | func encodeSlice(v interface{}) string { 58 | out, _ := yaml.Marshal(v) 59 | 60 | in := []string{} 61 | err := yaml.Unmarshal(out, &in) 62 | if err == nil { 63 | return strings.Join(in, ",") 64 | } 65 | out, _ = json.YAMLToJSON(out) 66 | return string(out) 67 | } 68 | -------------------------------------------------------------------------------- /yaml/compiler/encode_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package compiler 6 | 7 | import "testing" 8 | 9 | func TestEncode(t *testing.T) { 10 | testdatum := []struct { 11 | data interface{} 12 | text string 13 | }{ 14 | { 15 | data: "foo", 16 | text: "foo", 17 | }, 18 | { 19 | data: true, 20 | text: "true", 21 | }, 22 | { 23 | data: 42, 24 | text: "42", 25 | }, 26 | { 27 | data: float64(42.424242), 28 | text: "42.424242", 29 | }, 30 | { 31 | data: []interface{}{"foo", "bar", "baz"}, 32 | text: "foo,bar,baz", 33 | }, 34 | { 35 | data: []interface{}{1, 1, 2, 3, 5, 8}, 36 | text: "1,1,2,3,5,8", 37 | }, 38 | { 39 | data: []byte("foo"), 40 | text: "Zm9v", 41 | }, 42 | { 43 | data: []interface{}{ 44 | struct { 45 | Name string `json:"name"` 46 | }{ 47 | Name: "john", 48 | }, 49 | }, 50 | text: `[{"name":"john"}]`, 51 | }, 52 | { 53 | data: map[interface{}]interface{}{"foo": "bar"}, 54 | text: `{"foo":"bar"}`, 55 | }, 56 | } 57 | 58 | for _, testdata := range testdatum { 59 | if got, want := encode(testdata.data), testdata.text; got != want { 60 | t.Errorf("Want interface{} encoded to %q, got %q", want, got) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /yaml/compiler/image/image.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package image 16 | 17 | import "github.com/docker/distribution/reference" 18 | 19 | // Trim returns the short image name without tag. 20 | func Trim(name string) string { 21 | ref, err := reference.ParseAnyReference(name) 22 | if err != nil { 23 | return name 24 | } 25 | named, err := reference.ParseNamed(ref.String()) 26 | if err != nil { 27 | return name 28 | } 29 | named = reference.TrimNamed(named) 30 | return reference.FamiliarName(named) 31 | } 32 | 33 | // Expand returns the fully qualified image name. 34 | func Expand(name string) string { 35 | ref, err := reference.ParseAnyReference(name) 36 | if err != nil { 37 | return name 38 | } 39 | named, err := reference.ParseNamed(ref.String()) 40 | if err != nil { 41 | return name 42 | } 43 | named = reference.TagNameOnly(named) 44 | return named.String() 45 | } 46 | 47 | // Match returns true if the image name matches 48 | // an image in the list. Note the image tag is not used 49 | // in the matching logic. 50 | func Match(from string, to ...string) bool { 51 | from = Trim(from) 52 | for _, match := range to { 53 | if from == Trim(match) { 54 | return true 55 | } 56 | } 57 | return false 58 | } 59 | 60 | // MatchTag returns true if the image name matches 61 | // an image in the list, including the tag. 62 | func MatchTag(a, b string) bool { 63 | return Expand(a) == Expand(b) 64 | } 65 | 66 | // MatchHostname returns true if the image hostname 67 | // matches the specified hostname. 68 | func MatchHostname(image, hostname string) bool { 69 | ref, err := reference.ParseAnyReference(image) 70 | if err != nil { 71 | return false 72 | } 73 | named, err := reference.ParseNamed(ref.String()) 74 | if err != nil { 75 | return false 76 | } 77 | if hostname == "index.docker.io" { 78 | hostname = "docker.io" 79 | } 80 | return reference.Domain(named) == hostname 81 | } 82 | -------------------------------------------------------------------------------- /yaml/compiler/internal/rand/rand.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package rand 16 | 17 | import ( 18 | "crypto/rand" 19 | ) 20 | 21 | var chars = []byte("abcdefghijklmnopqrstuvwxyz0123456789") 22 | 23 | // random string length 24 | const length = 32 25 | 26 | // String returns a string value. 27 | func String() string { 28 | clen := len(chars) 29 | maxrb := 255 - (256 % clen) 30 | b := make([]byte, length) 31 | r := make([]byte, length+(length/4)) // storage for random bytes. 32 | i := 0 33 | for { 34 | if _, err := rand.Read(r); err != nil { 35 | panic("rand: error reading random bytes") 36 | } 37 | for _, rb := range r { 38 | c := int(rb) 39 | if c > maxrb { 40 | // Skip this number to avoid modulo bias. 41 | continue 42 | } 43 | b[i] = chars[c%clen] 44 | i++ 45 | if i == length { 46 | return string(b) 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /yaml/compiler/script_posix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package compiler 16 | 17 | import ( 18 | "bytes" 19 | "fmt" 20 | "strings" 21 | 22 | "github.com/drone/drone-runtime/engine" 23 | "github.com/drone/drone-yaml/yaml" 24 | "github.com/drone/drone-yaml/yaml/compiler/internal/rand" 25 | ) 26 | 27 | func setupScript(spec *engine.Spec, dst *engine.Step, src *yaml.Container) { 28 | var buf bytes.Buffer 29 | for _, command := range src.Commands { 30 | escaped := fmt.Sprintf("%q", command) 31 | escaped = strings.Replace(escaped, "$", `\$`, -1) 32 | buf.WriteString(fmt.Sprintf( 33 | traceScript, 34 | escaped, 35 | command, 36 | )) 37 | } 38 | script := fmt.Sprintf( 39 | buildScript, 40 | buf.String(), 41 | ) 42 | spec.Files = append(spec.Files, &engine.File{ 43 | Metadata: engine.Metadata{ 44 | UID: rand.String(), 45 | Namespace: spec.Metadata.Namespace, 46 | Name: src.Name, 47 | }, 48 | Data: []byte(script), 49 | }) 50 | dst.Files = append(dst.Files, &engine.FileMount{ 51 | Name: src.Name, 52 | Path: "/usr/drone/bin/init", 53 | Mode: 0777, 54 | }) 55 | 56 | dst.Docker.Command = []string{"/bin/sh"} 57 | dst.Docker.Args = []string{"/usr/drone/bin/init"} 58 | } 59 | 60 | // buildScript is a helper script this is added to the build 61 | // to prepare the environment and execute the build commands. 62 | const buildScript = ` 63 | if [ -n "$CI_NETRC_MACHINE" ]; then 64 | cat < $HOME/.netrc 65 | machine $CI_NETRC_MACHINE 66 | login $CI_NETRC_USERNAME 67 | password $CI_NETRC_PASSWORD 68 | EOF 69 | chmod 0600 $HOME/.netrc 70 | fi 71 | unset CI_NETRC_USERNAME 72 | unset CI_NETRC_PASSWORD 73 | unset DRONE_NETRC_USERNAME 74 | unset DRONE_NETRC_PASSWORD 75 | set -e 76 | %s 77 | ` 78 | 79 | // traceScript is a helper script that is added to 80 | // the build script to trace a command. 81 | const traceScript = ` 82 | echo + %s 83 | %s 84 | ` 85 | -------------------------------------------------------------------------------- /yaml/compiler/script_posix_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package compiler 6 | -------------------------------------------------------------------------------- /yaml/compiler/script_win.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package compiler 16 | 17 | import ( 18 | "bytes" 19 | "encoding/base64" 20 | "fmt" 21 | "strings" 22 | 23 | "github.com/drone/drone-runtime/engine" 24 | "github.com/drone/drone-yaml/yaml" 25 | ) 26 | 27 | func setupScriptWin(spec *engine.Spec, dst *engine.Step, src *yaml.Container) { 28 | var buf bytes.Buffer 29 | for _, command := range src.Commands { 30 | escaped := fmt.Sprintf("%q", command) 31 | escaped = strings.Replace(escaped, "$", `\$`, -1) 32 | buf.WriteString(fmt.Sprintf( 33 | traceScriptWin, 34 | escaped, 35 | command, 36 | )) 37 | } 38 | script := fmt.Sprintf( 39 | buildScriptWin, 40 | buf.String(), 41 | ) 42 | dst.Docker.Command = []string{"powershell", "-noprofile", "-noninteractive", "-command"} 43 | dst.Docker.Args = []string{"[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Env:CI_SCRIPT)) | iex"} 44 | dst.Envs["CI_SCRIPT"] = base64.StdEncoding.EncodeToString([]byte(script)) 45 | dst.Envs["SHELL"] = "powershell.exe" 46 | } 47 | 48 | // buildScriptWin is a helper script this is added to the build 49 | // to prepare the environment and execute the build commands. 50 | const buildScriptWin = ` 51 | if ($Env:CI_NETRC_MACHINE) { 52 | @" 53 | machine $Env:CI_NETRC_MACHINE 54 | login $Env:CI_NETRC_USERNAME 55 | password $Env:CI_NETRC_PASSWORD 56 | "@ > (Join-Path $Env:USERPROFILE '_netrc'); 57 | } 58 | [Environment]::SetEnvironmentVariable("CI_NETRC_USERNAME", $null); 59 | [Environment]::SetEnvironmentVariable("CI_NETRC_PASSWORD", $null); 60 | [Environment]::SetEnvironmentVariable("DRONE_NETRC_USERNAME", $null); 61 | [Environment]::SetEnvironmentVariable("DRONE_NETRC_PASSWORD", $null); 62 | [Environment]::SetEnvironmentVariable("CI_SCRIPT", $null); 63 | $ErrorActionPreference = 'Stop'; 64 | %s 65 | ` 66 | 67 | // traceScriptWin is a helper script that is added to 68 | // the build script to trace a command. 69 | const traceScriptWin = ` 70 | Write-Output ('+ %s'); 71 | & %s; if ($LASTEXITCODE -ne 0) {exit $LASTEXITCODE} 72 | ` 73 | -------------------------------------------------------------------------------- /yaml/compiler/script_win_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package compiler 6 | -------------------------------------------------------------------------------- /yaml/compiler/skip.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package compiler 16 | 17 | import "github.com/drone/drone-yaml/yaml" 18 | 19 | // SkipData provides build metadata use to determine if a 20 | // pipeline step should be skipped. 21 | type SkipData struct { 22 | Branch string 23 | Cron string 24 | Event string 25 | Instance string 26 | Ref string 27 | Repo string 28 | Target string 29 | Action string 30 | } 31 | 32 | // SkipFunc returns a function that can be used to skip 33 | // individual pipeline steps based on build metadata. 34 | func SkipFunc(data SkipData) func(*yaml.Container) bool { 35 | return func(container *yaml.Container) bool { 36 | switch { 37 | case !container.When.Branch.Match(data.Branch): 38 | return true 39 | case !container.When.Cron.Match(data.Cron): 40 | return true 41 | case !container.When.Event.Match(data.Event): 42 | return true 43 | case !container.When.Instance.Match(data.Instance): 44 | return true 45 | case !container.When.Ref.Match(data.Ref): 46 | return true 47 | case !container.When.Repo.Match(data.Repo): 48 | return true 49 | case !container.When.Target.Match(data.Target): 50 | return true 51 | case !container.When.Action.Match(data.Action): 52 | return true 53 | default: 54 | return false 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /yaml/compiler/transform/auths.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package transform 16 | 17 | import "github.com/drone/drone-runtime/engine" 18 | 19 | // WithAuths is a transform function that adds a set 20 | // of global registry credentials to the container. 21 | func WithAuths(auths []*engine.DockerAuth) func(*engine.Spec) { 22 | return func(spec *engine.Spec) { 23 | spec.Docker.Auths = append(spec.Docker.Auths, auths...) 24 | } 25 | } 26 | 27 | // AuthsFunc is a callback function used to request 28 | // registry credentials to pull private images. 29 | type AuthsFunc func() []*engine.DockerAuth 30 | 31 | // WithAuthsFunc is a transform function that provides 32 | // the sepcification with registry authentication 33 | // credentials via a callback function. 34 | func WithAuthsFunc(f AuthsFunc) func(*engine.Spec) { 35 | return func(spec *engine.Spec) { 36 | auths := f() 37 | if len(auths) != 0 { 38 | spec.Docker.Auths = append(spec.Docker.Auths, auths...) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /yaml/compiler/transform/auths_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package transform 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/drone/drone-runtime/engine" 11 | "github.com/google/go-cmp/cmp" 12 | ) 13 | 14 | func TestWithAuths(t *testing.T) { 15 | spec := &engine.Spec{ 16 | Steps: []*engine.Step{}, 17 | Docker: &engine.DockerConfig{}, 18 | } 19 | auths := []*engine.DockerAuth{ 20 | { 21 | Address: "docker.io", 22 | Username: "octocat", 23 | Password: "correct-horse-battery-staple", 24 | }, 25 | } 26 | WithAuths(auths)(spec) 27 | if diff := cmp.Diff(auths, spec.Docker.Auths); diff != "" { 28 | t.Errorf("Unexpected auths transform") 29 | t.Log(diff) 30 | } 31 | } 32 | 33 | func TestWithAuthsFunc(t *testing.T) { 34 | spec := &engine.Spec{ 35 | Steps: []*engine.Step{}, 36 | Docker: &engine.DockerConfig{}, 37 | } 38 | auths := []*engine.DockerAuth{ 39 | { 40 | Address: "docker.io", 41 | Username: "octocat", 42 | Password: "correct-horse-battery-staple", 43 | }, 44 | } 45 | fn := func() []*engine.DockerAuth { 46 | return auths 47 | } 48 | WithAuthsFunc(fn)(spec) 49 | if diff := cmp.Diff(auths, spec.Docker.Auths); diff != "" { 50 | t.Errorf("Unexpected auths transform") 51 | t.Log(diff) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /yaml/compiler/transform/combine.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package transform 16 | 17 | import "github.com/drone/drone-runtime/engine" 18 | 19 | // Combine is a transform function that combines 20 | // one or many transform functions. 21 | func Combine(fns ...func(*engine.Spec)) func(*engine.Spec) { 22 | return func(spec *engine.Spec) { 23 | for _, fn := range fns { 24 | fn(spec) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /yaml/compiler/transform/combine_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package transform 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/drone/drone-runtime/engine" 11 | "github.com/google/go-cmp/cmp" 12 | ) 13 | 14 | func TestCombine(t *testing.T) { 15 | step := &engine.Step{ 16 | Metadata: engine.Metadata{ 17 | UID: "1", 18 | Name: "build", 19 | }, 20 | Envs: map[string]string{}, 21 | } 22 | spec := &engine.Spec{ 23 | Steps: []*engine.Step{step}, 24 | } 25 | Combine( 26 | WithEnviron(map[string]string{"GOOS": "linux"}), 27 | WithEnviron(map[string]string{"GOARCH": "amd64"}), 28 | )(spec) 29 | want := map[string]string{ 30 | "GOOS": "linux", 31 | "GOARCH": "amd64", 32 | } 33 | if diff := cmp.Diff(want, step.Envs); diff != "" { 34 | t.Errorf("Unexpected transform") 35 | t.Log(diff) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /yaml/compiler/transform/env.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package transform 16 | 17 | import "github.com/drone/drone-runtime/engine" 18 | 19 | // WithEnviron is a transform function that adds a set 20 | // of environment variables to each container. 21 | func WithEnviron(envs map[string]string) func(*engine.Spec) { 22 | return func(spec *engine.Spec) { 23 | for key, value := range envs { 24 | for _, step := range spec.Steps { 25 | step.Envs[key] = value 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /yaml/compiler/transform/env_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package transform 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/drone/drone-runtime/engine" 11 | "github.com/google/go-cmp/cmp" 12 | ) 13 | 14 | func TestWithEnviron(t *testing.T) { 15 | step := &engine.Step{ 16 | Metadata: engine.Metadata{ 17 | UID: "1", 18 | Name: "build", 19 | }, 20 | Envs: map[string]string{}, 21 | } 22 | spec := &engine.Spec{ 23 | Steps: []*engine.Step{step}, 24 | } 25 | envs := map[string]string{ 26 | "GOOS": "linux", 27 | "GOARCH": "amd64", 28 | } 29 | WithEnviron(envs)(spec) 30 | if diff := cmp.Diff(envs, step.Envs); diff != "" { 31 | t.Errorf("Unexpected transform") 32 | t.Log(diff) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /yaml/compiler/transform/filter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package transform 16 | 17 | import "github.com/drone/drone-runtime/engine" 18 | 19 | // Include is a transform function that limits the 20 | // pipeline execution to a whitelist of named steps. 21 | func Include(names []string) func(*engine.Spec) { 22 | set := map[string]struct{}{} 23 | for _, name := range names { 24 | set[name] = struct{}{} 25 | } 26 | return func(spec *engine.Spec) { 27 | if len(names) == 0 { 28 | return 29 | } 30 | for _, step := range spec.Steps { 31 | if step.Metadata.Name == "clone" { 32 | continue 33 | } 34 | _, ok := set[step.Metadata.Name] 35 | if !ok { 36 | // if the step is not included in the 37 | // whitelist the run policy is set to never. 38 | step.RunPolicy = engine.RunNever 39 | } 40 | } 41 | } 42 | } 43 | 44 | // Exclude is a transform function that limits the 45 | // pipeline execution to a whitelist of named steps. 46 | func Exclude(names []string) func(*engine.Spec) { 47 | set := map[string]struct{}{} 48 | for _, name := range names { 49 | set[name] = struct{}{} 50 | } 51 | return func(spec *engine.Spec) { 52 | if len(names) == 0 { 53 | return 54 | } 55 | for _, step := range spec.Steps { 56 | if step.Metadata.Name == "clone" { 57 | continue 58 | } 59 | _, ok := set[step.Metadata.Name] 60 | if ok { 61 | // if the step is included in the blacklist 62 | // the run policy is set to never. 63 | step.RunPolicy = engine.RunNever 64 | } 65 | } 66 | } 67 | } 68 | 69 | // ResumeAt is a transform function that modifies the 70 | // exuction to resume at a named step. 71 | func ResumeAt(name string) func(*engine.Spec) { 72 | return func(spec *engine.Spec) { 73 | if name == "" { 74 | return 75 | } 76 | for _, step := range spec.Steps { 77 | if step.Metadata.Name == name { 78 | return 79 | } 80 | if step.Metadata.Name == "clone" { 81 | continue 82 | } 83 | step.RunPolicy = engine.RunNever 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /yaml/compiler/transform/labels.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package transform 16 | 17 | import "github.com/drone/drone-runtime/engine" 18 | 19 | // WithLables is a transform function that adds a set 20 | // of labels to each resource. 21 | func WithLables(labels map[string]string) func(*engine.Spec) { 22 | return func(spec *engine.Spec) { 23 | for k, v := range labels { 24 | spec.Metadata.Labels[k] = v 25 | } 26 | for _, resource := range spec.Docker.Volumes { 27 | for k, v := range labels { 28 | resource.Metadata.Labels[k] = v 29 | } 30 | } 31 | for _, resource := range spec.Steps { 32 | for k, v := range labels { 33 | resource.Metadata.Labels[k] = v 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /yaml/compiler/transform/labels_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package transform 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/drone/drone-runtime/engine" 11 | "github.com/google/go-cmp/cmp" 12 | ) 13 | 14 | func TestWithLabels(t *testing.T) { 15 | volume := &engine.Volume{ 16 | Metadata: engine.Metadata{ 17 | Labels: map[string]string{}, 18 | }, 19 | } 20 | step := &engine.Step{ 21 | Metadata: engine.Metadata{ 22 | Labels: map[string]string{}, 23 | }, 24 | Envs: map[string]string{}, 25 | } 26 | spec := &engine.Spec{ 27 | Metadata: engine.Metadata{ 28 | Labels: map[string]string{}, 29 | }, 30 | Steps: []*engine.Step{step}, 31 | Docker: &engine.DockerConfig{ 32 | Volumes: []*engine.Volume{volume}, 33 | }, 34 | } 35 | labels := map[string]string{ 36 | "io.drone.build.number": "1", 37 | "io.drone.build.event": "push", 38 | } 39 | WithLables(labels)(spec) 40 | if diff := cmp.Diff(labels, spec.Metadata.Labels); diff != "" { 41 | t.Errorf("Unexpected spec labels") 42 | t.Log(diff) 43 | } 44 | if diff := cmp.Diff(labels, step.Metadata.Labels); diff != "" { 45 | t.Errorf("Unexpected step labels") 46 | t.Log(diff) 47 | } 48 | if diff := cmp.Diff(labels, volume.Metadata.Labels); diff != "" { 49 | t.Errorf("Unexpected volume labels") 50 | t.Log(diff) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /yaml/compiler/transform/limits.go: -------------------------------------------------------------------------------- 1 | // Copyright the Drone Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package transform 16 | 17 | import "github.com/drone/drone-runtime/engine" 18 | 19 | // WithLimits is a transform function that applies 20 | // resource limits to the container processes. 21 | func WithLimits(memlimit, cpulimit int64) func(*engine.Spec) { 22 | return func(spec *engine.Spec) { 23 | // if no limits are defined exit immediately. 24 | if memlimit == 0 && cpulimit == 0 { 25 | return 26 | } 27 | // otherwise apply the resource limits to every 28 | // step in the runtime spec. 29 | for _, step := range spec.Steps { 30 | if step.Resources == nil { 31 | step.Resources = &engine.Resources{} 32 | } 33 | if step.Resources.Limits == nil { 34 | step.Resources.Limits = &engine.ResourceObject{} 35 | } 36 | step.Resources.Limits.Memory = memlimit 37 | step.Resources.Limits.CPU = cpulimit 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /yaml/compiler/transform/limits_test.go: -------------------------------------------------------------------------------- 1 | // Copyright the Drone Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package transform 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/drone/drone-runtime/engine" 21 | ) 22 | 23 | func TestWithLimits(t *testing.T) { 24 | step := &engine.Step{ 25 | Metadata: engine.Metadata{ 26 | UID: "1", 27 | Name: "build", 28 | }, 29 | Docker: &engine.DockerStep{}, 30 | } 31 | spec := &engine.Spec{ 32 | Steps: []*engine.Step{step}, 33 | } 34 | WithLimits(1, 2)(spec) 35 | if got, want := step.Resources.Limits.Memory, int64(1); got != want { 36 | t.Errorf("Want memory limit %v, got %v", want, got) 37 | } 38 | if got, want := step.Resources.Limits.CPU, int64(2); got != want { 39 | t.Errorf("Want cpu limit %v, got %v", want, got) 40 | } 41 | } 42 | 43 | func TestWithNoLimits(t *testing.T) { 44 | step := &engine.Step{ 45 | Metadata: engine.Metadata{ 46 | UID: "1", 47 | Name: "build", 48 | }, 49 | Docker: &engine.DockerStep{}, 50 | } 51 | spec := &engine.Spec{ 52 | Steps: []*engine.Step{step}, 53 | } 54 | WithLimits(0, 0)(spec) 55 | if step.Resources != nil { 56 | t.Errorf("Expect no limits applied") 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /yaml/compiler/transform/netrc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package transform 16 | 17 | import ( 18 | "fmt" 19 | 20 | "github.com/drone/drone-runtime/engine" 21 | "github.com/drone/drone-yaml/yaml/compiler/internal/rand" 22 | ) 23 | 24 | const ( 25 | netrcName = ".netrc" 26 | netrcPath = "/var/run/drone/.netrc" 27 | netrcMode = 0777 28 | ) 29 | 30 | const disableNetrcMount = true 31 | 32 | // WithNetrc is a helper function that creates a netrc file 33 | // and mounts the file to all container steps. 34 | func WithNetrc(machine, username, password string) func(*engine.Spec) { 35 | return func(spec *engine.Spec) { 36 | if username == "" || password == "" { 37 | return 38 | } 39 | 40 | // TODO(bradrydzewski) temporarily disable mounting 41 | // the netrc file due to issues with kubernetes 42 | // compatibility. 43 | if disableNetrcMount == false { 44 | // Currently file mounts don't seem to work in Windows so environment 45 | // variables are used instead 46 | // FIXME: https://github.com/drone/drone-yaml/issues/20 47 | if spec.Platform.OS != "windows" { 48 | netrc := generateNetrc(machine, username, password) 49 | spec.Files = append(spec.Files, &engine.File{ 50 | Metadata: engine.Metadata{ 51 | UID: rand.String(), 52 | Name: netrcName, 53 | Namespace: spec.Metadata.Namespace, 54 | }, 55 | Data: []byte(netrc), 56 | }) 57 | for _, step := range spec.Steps { 58 | step.Files = append(step.Files, &engine.FileMount{ 59 | Name: netrcName, 60 | Path: netrcPath, 61 | Mode: netrcMode, 62 | }) 63 | } 64 | } 65 | } 66 | 67 | // TODO(bradrydzewski) these should only be injected 68 | // if the file is not mounted, if OS == Windows. 69 | for _, step := range spec.Steps { 70 | if step.Envs == nil { 71 | step.Envs = map[string]string{} 72 | } 73 | step.Envs["CI_NETRC_MACHINE"] = machine 74 | step.Envs["CI_NETRC_USERNAME"] = username 75 | step.Envs["CI_NETRC_PASSWORD"] = password 76 | 77 | step.Envs["DRONE_NETRC_MACHINE"] = machine 78 | step.Envs["DRONE_NETRC_USERNAME"] = username 79 | step.Envs["DRONE_NETRC_PASSWORD"] = password 80 | } 81 | } 82 | } 83 | 84 | func generateNetrc(machine, username, password string) string { 85 | return fmt.Sprintf("machine %s login %s password %s", 86 | machine, username, password) 87 | } 88 | -------------------------------------------------------------------------------- /yaml/compiler/transform/netrc_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package transform 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/drone/drone-runtime/engine" 11 | 12 | "github.com/google/go-cmp/cmp" 13 | "github.com/google/go-cmp/cmp/cmpopts" 14 | ) 15 | 16 | var ignoreMetadata = cmpopts.IgnoreFields( 17 | engine.Metadata{}, "UID") 18 | 19 | func TestWithNetrc(t *testing.T) { 20 | if true { 21 | t.Skipf("mounting the netrc is temporarily disabled") 22 | return 23 | } 24 | step := &engine.Step{ 25 | Metadata: engine.Metadata{ 26 | UID: "1", 27 | Name: "build", 28 | }, 29 | } 30 | spec := &engine.Spec{ 31 | Metadata: engine.Metadata{ 32 | UID: "acdj0yjqv7uh5hidveg0ggr42x8oj67b", 33 | Namespace: "pivqfthg1c9hy83ylht1sxx4nygjc7tk", 34 | }, 35 | Steps: []*engine.Step{step}, 36 | } 37 | WithNetrc("@machine", "@username", "@password")(spec) 38 | if len(step.Files) == 0 { 39 | t.Errorf("File mount not added to step") 40 | return 41 | } 42 | if len(spec.Files) == 0 { 43 | t.Errorf("File not declared in spec") 44 | return 45 | } 46 | file := &engine.File{ 47 | Metadata: engine.Metadata{ 48 | Name: ".netrc", 49 | Namespace: "pivqfthg1c9hy83ylht1sxx4nygjc7tk", 50 | }, 51 | Data: []byte("machine @machine login @username password @password"), 52 | } 53 | if diff := cmp.Diff(file, spec.Files[0], ignoreMetadata); diff != "" { 54 | t.Errorf("Unexpected file declaration") 55 | t.Log(diff) 56 | } 57 | 58 | fileMount := &engine.FileMount{Name: ".netrc", Path: "/root/.netrc", Mode: 0600} 59 | if diff := cmp.Diff(fileMount, step.Files[0], ignoreMetadata); diff != "" { 60 | t.Errorf("Unexpected file mount") 61 | t.Log(diff) 62 | } 63 | } 64 | 65 | func TestWithEmptyNetrc(t *testing.T) { 66 | step := &engine.Step{ 67 | Metadata: engine.Metadata{ 68 | UID: "1", 69 | Name: "build", 70 | }, 71 | } 72 | spec := &engine.Spec{ 73 | Steps: []*engine.Step{step}, 74 | } 75 | WithNetrc("@machine", "", "")(spec) 76 | if len(spec.Files) != 0 { 77 | t.Errorf("Unexpected file declaration") 78 | } 79 | if len(step.Files) != 0 { 80 | t.Errorf("Unexpected file mount") 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /yaml/compiler/transform/network.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package transform 16 | 17 | import "github.com/drone/drone-runtime/engine" 18 | 19 | // WithNetworks is a transform function that attaches a 20 | // list of user-defined Docker networks to each step. 21 | func WithNetworks(networks []string) func(*engine.Spec) { 22 | return func(spec *engine.Spec) { 23 | for _, step := range spec.Steps { 24 | step.Docker.Networks = append( 25 | step.Docker.Networks, networks...) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /yaml/compiler/transform/network_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package transform 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/drone/drone-runtime/engine" 11 | "github.com/google/go-cmp/cmp" 12 | ) 13 | 14 | func TestWithNetwork(t *testing.T) { 15 | step := &engine.Step{ 16 | Metadata: engine.Metadata{ 17 | UID: "1", 18 | Name: "build", 19 | }, 20 | Docker: &engine.DockerStep{ 21 | Networks: nil, 22 | }, 23 | } 24 | spec := &engine.Spec{ 25 | Steps: []*engine.Step{step}, 26 | } 27 | nets := []string{"a", "b"} 28 | WithNetworks(nets)(spec) 29 | if diff := cmp.Diff(nets, step.Docker.Networks); diff != "" { 30 | t.Errorf("Unexpected transform") 31 | t.Log(diff) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /yaml/compiler/transform/proxy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package transform 16 | 17 | import ( 18 | "os" 19 | "strings" 20 | 21 | "github.com/drone/drone-runtime/engine" 22 | ) 23 | 24 | // WithProxy is a transform function that adds the 25 | // http_proxy environment variables to every container. 26 | func WithProxy() func(*engine.Spec) { 27 | environ := map[string]string{} 28 | if value := getenv("no_proxy"); value != "" { 29 | environ["no_proxy"] = value 30 | environ["NO_PROXY"] = value 31 | } 32 | if value := getenv("http_proxy"); value != "" { 33 | environ["http_proxy"] = value 34 | environ["HTTP_PROXY"] = value 35 | } 36 | if value := getenv("https_proxy"); value != "" { 37 | environ["https_proxy"] = value 38 | environ["HTTPS_PROXY"] = value 39 | } 40 | return WithEnviron(environ) 41 | } 42 | 43 | func getenv(name string) (value string) { 44 | name = strings.ToUpper(name) 45 | if value := os.Getenv(name); value != "" { 46 | return value 47 | } 48 | name = strings.ToLower(name) 49 | if value := os.Getenv(name); value != "" { 50 | return value 51 | } 52 | return 53 | } 54 | -------------------------------------------------------------------------------- /yaml/compiler/transform/proxy_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package transform 6 | 7 | import ( 8 | "os" 9 | "testing" 10 | 11 | "github.com/drone/drone-runtime/engine" 12 | ) 13 | 14 | func TestWithProxy(t *testing.T) { 15 | var ( 16 | noProxy = getenv("no_proxy") 17 | httpProxy = getenv("https_proxy") 18 | httpsProxy = getenv("https_proxy") 19 | ) 20 | defer func() { 21 | os.Setenv("no_proxy", noProxy) 22 | os.Setenv("NO_PROXY", noProxy) 23 | os.Setenv("http_proxy", httpProxy) 24 | os.Setenv("HTTP_PROXY", httpProxy) 25 | os.Setenv("HTTPS_PROXY", httpsProxy) 26 | os.Setenv("https_proxy", httpsProxy) 27 | }() 28 | 29 | testdata := map[string]string{ 30 | "NO_PROXY": "http://dummy.no.proxy", 31 | "http_proxy": "http://dummy.http.proxy", 32 | "https_proxy": "http://dummy.https.proxy", 33 | } 34 | 35 | for k, v := range testdata { 36 | os.Setenv(k, v) 37 | } 38 | 39 | step := &engine.Step{ 40 | Metadata: engine.Metadata{ 41 | UID: "1", 42 | Name: "build", 43 | }, 44 | Envs: map[string]string{}, 45 | } 46 | spec := &engine.Spec{ 47 | Steps: []*engine.Step{step}, 48 | } 49 | WithProxy()(spec) 50 | for k, v := range testdata { 51 | step := spec.Steps[0] 52 | if step.Envs[k] != v { 53 | t.Errorf("Expect proxy varaible %s=%q, got %q", k, v, step.Envs[k]) 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /yaml/compiler/transform/secret.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package transform 16 | 17 | import ( 18 | "github.com/drone/drone-runtime/engine" 19 | "github.com/drone/drone-yaml/yaml/compiler/internal/rand" 20 | ) 21 | 22 | // WithSecrets is a transform function that adds a set 23 | // of global secrets to the container. 24 | func WithSecrets(secrets map[string]string) func(*engine.Spec) { 25 | return func(spec *engine.Spec) { 26 | for key, value := range secrets { 27 | spec.Secrets = append(spec.Secrets, 28 | &engine.Secret{ 29 | Metadata: engine.Metadata{ 30 | UID: rand.String(), 31 | Name: key, 32 | Namespace: spec.Metadata.Namespace, 33 | }, 34 | Data: value, 35 | }, 36 | ) 37 | } 38 | } 39 | } 40 | 41 | // SecretFunc is a callback function used to request 42 | // named secret, required by a pipeline step. 43 | type SecretFunc func(string) *engine.Secret 44 | 45 | // WithSecretFunc is a transform function that resolves 46 | // all named secrets through a callback function, and 47 | // adds the secrets to the specification. 48 | func WithSecretFunc(f SecretFunc) func(*engine.Spec) { 49 | return func(spec *engine.Spec) { 50 | // first we get a unique list of all secrets 51 | // used by the specification. 52 | set := map[string]struct{}{} 53 | for _, step := range spec.Steps { 54 | // if we know the step is not going to run, 55 | // we can ignore any secrets that it requires. 56 | if step.RunPolicy == engine.RunNever { 57 | continue 58 | } 59 | for _, v := range step.Secrets { 60 | set[v.Name] = struct{}{} 61 | } 62 | } 63 | 64 | // next we use the callback function to 65 | // get the value for each secret, and append 66 | // to the specification. 67 | for name := range set { 68 | secret := f(name) 69 | if secret != nil { 70 | secret.Metadata.UID = rand.String() 71 | secret.Metadata.Namespace = spec.Metadata.Namespace 72 | spec.Secrets = append(spec.Secrets, secret) 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /yaml/compiler/transform/secret_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package transform 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/drone/drone-runtime/engine" 11 | "github.com/google/go-cmp/cmp" 12 | ) 13 | 14 | func TestWithSecret(t *testing.T) { 15 | step := &engine.Step{ 16 | Metadata: engine.Metadata{ 17 | UID: "1", 18 | Name: "build", 19 | }, 20 | Envs: map[string]string{}, 21 | } 22 | spec := &engine.Spec{ 23 | Metadata: engine.Metadata{ 24 | UID: "acdj0yjqv7uh5hidveg0ggr42x8oj67b", 25 | Namespace: "pivqfthg1c9hy83ylht1sxx4nygjc7tk", 26 | }, 27 | Steps: []*engine.Step{step}, 28 | } 29 | secrets := map[string]string{ 30 | "password": "correct-horse-battery-staple", 31 | } 32 | WithSecrets(secrets)(spec) 33 | 34 | want := []*engine.Secret{ 35 | { 36 | Metadata: engine.Metadata{ 37 | Name: "password", 38 | Namespace: "pivqfthg1c9hy83ylht1sxx4nygjc7tk", 39 | }, 40 | Data: "correct-horse-battery-staple", 41 | }, 42 | } 43 | if diff := cmp.Diff(want, spec.Secrets, ignoreMetadata); diff != "" { 44 | t.Errorf("Unexpected secret transform") 45 | t.Log(diff) 46 | } 47 | } 48 | 49 | func TestWithSecretFunc(t *testing.T) { 50 | step := &engine.Step{ 51 | Metadata: engine.Metadata{ 52 | UID: "1", 53 | Name: "build", 54 | }, 55 | Envs: map[string]string{}, 56 | Secrets: []*engine.SecretVar{ 57 | { 58 | Name: "password", 59 | Env: "PASSWORD", 60 | }, 61 | }, 62 | } 63 | spec := &engine.Spec{ 64 | Metadata: engine.Metadata{ 65 | UID: "acdj0yjqv7uh5hidveg0ggr42x8oj67b", 66 | Namespace: "pivqfthg1c9hy83ylht1sxx4nygjc7tk", 67 | }, 68 | Steps: []*engine.Step{ 69 | step, 70 | // this is a step that requests a secret 71 | // but should be skipped. 72 | { 73 | RunPolicy: engine.RunNever, 74 | Secrets: []*engine.SecretVar{ 75 | { 76 | Name: "github_token", 77 | Env: "GITHUB_TOKEN", 78 | }, 79 | }, 80 | }, 81 | }, 82 | } 83 | 84 | fn := func(name string) *engine.Secret { 85 | if name == "github_token" { 86 | t.Errorf("Requested secret for skipped step") 87 | return nil 88 | } 89 | return &engine.Secret{ 90 | Metadata: engine.Metadata{ 91 | Name: "password", 92 | }, 93 | Data: "correct-horse-battery-staple", 94 | } 95 | } 96 | WithSecretFunc(fn)(spec) 97 | 98 | want := []*engine.Secret{ 99 | { 100 | Metadata: engine.Metadata{ 101 | Name: "password", 102 | Namespace: "pivqfthg1c9hy83ylht1sxx4nygjc7tk", 103 | }, 104 | Data: "correct-horse-battery-staple", 105 | }, 106 | } 107 | if diff := cmp.Diff(want, spec.Secrets, ignoreMetadata); diff != "" { 108 | t.Errorf("Unexpected secret transform") 109 | t.Log(diff) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /yaml/compiler/transform/volume.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package transform 16 | 17 | import ( 18 | "strings" 19 | 20 | "github.com/drone/drone-runtime/engine" 21 | "github.com/drone/drone-yaml/yaml/compiler/internal/rand" 22 | ) 23 | 24 | // WithVolumes is a transform function that adds a set 25 | // of global volumes to the container. 26 | func WithVolumes(volumes map[string]string) func(*engine.Spec) { 27 | return func(spec *engine.Spec) { 28 | for key, value := range volumes { 29 | volume := &engine.Volume{ 30 | Metadata: engine.Metadata{ 31 | UID: rand.String(), 32 | Name: rand.String(), 33 | Namespace: spec.Metadata.Name, 34 | Labels: map[string]string{}, 35 | }, 36 | HostPath: &engine.VolumeHostPath{ 37 | Path: key, 38 | }, 39 | } 40 | spec.Docker.Volumes = append(spec.Docker.Volumes, volume) 41 | for _, step := range spec.Steps { 42 | mount := &engine.VolumeMount{ 43 | Name: volume.Metadata.Name, 44 | Path: value, 45 | } 46 | step.Volumes = append(step.Volumes, mount) 47 | } 48 | } 49 | } 50 | } 51 | 52 | // WithVolumeSlice is a transform function that adds a set 53 | // of global volumes to the container that are defined in 54 | // --volume=host:container format. 55 | func WithVolumeSlice(volumes []string) func(*engine.Spec) { 56 | to := map[string]string{} 57 | for _, s := range volumes { 58 | parts := strings.Split(s, ":") 59 | if len(parts) != 2 { 60 | continue 61 | } 62 | key := parts[0] 63 | val := parts[1] 64 | to[key] = val 65 | } 66 | return WithVolumes(to) 67 | } 68 | -------------------------------------------------------------------------------- /yaml/compiler/transform/volume_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package transform 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/drone/drone-runtime/engine" 11 | ) 12 | 13 | func TestWithVolumes(t *testing.T) { 14 | step := &engine.Step{ 15 | Metadata: engine.Metadata{ 16 | UID: "1", 17 | Name: "build", 18 | }, 19 | Docker: &engine.DockerStep{ 20 | Networks: nil, 21 | }, 22 | } 23 | spec := &engine.Spec{ 24 | Docker: &engine.DockerConfig{}, 25 | Steps: []*engine.Step{step}, 26 | } 27 | vols := map[string]string{"/path/on/host": "/path/in/container"} 28 | WithVolumes(vols)(spec) 29 | 30 | if len(step.Volumes) == 0 { 31 | t.Error("Expected volume added to container") 32 | } 33 | if got, want := step.Volumes[0].Path, "/path/in/container"; got != want { 34 | t.Errorf("Want mount path %s, got %s", want, got) 35 | } 36 | if len(spec.Docker.Volumes) == 0 { 37 | t.Error("Expected volume added to spec") 38 | } 39 | if got, want := spec.Docker.Volumes[0].HostPath.Path, "/path/on/host"; got != want { 40 | t.Errorf("Want host mount path %s, got %s", want, got) 41 | } 42 | } 43 | 44 | func TestWithVolumeSlice(t *testing.T) { 45 | step := &engine.Step{ 46 | Metadata: engine.Metadata{ 47 | UID: "1", 48 | Name: "build", 49 | }, 50 | Docker: &engine.DockerStep{ 51 | Networks: nil, 52 | }, 53 | } 54 | spec := &engine.Spec{ 55 | Docker: &engine.DockerConfig{}, 56 | Steps: []*engine.Step{step}, 57 | } 58 | vols := []string{"/path/on/host:/path/in/container"} 59 | WithVolumeSlice(vols)(spec) 60 | 61 | if len(step.Volumes) == 0 { 62 | t.Error("Expected volume added to container") 63 | } 64 | if got, want := step.Volumes[0].Path, "/path/in/container"; got != want { 65 | t.Errorf("Want mount path %s, got %s", want, got) 66 | } 67 | if len(spec.Docker.Volumes) == 0 { 68 | t.Error("Expected volume added to spec") 69 | } 70 | if got, want := spec.Docker.Volumes[0].HostPath.Path, "/path/on/host"; got != want { 71 | t.Errorf("Want host mount path %s, got %s", want, got) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /yaml/converter/bitbucket/config_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package bitbucket 6 | -------------------------------------------------------------------------------- /yaml/converter/bitbucket/convert.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package bitbucket 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | 11 | droneyaml "github.com/drone/drone-yaml/yaml" 12 | "github.com/drone/drone-yaml/yaml/pretty" 13 | 14 | "github.com/buildkite/yaml" 15 | ) 16 | 17 | // Convert converts the yaml configuration file from 18 | // the legacy format to the 1.0+ format. 19 | func Convert(b []byte, ref string) ([]byte, error) { 20 | config := new(Config) 21 | err := yaml.Unmarshal(b, config) 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | // TODO (bradrydzewski) to correctly choose 27 | // the pipeline we need to pass the branch 28 | // and ref. 29 | stage := config.Pipeline(ref) 30 | 31 | pipeline := &droneyaml.Pipeline{} 32 | pipeline.Name = "default" 33 | pipeline.Kind = "pipeline" 34 | 35 | // 36 | // clone 37 | // 38 | 39 | pipeline.Clone.Depth = config.Clone.Depth 40 | 41 | // 42 | // steps 43 | // 44 | 45 | for i, from := range stage.Steps { 46 | to := toContainer(from) 47 | // defaults to the global image if the 48 | // step does not define an image. 49 | if to.Image == "" { 50 | to.Image = config.Image 51 | } 52 | if to.Name == "" { 53 | to.Name = fmt.Sprintf("step_%d", i) 54 | } 55 | pipeline.Steps = append(pipeline.Steps, to) 56 | } 57 | 58 | // 59 | // services 60 | // 61 | 62 | for name, from := range config.Definitions.Services { 63 | to := toContainer(from) 64 | to.Name = name 65 | pipeline.Services = append(pipeline.Services, to) 66 | } 67 | 68 | // 69 | // wrap the pipeline in the manifest 70 | // 71 | 72 | manifest := &droneyaml.Manifest{} 73 | manifest.Resources = append(manifest.Resources, pipeline) 74 | 75 | buf := new(bytes.Buffer) 76 | pretty.Print(buf, manifest) 77 | return buf.Bytes(), nil 78 | } 79 | 80 | func toContainer(from *Step) *droneyaml.Container { 81 | return &droneyaml.Container{ 82 | Name: from.Name, 83 | Image: from.Image, 84 | Commands: from.Script, 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /yaml/converter/bitbucket/convert_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package bitbucket 6 | 7 | import ( 8 | "bytes" 9 | "io/ioutil" 10 | "testing" 11 | ) 12 | 13 | func TestConvert(t *testing.T) { 14 | tests := []struct { 15 | before, after, ref string 16 | }{ 17 | { 18 | before: "testdata/sample1.yaml", 19 | after: "testdata/sample1.yaml.golden", 20 | ref: "refs/heads/master", 21 | }, 22 | { 23 | before: "testdata/sample2.yaml", 24 | after: "testdata/sample2.yaml.golden", 25 | ref: "refs/heads/feature/foo", 26 | }, 27 | } 28 | 29 | for _, test := range tests { 30 | a, err := ioutil.ReadFile(test.before) 31 | if err != nil { 32 | t.Error(err) 33 | return 34 | } 35 | b, err := ioutil.ReadFile(test.after) 36 | if err != nil { 37 | t.Error(err) 38 | return 39 | } 40 | c, err := Convert([]byte(a), test.ref) 41 | if err != nil { 42 | t.Error(err) 43 | return 44 | } 45 | if bytes.Equal(b, c) == false { 46 | t.Errorf("Unexpected yaml conversion of %s", test.before) 47 | t.Log(string(c)) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /yaml/converter/bitbucket/testdata/sample1.yaml: -------------------------------------------------------------------------------- 1 | pipelines: 2 | default: 3 | - step: 4 | name: Build and test 5 | image: node:8.5.0 6 | caches: 7 | - node 8 | script: 9 | - npm install 10 | - npm test 11 | - npm build 12 | artifacts: 13 | - dist/** 14 | - step: 15 | name: Integration test 16 | image: node:8.5.0 17 | caches: 18 | - node 19 | services: 20 | - postgres 21 | script: 22 | - npm run integration-test 23 | - step: 24 | name: Deploy to beanstalk 25 | image: python:3.5.1 26 | script: 27 | - python deploy-to-beanstalk.py 28 | 29 | definitions: 30 | services: 31 | postgres: 32 | image: postgres:9.6.4 33 | -------------------------------------------------------------------------------- /yaml/converter/bitbucket/testdata/sample1.yaml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: Build and test 11 | image: node:8.5.0 12 | commands: 13 | - npm install 14 | - npm test 15 | - npm build 16 | 17 | - name: Integration test 18 | image: node:8.5.0 19 | commands: 20 | - npm run integration-test 21 | 22 | - name: Deploy to beanstalk 23 | image: python:3.5.1 24 | commands: 25 | - python deploy-to-beanstalk.py 26 | 27 | services: 28 | - name: postgres 29 | image: postgres:9.6.4 30 | 31 | ... 32 | -------------------------------------------------------------------------------- /yaml/converter/bitbucket/testdata/sample2.yaml: -------------------------------------------------------------------------------- 1 | pipelines: 2 | branches: 3 | feature/*: 4 | - step: 5 | name: Test 6 | image: node:latest 7 | script: 8 | - npm install 9 | - npm test 10 | default: 11 | - step: 12 | name: Build and test 13 | image: node:8.5.0 14 | caches: 15 | - node 16 | script: 17 | - npm install 18 | - npm test 19 | - npm build 20 | artifacts: 21 | - dist/** 22 | - step: 23 | name: Integration test 24 | image: node:8.5.0 25 | caches: 26 | - node 27 | services: 28 | - postgres 29 | script: 30 | - npm run integration-test 31 | - step: 32 | name: Deploy to beanstalk 33 | image: python:3.5.1 34 | script: 35 | - python deploy-to-beanstalk.py 36 | 37 | definitions: 38 | services: 39 | postgres: 40 | image: postgres:9.6.4 41 | -------------------------------------------------------------------------------- /yaml/converter/bitbucket/testdata/sample2.yaml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: Test 11 | image: node:latest 12 | commands: 13 | - npm install 14 | - npm test 15 | 16 | services: 17 | - name: postgres 18 | image: postgres:9.6.4 19 | 20 | ... 21 | -------------------------------------------------------------------------------- /yaml/converter/circleci/config.go: -------------------------------------------------------------------------------- 1 | package circleci 2 | 3 | type ( 4 | // Config defines the pipeline configuration. 5 | Config struct { 6 | // Version specifies the yaml configuration 7 | // file version. 8 | Version string 9 | 10 | // Jobs defines a list of pipeline jobs. 11 | Jobs []*Job 12 | 13 | // Workflows are used to orchestrate jobs. 14 | Workflows struct { 15 | Version string 16 | List map[string]*Workflow `yaml:",inline"` 17 | } 18 | } 19 | 20 | // Workflow ochestrates one or more jobs. 21 | Workflow struct { 22 | Jobs []string 23 | } 24 | 25 | // Job defines a pipeline job. 26 | Job struct { 27 | // Name of the stage. 28 | Name string 29 | 30 | // Docker configures a Docker executor. 31 | Docker Docker 32 | 33 | // Environment variables passed to the executor. 34 | Environment map[string]string 35 | 36 | // Steps configures the Job steps. 37 | Steps map[string]Step 38 | 39 | // Branches limits execution by branch. 40 | Branches []struct { 41 | Only []string 42 | Ignore []string 43 | } 44 | } 45 | 46 | // Step defines a build execution unit. 47 | Step struct { 48 | Run Run 49 | AddSSHKeys map[string]interface{} `yaml:"add_ssh_keys"` 50 | AttachWorkspace map[string]interface{} `yaml:"attach_workspace"` 51 | Checkout map[string]interface{} `yaml:"checkout"` 52 | Deploy map[string]interface{} `yaml:"deploy"` 53 | PersistToWorkspace map[string]interface{} `yaml:"persist_to_workspace"` 54 | RestoreCache map[string]interface{} `yaml:"restore_cache"` 55 | SaveCache map[string]interface{} `yaml:"save_cache"` 56 | SetupRemoteDocker map[string]interface{} `yaml:"setup_remote_docker"` 57 | StoreArtifacts map[string]interface{} `yaml:"store_artifacts"` 58 | StoreTestResults map[string]interface{} `yaml:"store_test_results"` 59 | } 60 | ) 61 | 62 | // // UnmarshalYAML implements custom parsing for the stage section of the yaml 63 | // // to cleanup the structure a bit. 64 | // func (s *Stage) UnmarshalYAML(unmarshal func(interface{}) error) error { 65 | // in := []struct { 66 | // Step *Step 67 | // }{} 68 | // err := unmarshal(&in) 69 | // if err != nil { 70 | // return err 71 | // } 72 | // for _, step := range in { 73 | // s.Steps = append(s.Steps, step.Step) 74 | // } 75 | // return nil 76 | // } 77 | -------------------------------------------------------------------------------- /yaml/converter/circleci/docker.go: -------------------------------------------------------------------------------- 1 | package circleci 2 | 3 | // Docker configures a Docker executor. 4 | type Docker struct { 5 | // Image is the Docker image name. 6 | Image string 7 | 8 | // Name is the Docker container hostname. 9 | Name string 10 | 11 | // Entrypoint is the Docker container entrypoint. 12 | Entrypoint []string 13 | 14 | // Command is the Docker container command. 15 | Command []string 16 | 17 | // User is user that runs the Docker entrypoint. 18 | User string 19 | 20 | // Environment variables passed to the container. 21 | Environment map[string]string 22 | 23 | // Auth credentials to pull private images. 24 | Auth map[string]string 25 | 26 | // Auth credentials to pull private ECR images. 27 | AWSAuth map[string]string `yaml:"aws_auth"` 28 | } 29 | -------------------------------------------------------------------------------- /yaml/converter/circleci/docker_test.go: -------------------------------------------------------------------------------- 1 | package circleci 2 | -------------------------------------------------------------------------------- /yaml/converter/circleci/run.go: -------------------------------------------------------------------------------- 1 | package circleci 2 | 3 | import "time" 4 | 5 | // Run defines a command 6 | type Run struct { 7 | // Name of the command 8 | Name string 9 | 10 | // Command run in the shell. 11 | Command string 12 | 13 | // Shell to use to execute the command. 14 | Shell string 15 | 16 | // Workiring Directory in which the command 17 | // is run. 18 | WorkingDir string `yaml:"working_directory"` 19 | 20 | // Command is run in the background. 21 | Background bool `yaml:"background"` 22 | 23 | // Amount of time the command can run with 24 | // no output before being canceled. 25 | NoOutputTimeout time.Duration `yaml:"no_output_timeout"` 26 | 27 | // Environment variables set when running 28 | // the command in the shell. 29 | Environment map[string]string 30 | 31 | // Defines when the command should be executed. 32 | // Values are always, on_success, and on_fail. 33 | When string 34 | } 35 | -------------------------------------------------------------------------------- /yaml/converter/circleci/run_test.go: -------------------------------------------------------------------------------- 1 | package circleci 2 | 3 | const testRun = ` 4 | - run: 5 | name: test 6 | command: go test 7 | ` 8 | 9 | const testRunShort = ` 10 | - run: go test 11 | ` 12 | -------------------------------------------------------------------------------- /yaml/converter/circleci/testdata/sample1.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | backend: 4 | docker: 5 | - image: golang:1.8 6 | steps: 7 | - checkout 8 | - run: go build 9 | - run: go test 10 | frontend: 11 | docker: 12 | - image: node:latest 13 | steps: 14 | - checkout 15 | - run: npm install 16 | - run: npm test 17 | workflows: 18 | version: 2 19 | default: 20 | jobs: 21 | - backend 22 | - frontend -------------------------------------------------------------------------------- /yaml/converter/circleci/testdata/sample1.yml.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drone/drone-yaml/01fb17858c9bdf157c62f96fdd438e74c060b3b7/yaml/converter/circleci/testdata/sample1.yml.golden -------------------------------------------------------------------------------- /yaml/converter/convert.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | // +build !oss 6 | 7 | package converter 8 | 9 | import ( 10 | "github.com/drone/drone-yaml/yaml/converter/bitbucket" 11 | "github.com/drone/drone-yaml/yaml/converter/gitlab" 12 | "github.com/drone/drone-yaml/yaml/converter/legacy" 13 | ) 14 | 15 | // Convert converts the yaml configuration file from 16 | // the legacy format to the 1.0+ format. 17 | func Convert(d []byte, m Metadata) ([]byte, error) { 18 | switch m.Filename { 19 | case "bitbucket-pipelines.yml": 20 | return bitbucket.Convert(d, m.Ref) 21 | case "circle.yml", ".circleci/config.yml": 22 | // TODO(bradrydzewski) 23 | case ".gitlab-ci.yml": 24 | return gitlab.Convert(d) 25 | case ".travis.yml": 26 | // TODO(bradrydzewski) 27 | } 28 | // if the filename does not match any external 29 | // systems we check to see if the configuration 30 | // file is a legacy (pre 1.0) .drone.yml format. 31 | if legacy.Match(d) { 32 | return legacy.Convert(d, m.URL) 33 | } 34 | // else return the unmodified configuration 35 | // back to the caller. 36 | return d, nil 37 | } 38 | 39 | // ConvertString converts the yaml configuration file from 40 | // the legacy format to the 1.0+ format. 41 | func ConvertString(s string, m Metadata) (string, error) { 42 | b, err := Convert([]byte(s), m) 43 | return string(b), err 44 | } 45 | -------------------------------------------------------------------------------- /yaml/converter/convert_oss.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // +build oss 16 | 17 | package converter 18 | 19 | // Convert converts the yaml configuration file from 20 | // the legacy format to the 1.0+ format. 21 | func Convert(d []byte, m Metadata) ([]byte, error) { 22 | return d, nil 23 | } 24 | 25 | // ConvertString converts the yaml configuration file from 26 | // the legacy format to the 1.0+ format. 27 | func ConvertString(s string, m Metadata) (string, error) { 28 | return s, nil 29 | } 30 | -------------------------------------------------------------------------------- /yaml/converter/convert_oss_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // +build oss 16 | 17 | package converter 18 | -------------------------------------------------------------------------------- /yaml/converter/convert_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | // +build !oss 6 | 7 | package converter 8 | -------------------------------------------------------------------------------- /yaml/converter/gitlab/convert.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package gitlab 6 | 7 | import ( 8 | "bytes" 9 | "strings" 10 | 11 | droneyaml "github.com/drone/drone-yaml/yaml" 12 | "github.com/drone/drone-yaml/yaml/compiler/image" 13 | "github.com/drone/drone-yaml/yaml/pretty" 14 | 15 | "github.com/buildkite/yaml" 16 | ) 17 | 18 | // Convert converts the yaml configuration file from 19 | // the legacy format to the 1.0+ format. 20 | func Convert(b []byte) ([]byte, error) { 21 | config := new(Config) 22 | err := yaml.Unmarshal(b, config) 23 | if err != nil { 24 | return nil, err 25 | } 26 | manifest := &droneyaml.Manifest{} 27 | 28 | // if no stages are defined, we create a single, 29 | // default stage that will be used for all jobs. 30 | if len(config.Stages) == 0 { 31 | for name, job := range config.Jobs { 32 | config.Stages = append(config.Stages, name) 33 | job.Stage = name 34 | } 35 | } 36 | 37 | // create a new pipeline for each stage. 38 | var prevstage string 39 | for _, stage := range config.Stages { 40 | pipeline := &droneyaml.Pipeline{} 41 | pipeline.Name = stage 42 | pipeline.Kind = droneyaml.KindPipeline 43 | manifest.Resources = append(manifest.Resources, pipeline) 44 | for name, job := range config.Jobs { 45 | if job.Stage != stage { 46 | continue 47 | } 48 | cmds := []string(config.Before) 49 | cmds = append(cmds, []string(job.Before)...) 50 | cmds = append(cmds, []string(job.Script)...) 51 | cmds = append(cmds, []string(job.After)...) 52 | cmds = append(cmds, []string(config.After)...) 53 | 54 | step := &droneyaml.Container{ 55 | Name: name, 56 | Image: job.Image.Name, 57 | Command: job.Image.Command, 58 | Entrypoint: job.Image.Entrypoint, 59 | Commands: cmds, 60 | } 61 | 62 | if job.AllowFailure { 63 | step.Failure = "ignore" 64 | } 65 | 66 | if step.Image == "" { 67 | step.Image = config.Image.Name 68 | } 69 | // TODO: handle Services 70 | // TODO: handle Only 71 | // TODO: handle Except 72 | // TODO: handle Variables 73 | // TODO: handle When 74 | 75 | pipeline.Steps = append(pipeline.Steps, step) 76 | } 77 | 78 | for _, step := range config.Services { 79 | step := &droneyaml.Container{ 80 | Name: step.Alias, 81 | Image: step.Name, 82 | Command: step.Command, 83 | Entrypoint: step.Entrypoint, 84 | } 85 | if step.Name == "" { 86 | step.Name = serviceSlug(step.Image) 87 | } 88 | pipeline.Services = append(pipeline.Services, step) 89 | } 90 | 91 | if prevstage != "" { 92 | pipeline.DependsOn = []string{prevstage} 93 | } 94 | prevstage = stage 95 | } 96 | 97 | buf := new(bytes.Buffer) 98 | pretty.Print(buf, manifest) 99 | return buf.Bytes(), nil 100 | } 101 | 102 | func serviceSlug(s string) string { 103 | s = image.Trim(s) 104 | s = strings.Replace(s, "/", "__", -1) 105 | return s 106 | } 107 | -------------------------------------------------------------------------------- /yaml/converter/gitlab/convert_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package gitlab 6 | 7 | import ( 8 | "bytes" 9 | "io/ioutil" 10 | "testing" 11 | 12 | "github.com/sergi/go-diff/diffmatchpatch" 13 | ) 14 | 15 | func TestConvert(t *testing.T) { 16 | tests := []struct { 17 | before, after, ref string 18 | }{ 19 | { 20 | before: "testdata/example1.yml", 21 | after: "testdata/example1.yml.golden", 22 | }, 23 | { 24 | before: "testdata/example2.yml", 25 | after: "testdata/example2.yml.golden", 26 | }, 27 | { 28 | before: "testdata/example3.yml", 29 | after: "testdata/example3.yml.golden", 30 | }, 31 | { 32 | before: "testdata/example4.yml", 33 | after: "testdata/example4.yml.golden", 34 | }, 35 | } 36 | 37 | for _, test := range tests { 38 | a, err := ioutil.ReadFile(test.before) 39 | if err != nil { 40 | t.Error(err) 41 | return 42 | } 43 | b, err := ioutil.ReadFile(test.after) 44 | if err != nil { 45 | t.Error(err) 46 | return 47 | } 48 | c, err := Convert([]byte(a)) 49 | if err != nil { 50 | t.Error(err) 51 | return 52 | } 53 | 54 | if bytes.Equal(b, c) == false { 55 | t.Errorf("Unexpected yaml conversion of %s", test.before) 56 | dmp := diffmatchpatch.New() 57 | diffs := dmp.DiffMain(string(b), string(c), false) 58 | t.Log(dmp.DiffCleanupSemantic(diffs)) 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /yaml/converter/gitlab/testdata/example1.yml: -------------------------------------------------------------------------------- 1 | image: ruby:2.2 2 | 3 | services: 4 | - postgres:9.3 5 | 6 | before_script: 7 | - bundle install 8 | 9 | test: 10 | script: 11 | - bundle exec rake spec 12 | -------------------------------------------------------------------------------- /yaml/converter/gitlab/testdata/example1.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: test 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: test 11 | image: ruby:2.2 12 | commands: 13 | - bundle install 14 | - bundle exec rake spec 15 | 16 | services: 17 | - name: postgres 18 | image: postgres:9.3 19 | 20 | ... 21 | -------------------------------------------------------------------------------- /yaml/converter/gitlab/testdata/example2.yml: -------------------------------------------------------------------------------- 1 | before_script: 2 | - bundle install 3 | 4 | test2.1: 5 | image: ruby:2.1 6 | services: 7 | - postgres:9.3 8 | script: 9 | - bundle exec rake spec 10 | 11 | test2.2: 12 | image: ruby:2.2 13 | services: 14 | - postgres:9.4 15 | script: 16 | - bundle exec rake spec -------------------------------------------------------------------------------- /yaml/converter/gitlab/testdata/example2.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: test2.1 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: test2.1 11 | image: ruby:2.1 12 | commands: 13 | - bundle install 14 | - bundle exec rake spec 15 | 16 | --- 17 | kind: pipeline 18 | name: test2.2 19 | 20 | platform: 21 | os: linux 22 | arch: amd64 23 | 24 | steps: 25 | - name: test2.2 26 | image: ruby:2.2 27 | commands: 28 | - bundle install 29 | - bundle exec rake spec 30 | 31 | depends_on: 32 | - test2.1 33 | 34 | ... 35 | -------------------------------------------------------------------------------- /yaml/converter/gitlab/testdata/example3.yml: -------------------------------------------------------------------------------- 1 | image: 2 | name: ruby:2.2 3 | entrypoint: ["/bin/bash"] 4 | 5 | services: 6 | - name: my-postgres:9.4 7 | alias: db-postgres 8 | entrypoint: ["/usr/local/bin/db-postgres"] 9 | command: ["start"] 10 | 11 | before_script: 12 | - bundle install 13 | 14 | test: 15 | script: 16 | - bundle exec rake spec -------------------------------------------------------------------------------- /yaml/converter/gitlab/testdata/example3.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: test 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: test 11 | image: ruby:2.2 12 | commands: 13 | - bundle install 14 | - bundle exec rake spec 15 | 16 | services: 17 | - name: db-postgres 18 | image: my-postgres:9.4 19 | entrypoint: 20 | - /usr/local/bin/db-postgres 21 | command: 22 | - start 23 | 24 | ... 25 | -------------------------------------------------------------------------------- /yaml/converter/gitlab/testdata/example4.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - build 3 | - test 4 | - deploy 5 | 6 | image: ruby:2.2 7 | 8 | job 1: 9 | stage: build 10 | script: make build dependencies 11 | 12 | job 2: 13 | stage: build 14 | script: make build artifacts 15 | 16 | job 3: 17 | stage: test 18 | script: make test 19 | 20 | job 4: 21 | stage: deploy 22 | script: make deploy 23 | -------------------------------------------------------------------------------- /yaml/converter/gitlab/testdata/example4.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: build 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: job 1 11 | image: ruby:2.2 12 | commands: 13 | - make build dependencies 14 | 15 | - name: job 2 16 | image: ruby:2.2 17 | commands: 18 | - make build artifacts 19 | 20 | --- 21 | kind: pipeline 22 | name: test 23 | 24 | platform: 25 | os: linux 26 | arch: amd64 27 | 28 | steps: 29 | - name: job 3 30 | image: ruby:2.2 31 | commands: 32 | - make test 33 | 34 | depends_on: 35 | - build 36 | 37 | --- 38 | kind: pipeline 39 | name: deploy 40 | 41 | platform: 42 | os: linux 43 | arch: amd64 44 | 45 | steps: 46 | - name: job 4 47 | image: ruby:2.2 48 | commands: 49 | - make deploy 50 | 51 | depends_on: 52 | - test 53 | 54 | ... 55 | -------------------------------------------------------------------------------- /yaml/converter/internal/string_slice.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package internal 6 | 7 | // StringSlice represents a slice of strings or a string. 8 | type StringSlice []string 9 | 10 | // UnmarshalYAML implements the Unmarshaller interface. 11 | func (s *StringSlice) UnmarshalYAML(unmarshal func(interface{}) error) error { 12 | var stringType string 13 | if err := unmarshal(&stringType); err == nil { 14 | *s = []string{stringType} 15 | return nil 16 | } 17 | 18 | var sliceType []string 19 | if err := unmarshal(&sliceType); err != nil { 20 | return err 21 | } 22 | *s = sliceType 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /yaml/converter/internal/string_slice_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package internal 6 | 7 | import ( 8 | "reflect" 9 | "testing" 10 | 11 | "github.com/buildkite/yaml" 12 | ) 13 | 14 | func TestStringSlice(t *testing.T) { 15 | var tests = []struct { 16 | yaml string 17 | want []string 18 | }{ 19 | { 20 | yaml: "hello world", 21 | want: []string{"hello world"}, 22 | }, 23 | { 24 | yaml: "[ hello, world ]", 25 | want: []string{"hello", "world"}, 26 | }, 27 | { 28 | yaml: "42", 29 | want: []string{"42"}, 30 | }, 31 | } 32 | 33 | for _, test := range tests { 34 | var got StringSlice 35 | 36 | if err := yaml.Unmarshal([]byte(test.yaml), &got); err != nil { 37 | t.Error(err) 38 | } 39 | 40 | if !reflect.DeepEqual([]string(got), test.want) { 41 | t.Errorf("Got slice %v want %v", got, test.want) 42 | } 43 | } 44 | 45 | var got StringSlice 46 | if err := yaml.Unmarshal([]byte("{}"), &got); err == nil { 47 | t.Errorf("Want error unmarshaling invalid string or slice value.") 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /yaml/converter/legacy/convert.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package legacy 6 | 7 | import "github.com/drone/drone-yaml/yaml/converter/legacy/internal" 8 | 9 | // Convert converts the yaml configuration file from 10 | // the legacy format to the 1.0+ format. 11 | func Convert(d []byte, remote string) ([]byte, error) { 12 | return yaml.Convert(d, remote) 13 | } 14 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/config_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | import ( 8 | "bytes" 9 | "io/ioutil" 10 | "testing" 11 | 12 | "github.com/sergi/go-diff/diffmatchpatch" 13 | ) 14 | 15 | func TestConvert(t *testing.T) { 16 | tests := []struct { 17 | before, after, url string 18 | }{ 19 | { 20 | before: "testdata/simple.yml", 21 | after: "testdata/simple.yml.golden", 22 | }, 23 | { 24 | before: "testdata/branches.yml", 25 | after: "testdata/branches.yml.golden", 26 | }, 27 | { 28 | before: "testdata/tags.yml", 29 | after: "testdata/tags.yml.golden", 30 | }, 31 | { 32 | before: "testdata/vault_1.yml", 33 | after: "testdata/vault_1.yml.golden", 34 | }, 35 | { 36 | before: "testdata/vault_2.yml", 37 | after: "testdata/vault_2.yml.golden", 38 | }, 39 | { 40 | before: "testdata/vault_3.yml", 41 | after: "testdata/vault_3.yml.golden", 42 | }, 43 | { 44 | before: "testdata/matrix_1.yml", 45 | after: "testdata/matrix_1.yml.golden", 46 | }, 47 | { 48 | before: "testdata/matrix_2.yml", 49 | after: "testdata/matrix_2.yml.golden", 50 | }, 51 | } 52 | 53 | for _, test := range tests { 54 | a, err := ioutil.ReadFile(test.before) 55 | if err != nil { 56 | t.Error(err) 57 | return 58 | } 59 | b, err := ioutil.ReadFile(test.after) 60 | if err != nil { 61 | t.Error(err) 62 | return 63 | } 64 | c, err := Convert(a, test.url) 65 | if err != nil { 66 | t.Error(err) 67 | return 68 | } 69 | if bytes.Equal(b, c) == false { 70 | t.Errorf("Unexpected yaml conversion of %s", test.before) 71 | dmp := diffmatchpatch.New() 72 | diffs := dmp.DiffMain(string(b), string(c), false) 73 | t.Log(dmp.DiffCleanupSemantic(diffs)) 74 | } 75 | } 76 | } 77 | 78 | func TestWorkspacePath(t *testing.T) { 79 | tests := []struct{ 80 | a string 81 | b string 82 | }{ 83 | { 84 | a: "", 85 | b: "src", 86 | }, 87 | { 88 | a: "https://github.com/octocat/hello-world", 89 | b: "src/github.com/octocat/hello-world", 90 | }, 91 | { 92 | a: "https://github.com:80/octocat/hello-world", 93 | b: "src/github.com/octocat/hello-world", 94 | }, 95 | { 96 | a: "github.com:80/octocat/hello-world", 97 | b: "src", 98 | }, 99 | } 100 | for _, test := range tests { 101 | if got, want := toWorkspacePath(test.a), test.b; got != want { 102 | t.Errorf("Want workspace path %s, got %s", want, got) 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/constraint.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | type ( 8 | // Constraints defines a set of runtime constraints. 9 | Constraints struct { 10 | Ref Constraint 11 | Repo Constraint 12 | Instance Constraint 13 | Environment Constraint 14 | Event Constraint 15 | Branch Constraint 16 | Status Constraint 17 | Matrix map[string]string 18 | } 19 | 20 | // Constraint defines a runtime constraint. 21 | Constraint struct { 22 | Include []string 23 | Exclude []string 24 | } 25 | 26 | // ConstraintMap defines a runtime constraint map. 27 | ConstraintMap struct { 28 | Include map[string]string 29 | Exclude map[string]string 30 | } 31 | ) 32 | 33 | // UnmarshalYAML unmarshals the constraint. 34 | func (c *Constraint) UnmarshalYAML(unmarshal func(interface{}) error) error { 35 | var out1 = struct { 36 | Include StringSlice 37 | Exclude StringSlice 38 | }{} 39 | 40 | var out2 StringSlice 41 | 42 | unmarshal(&out1) 43 | unmarshal(&out2) 44 | 45 | c.Exclude = out1.Exclude 46 | c.Include = append( 47 | out1.Include, 48 | out2..., 49 | ) 50 | return nil 51 | } 52 | 53 | // UnmarshalYAML unmarshals the constraint map. 54 | func (c *ConstraintMap) UnmarshalYAML(unmarshal func(interface{}) error) error { 55 | out1 := struct { 56 | Include map[string]string 57 | Exclude map[string]string 58 | }{ 59 | Include: map[string]string{}, 60 | Exclude: map[string]string{}, 61 | } 62 | 63 | out2 := map[string]string{} 64 | 65 | unmarshal(&out1) 66 | unmarshal(&out2) 67 | 68 | c.Include = out1.Include 69 | c.Exclude = out1.Exclude 70 | for k, v := range out2 { 71 | c.Include[k] = v 72 | } 73 | return nil 74 | } 75 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/container.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/buildkite/yaml" 11 | ) 12 | 13 | type ( 14 | // Containers represents an ordered list of containers. 15 | Containers struct { 16 | Containers []*Container 17 | } 18 | 19 | // Container represents a Docker container. 20 | Container struct { 21 | Command StringSlice `yaml:"command,omitempty"` 22 | Commands StringSlice `yaml:"commands,omitempty"` 23 | Detached bool `yaml:"detach,omitempty"` 24 | Devices []string `yaml:"devices,omitempty"` 25 | ErrIgnore bool `yaml:"allow_failure,omitempty"` 26 | Tmpfs []string `yaml:"tmpfs,omitempty"` 27 | DNS StringSlice `yaml:"dns,omitempty"` 28 | DNSSearch StringSlice `yaml:"dns_search,omitempty"` 29 | Entrypoint StringSlice `yaml:"entrypoint,omitempty"` 30 | Environment SliceMap `yaml:"environment,omitempty"` 31 | ExtraHosts []string `yaml:"extra_hosts,omitempty"` 32 | Image string `yaml:"image,omitempty"` 33 | Name string `yaml:"name,omitempty"` 34 | Privileged bool `yaml:"privileged,omitempty"` 35 | Pull bool `yaml:"pull,omitempty"` 36 | Shell string `yaml:"shell,omitempty"` 37 | Volumes []*Volume `yaml:"volumes,omitempty"` 38 | Secrets Secrets `yaml:"secrets,omitempty"` 39 | Constraints Constraints `yaml:"when,omitempty"` 40 | Vargs map[string]interface{} `yaml:",inline"` 41 | } 42 | ) 43 | 44 | // UnmarshalYAML implements the Unmarshaller interface. 45 | func (c *Containers) UnmarshalYAML(unmarshal func(interface{}) error) error { 46 | slice := yaml.MapSlice{} 47 | if err := unmarshal(&slice); err != nil { 48 | return err 49 | } 50 | 51 | for _, s := range slice { 52 | container := Container{} 53 | out, _ := yaml.Marshal(s.Value) 54 | 55 | if err := yaml.Unmarshal(out, &container); err != nil { 56 | return err 57 | } 58 | container.Name = fmt.Sprintf("%v", s.Key) 59 | c.Containers = append(c.Containers, &container) 60 | } 61 | return nil 62 | } 63 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/container_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/secret.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | type ( 8 | // Secrets represents a list of container secrets. 9 | Secrets struct { 10 | Secrets []*Secret 11 | } 12 | 13 | // Secret represents a container secret. 14 | Secret struct { 15 | Source string 16 | Target string 17 | } 18 | ) 19 | 20 | // UnmarshalYAML implements the Unmarshaller interface. 21 | func (s *Secrets) UnmarshalYAML(unmarshal func(interface{}) error) error { 22 | var strslice []string 23 | err := unmarshal(&strslice) 24 | if err == nil { 25 | for _, str := range strslice { 26 | s.Secrets = append(s.Secrets, &Secret{ 27 | Source: str, 28 | Target: str, 29 | }) 30 | } 31 | return nil 32 | } 33 | return unmarshal(&s.Secrets) 34 | } 35 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/secret_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | import ( 8 | "reflect" 9 | "testing" 10 | 11 | "github.com/buildkite/yaml" 12 | ) 13 | 14 | func TestUnmarshalSecrets(t *testing.T) { 15 | testdata := []struct { 16 | from string 17 | want []*Secret 18 | }{ 19 | { 20 | from: "[ mysql_username, mysql_password]", 21 | want: []*Secret{ 22 | { 23 | Source: "mysql_username", 24 | Target: "mysql_username", 25 | }, 26 | { 27 | Source: "mysql_password", 28 | Target: "mysql_password", 29 | }, 30 | }, 31 | }, 32 | { 33 | from: "[ { source: mysql_prod_username, target: mysql_username } ]", 34 | want: []*Secret{ 35 | { 36 | Source: "mysql_prod_username", 37 | Target: "mysql_username", 38 | }, 39 | }, 40 | }, 41 | { 42 | from: "[ { source: mysql_prod_username, target: mysql_username }, { source: redis_username, target: redis_username } ]", 43 | want: []*Secret{ 44 | { 45 | Source: "mysql_prod_username", 46 | Target: "mysql_username", 47 | }, 48 | { 49 | Source: "redis_username", 50 | Target: "redis_username", 51 | }, 52 | }, 53 | }, 54 | } 55 | 56 | for _, test := range testdata { 57 | in := []byte(test.from) 58 | got := Secrets{} 59 | err := yaml.Unmarshal(in, &got) 60 | if err != nil { 61 | t.Error(err) 62 | } else if !reflect.DeepEqual(test.want, got.Secrets) { 63 | t.Errorf("got secret %v want %v", got.Secrets, test.want) 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/slice_map.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | import "strings" 8 | 9 | // SliceMap represents a slice or map of key pairs. 10 | type SliceMap struct { 11 | Map map[string]string 12 | } 13 | 14 | // UnmarshalYAML implements custom Yaml unmarshaling. 15 | func (s *SliceMap) UnmarshalYAML(unmarshal func(interface{}) error) error { 16 | s.Map = map[string]string{} 17 | err := unmarshal(&s.Map) 18 | if err == nil { 19 | return nil 20 | } 21 | 22 | var slice []string 23 | err = unmarshal(&slice) 24 | if err != nil { 25 | return err 26 | } 27 | for _, v := range slice { 28 | parts := strings.SplitN(v, "=", 2) 29 | if len(parts) == 2 { 30 | key := parts[0] 31 | val := parts[1] 32 | s.Map[key] = val 33 | } 34 | } 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/slice_map_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | import ( 8 | "reflect" 9 | "testing" 10 | 11 | "github.com/buildkite/yaml" 12 | ) 13 | 14 | func TestMapSlice(t *testing.T) { 15 | var tests = []struct { 16 | yaml string 17 | want map[string]string 18 | }{ 19 | { 20 | yaml: "[ foo=bar, baz=qux ]", 21 | want: map[string]string{"foo": "bar", "baz": "qux"}, 22 | }, 23 | { 24 | yaml: "{ foo: bar, baz: qux }", 25 | want: map[string]string{"foo": "bar", "baz": "qux"}, 26 | }, 27 | } 28 | 29 | for _, test := range tests { 30 | var got SliceMap 31 | 32 | if err := yaml.Unmarshal([]byte(test.yaml), &got); err != nil { 33 | t.Error(err) 34 | } 35 | 36 | if !reflect.DeepEqual(got.Map, test.want) { 37 | t.Errorf("Got map %v want %v", got, test.want) 38 | } 39 | } 40 | 41 | var got SliceMap 42 | if err := yaml.Unmarshal([]byte("1"), &got); err == nil { 43 | t.Errorf("Want error unmarshaling invalid map value.") 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/string_slice.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | // StringSlice represents a slice of strings or a string. 8 | type StringSlice []string 9 | 10 | // UnmarshalYAML implements the Unmarshaller interface. 11 | func (s *StringSlice) UnmarshalYAML(unmarshal func(interface{}) error) error { 12 | var stringType string 13 | if err := unmarshal(&stringType); err == nil { 14 | *s = []string{stringType} 15 | return nil 16 | } 17 | 18 | var sliceType []string 19 | if err := unmarshal(&sliceType); err != nil { 20 | return err 21 | } 22 | *s = sliceType 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/string_slice_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | import ( 8 | "reflect" 9 | "testing" 10 | 11 | "github.com/buildkite/yaml" 12 | ) 13 | 14 | func TestStringSlice(t *testing.T) { 15 | var tests = []struct { 16 | yaml string 17 | want []string 18 | }{ 19 | { 20 | yaml: "hello world", 21 | want: []string{"hello world"}, 22 | }, 23 | { 24 | yaml: "[ hello, world ]", 25 | want: []string{"hello", "world"}, 26 | }, 27 | { 28 | yaml: "42", 29 | want: []string{"42"}, 30 | }, 31 | } 32 | 33 | for _, test := range tests { 34 | var got StringSlice 35 | 36 | if err := yaml.Unmarshal([]byte(test.yaml), &got); err != nil { 37 | t.Error(err) 38 | } 39 | 40 | if !reflect.DeepEqual([]string(got), test.want) { 41 | t.Errorf("Got slice %v want %v", got, test.want) 42 | } 43 | } 44 | 45 | var got StringSlice 46 | if err := yaml.Unmarshal([]byte("{}"), &got); err == nil { 47 | t.Errorf("Want error unmarshaling invalid string or slice value.") 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/testdata/branches.yml: -------------------------------------------------------------------------------- 1 | branches: 2 | - master 3 | 4 | pipeline: 5 | greeting: 6 | image: alpine 7 | commands: 8 | - echo hello 9 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/testdata/branches.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: greeting 11 | pull: if-not-exists 12 | image: alpine 13 | commands: 14 | - echo hello 15 | 16 | trigger: 17 | ref: 18 | - refs/pull/** 19 | - refs/pull-requests/** 20 | - refs/merge-requests/** 21 | - refs/heads/master 22 | 23 | ... 24 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/testdata/matrix_1.yml: -------------------------------------------------------------------------------- 1 | 2 | pipeline: 3 | test: 4 | image: golang:${GO_VERSION} 5 | commands: 6 | - go test -v ./... 7 | 8 | services: 9 | redis: 10 | image: redis:2.6 11 | 12 | matrix: 13 | include: 14 | - GO_VERSION: 1.11 15 | REDIS_VERSION: 2.6 16 | - GO_VERSION: 1.10 17 | REDIS_VERSION: 2.6 18 | - GO_VERSION: 1.9 19 | REDIS_VERSION: 2.6 20 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/testdata/matrix_1.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: matrix-1 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: test 11 | pull: if-not-exists 12 | image: golang:1.11 13 | commands: 14 | - go test -v ./... 15 | 16 | services: 17 | - name: redis 18 | pull: if-not-exists 19 | image: redis:2.6 20 | 21 | --- 22 | kind: pipeline 23 | name: matrix-2 24 | 25 | platform: 26 | os: linux 27 | arch: amd64 28 | 29 | steps: 30 | - name: test 31 | pull: if-not-exists 32 | image: golang:1.10 33 | commands: 34 | - go test -v ./... 35 | 36 | services: 37 | - name: redis 38 | pull: if-not-exists 39 | image: redis:2.6 40 | 41 | --- 42 | kind: pipeline 43 | name: matrix-3 44 | 45 | platform: 46 | os: linux 47 | arch: amd64 48 | 49 | steps: 50 | - name: test 51 | pull: if-not-exists 52 | image: golang:1.9 53 | commands: 54 | - go test -v ./... 55 | 56 | services: 57 | - name: redis 58 | pull: if-not-exists 59 | image: redis:2.6 60 | 61 | ... 62 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/testdata/matrix_2.yml: -------------------------------------------------------------------------------- 1 | 2 | pipeline: 3 | test: 4 | image: golang:${GO_VERSION} 5 | commands: 6 | - go test -v ./... 7 | 8 | services: 9 | redis: 10 | image: redis:${REDIS_VERSION} 11 | 12 | matrix: 13 | GO_VERSION: 14 | - 1.11 15 | - 1.10 16 | 17 | REDIS_VERSION: 18 | - 2.6 19 | - 2.8 20 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/testdata/matrix_2.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: matrix-1 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: test 11 | pull: if-not-exists 12 | image: golang:1.11 13 | commands: 14 | - go test -v ./... 15 | 16 | services: 17 | - name: redis 18 | pull: if-not-exists 19 | image: redis:2.6 20 | 21 | --- 22 | kind: pipeline 23 | name: matrix-2 24 | 25 | platform: 26 | os: linux 27 | arch: amd64 28 | 29 | steps: 30 | - name: test 31 | pull: if-not-exists 32 | image: golang:1.11 33 | commands: 34 | - go test -v ./... 35 | 36 | services: 37 | - name: redis 38 | pull: if-not-exists 39 | image: redis:2.8 40 | 41 | --- 42 | kind: pipeline 43 | name: matrix-3 44 | 45 | platform: 46 | os: linux 47 | arch: amd64 48 | 49 | steps: 50 | - name: test 51 | pull: if-not-exists 52 | image: golang:1.10 53 | commands: 54 | - go test -v ./... 55 | 56 | services: 57 | - name: redis 58 | pull: if-not-exists 59 | image: redis:2.6 60 | 61 | --- 62 | kind: pipeline 63 | name: matrix-4 64 | 65 | platform: 66 | os: linux 67 | arch: amd64 68 | 69 | steps: 70 | - name: test 71 | pull: if-not-exists 72 | image: golang:1.10 73 | commands: 74 | - go test -v ./... 75 | 76 | services: 77 | - name: redis 78 | pull: if-not-exists 79 | image: redis:2.8 80 | 81 | ... 82 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/testdata/simple.yml: -------------------------------------------------------------------------------- 1 | workspace: 2 | base: /go 3 | path: src/github.com/octocat/hello-world 4 | 5 | pipeline: 6 | build: 7 | image: golang 8 | commands: 9 | - go get 10 | - go build 11 | volumes: 12 | - /tmp/go:/go/bin 13 | environment: 14 | - GOOS=linux 15 | - GOARCH=amd64 16 | 17 | test: 18 | image: golang:latest 19 | volumes: 20 | - /tmp/go:/go/bin 21 | commands: 22 | - go test -v 23 | 24 | docker: 25 | image: plugins/docker 26 | secrets: 27 | - docker_username 28 | - docker_password 29 | repo: octocat/hello-world 30 | when: 31 | branch: master 32 | 33 | slack: 34 | image: plugins/slack 35 | secrets: 36 | - source: token 37 | target: slack_token 38 | channel: general 39 | 40 | services: 41 | database: 42 | image: mysql 43 | environment: 44 | MYSQL_USERNAME: foo 45 | MYSQL_PASSWORD: bar 46 | 47 | branches: 48 | - master -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/testdata/simple.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | workspace: 10 | base: /go 11 | path: src/github.com/octocat/hello-world 12 | 13 | steps: 14 | - name: build 15 | pull: if-not-exists 16 | image: golang 17 | commands: 18 | - go get 19 | - go build 20 | environment: 21 | GOARCH: amd64 22 | GOOS: linux 23 | volumes: 24 | - name: 2f746d702f676f 25 | path: /go/bin 26 | 27 | - name: test 28 | pull: if-not-exists 29 | image: golang:latest 30 | commands: 31 | - go test -v 32 | volumes: 33 | - name: 2f746d702f676f 34 | path: /go/bin 35 | 36 | - name: docker 37 | pull: if-not-exists 38 | image: plugins/docker 39 | settings: 40 | repo: octocat/hello-world 41 | environment: 42 | DOCKER_PASSWORD: 43 | from_secret: docker_password 44 | DOCKER_USERNAME: 45 | from_secret: docker_username 46 | when: 47 | branch: 48 | - master 49 | 50 | - name: slack 51 | pull: if-not-exists 52 | image: plugins/slack 53 | settings: 54 | channel: general 55 | environment: 56 | SLACK_TOKEN: 57 | from_secret: token 58 | 59 | services: 60 | - name: database 61 | pull: if-not-exists 62 | image: mysql 63 | environment: 64 | MYSQL_PASSWORD: bar 65 | MYSQL_USERNAME: foo 66 | 67 | volumes: 68 | - name: 2f746d702f676f 69 | host: 70 | path: /tmp/go 71 | 72 | trigger: 73 | ref: 74 | - refs/pull/** 75 | - refs/pull-requests/** 76 | - refs/merge-requests/** 77 | - refs/heads/master 78 | 79 | ... 80 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/testdata/tags.yml: -------------------------------------------------------------------------------- 1 | branches: 2 | - master 3 | 4 | pipeline: 5 | greeting: 6 | image: alpine 7 | commands: 8 | - echo hello 9 | when: 10 | event: [ tag, push ] -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/testdata/tags.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: greeting 11 | pull: if-not-exists 12 | image: alpine 13 | commands: 14 | - echo hello 15 | when: 16 | event: 17 | - tag 18 | - push 19 | 20 | trigger: 21 | ref: 22 | - refs/pull/** 23 | - refs/pull-requests/** 24 | - refs/merge-requests/** 25 | - refs/heads/master 26 | - refs/tags/** 27 | 28 | ... 29 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/testdata/vault_1.yml: -------------------------------------------------------------------------------- 1 | pipeline: 2 | docker: 3 | image: plugins/docker 4 | secrets: [ docker_username, docker_password ] 5 | repo: octocat/hello-world 6 | 7 | secrets: 8 | docker_username: 9 | driver: vault 10 | driver_opts: 11 | path: secret/docker/username 12 | docker_password: 13 | driver: vault 14 | driver_opts: 15 | path: secret/docker 16 | key: password 17 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/testdata/vault_1.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: docker 11 | pull: if-not-exists 12 | image: plugins/docker 13 | settings: 14 | repo: octocat/hello-world 15 | environment: 16 | DOCKER_PASSWORD: 17 | from_secret: docker_password 18 | DOCKER_USERNAME: 19 | from_secret: docker_username 20 | 21 | --- 22 | kind: secret 23 | name: docker_password 24 | 25 | get: 26 | path: secret/docker 27 | name: password 28 | 29 | --- 30 | kind: secret 31 | name: docker_username 32 | 33 | get: 34 | path: secret/docker/username 35 | 36 | ... 37 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/testdata/vault_2.yml: -------------------------------------------------------------------------------- 1 | pipeline: 2 | docker: 3 | image: plugins/docker 4 | secrets: [ docker_username, docker_password ] 5 | repo: octocat/hello-world 6 | 7 | secrets: 8 | docker_username: 9 | path: secret/docker/username 10 | docker_password: 11 | path: secret/docker/password 12 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/testdata/vault_2.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: docker 11 | pull: if-not-exists 12 | image: plugins/docker 13 | settings: 14 | repo: octocat/hello-world 15 | environment: 16 | DOCKER_PASSWORD: 17 | from_secret: docker_password 18 | DOCKER_USERNAME: 19 | from_secret: docker_username 20 | 21 | --- 22 | kind: secret 23 | name: docker_password 24 | 25 | get: 26 | path: secret/docker/password 27 | 28 | --- 29 | kind: secret 30 | name: docker_username 31 | 32 | get: 33 | path: secret/docker/username 34 | 35 | ... 36 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/testdata/vault_3.yml: -------------------------------------------------------------------------------- 1 | pipeline: 2 | docker: 3 | image: plugins/docker 4 | secrets: [ docker_username, docker_password ] 5 | repo: octocat/hello-world 6 | 7 | secrets: 8 | docker_username: 9 | vault: secret/docker/username 10 | docker_password: 11 | vault: secret/docker/password 12 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/testdata/vault_3.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: docker 11 | pull: if-not-exists 12 | image: plugins/docker 13 | settings: 14 | repo: octocat/hello-world 15 | environment: 16 | DOCKER_PASSWORD: 17 | from_secret: docker_password 18 | DOCKER_USERNAME: 19 | from_secret: docker_username 20 | 21 | --- 22 | kind: secret 23 | name: docker_password 24 | 25 | get: 26 | path: secret/docker/password 27 | 28 | --- 29 | kind: secret 30 | name: docker_username 31 | 32 | get: 33 | path: secret/docker/username 34 | 35 | ... 36 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/volume.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | import "strings" 8 | 9 | // Volume represent a container volume. 10 | type Volume struct { 11 | Source string 12 | Destination string 13 | ReadOnly bool 14 | } 15 | 16 | // UnmarshalYAML implements the Unmarshaller interface. 17 | func (v *Volume) UnmarshalYAML(unmarshal func(interface{}) error) error { 18 | var stringType string 19 | if err := unmarshal(&stringType); err != nil { 20 | return err 21 | } 22 | parts := strings.SplitN(stringType, ":", 3) 23 | switch { 24 | case len(parts) == 2: 25 | v.Source = parts[0] 26 | v.Destination = parts[1] 27 | case len(parts) == 3: 28 | v.Source = parts[0] 29 | v.Destination = parts[1] 30 | v.ReadOnly = parts[2] == "ro" 31 | } 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/volume_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | import ( 8 | "reflect" 9 | "testing" 10 | 11 | "github.com/buildkite/yaml" 12 | ) 13 | 14 | func TestVolume(t *testing.T) { 15 | var tests = []struct { 16 | yaml string 17 | want Volume 18 | }{ 19 | { 20 | yaml: "/opt/data:/var/lib/mysql", 21 | want: Volume{Source: "/opt/data", Destination: "/var/lib/mysql"}, 22 | }, 23 | { 24 | yaml: "/opt/data:/var/lib/mysql:ro", 25 | want: Volume{Source: "/opt/data", Destination: "/var/lib/mysql", ReadOnly: true}, 26 | }, 27 | { 28 | yaml: "/opt/data:/var/lib/mysql", 29 | want: Volume{Source: "/opt/data", Destination: "/var/lib/mysql", ReadOnly: false}, 30 | }, 31 | } 32 | 33 | for _, test := range tests { 34 | got := Volume{} 35 | if err := yaml.Unmarshal([]byte(test.yaml), &got); err != nil { 36 | t.Errorf("got error unmarshaling volume %q", test.yaml) 37 | } 38 | if !reflect.DeepEqual(got, test.want) { 39 | t.Errorf("got volume %v want %v", got, test.want) 40 | } 41 | } 42 | 43 | var got Volume 44 | if err := yaml.Unmarshal([]byte("{}"), &got); err == nil { 45 | t.Errorf("Want error unmarshaling invalid volume string.") 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /yaml/converter/legacy/internal/yaml.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | import ( 8 | "bytes" 9 | 10 | "github.com/vinzenz/yaml" 11 | ) 12 | 13 | type temporary struct { 14 | Attributes map[string]interface{} `yaml:",inline"` 15 | Pipeline yaml.MapSlice `yaml:"pipeline"` 16 | } 17 | 18 | // this is a helper function that expands merge keys 19 | func expandMergeKeys(b []byte) ([]byte, error) { 20 | v := new(temporary) 21 | if err := yaml.Unmarshal(b, v); err != nil { 22 | return b, err 23 | } 24 | o, err := yaml.Marshal(v) 25 | if err != nil { 26 | return b, err 27 | } 28 | return o, nil 29 | } 30 | 31 | func hasMergeKeys(b []byte) bool { 32 | return bytes.Contains(b, []byte("<<:")) 33 | } 34 | -------------------------------------------------------------------------------- /yaml/converter/legacy/match.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package legacy 6 | 7 | import ( 8 | "regexp" 9 | ) 10 | 11 | var re = regexp.MustCompile(`(?m)^pipeline:(\s+)?$`) 12 | 13 | // Match returns true if the yaml configuration file 14 | // is legacy and requires converstion. 15 | func Match(b []byte) bool { 16 | matches := re.FindAll(b, -1) 17 | return len(matches) != 0 18 | } 19 | -------------------------------------------------------------------------------- /yaml/converter/legacy/match_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package legacy 6 | 7 | import "testing" 8 | 9 | func TestMatch(t *testing.T) { 10 | tests := []struct { 11 | config string 12 | result bool 13 | }{ 14 | { 15 | config: "pipeline:\n build:\n image: golang:1.11", 16 | result: true, 17 | }, 18 | { 19 | config: "\n\npipeline:\n", 20 | result: true, 21 | }, 22 | { 23 | config: "\n\npipeline: \n", 24 | result: true, 25 | }, 26 | { 27 | config: "---\nkind: pipeline\n", 28 | result: false, 29 | }, 30 | } 31 | for i, test := range tests { 32 | b := []byte(test.config) 33 | if got, want := Match(b), test.result; got != want { 34 | t.Errorf("Want IsLegacyBytes %v at index %d,", want, i) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /yaml/converter/legacy/testdata/legacy.yml: -------------------------------------------------------------------------------- 1 | 2 | pipeline: 3 | build: 4 | image: golang:1.11 5 | commands: 6 | - echo foo 7 | - echo bar 8 | -------------------------------------------------------------------------------- /yaml/converter/metadata.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package converter 16 | 17 | // Metadata provides additional metadata used to 18 | // convert the configuration file format. 19 | type Metadata struct { 20 | // Filename of the configuration file, helps 21 | // determine the yaml configuration format. 22 | Filename string 23 | 24 | // URL of the repository used to create the repository 25 | // workspace directory using the fully qualified name. 26 | // e.g. /drone/src/github.com/octocat/hello-world 27 | URL string 28 | 29 | // Ref of the commit used to choose the correct 30 | // pipeline if the configuration format defines 31 | // multiple pipelines (like Bitbucket) 32 | Ref string 33 | } 34 | -------------------------------------------------------------------------------- /yaml/cron.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package yaml 16 | 17 | import "errors" 18 | 19 | type ( 20 | // Cron is a resource that defines a cron job, used 21 | // to execute pipelines at scheduled intervals. 22 | Cron struct { 23 | Version string `json:"version,omitempty"` 24 | Kind string `json:"kind,omitempty"` 25 | Type string `json:"type,omitempty"` 26 | Name string `json:"name,omitempty"` 27 | 28 | Spec CronSpec `json:"spec,omitempty"` 29 | } 30 | 31 | // CronSpec defines the cron job. 32 | CronSpec struct { 33 | Schedule string `json:"schedule,omitempty"` 34 | Branch string `json:"branch,omitempty"` 35 | Deploy CronDeployment `json:"deployment,omitempty" yaml:"deployment"` 36 | } 37 | 38 | // CronDeployment defines a cron job deployment. 39 | CronDeployment struct { 40 | Target string `json:"target,omitempty"` 41 | } 42 | ) 43 | 44 | // GetVersion returns the resource version. 45 | func (c *Cron) GetVersion() string { return c.Version } 46 | 47 | // GetKind returns the resource kind. 48 | func (c *Cron) GetKind() string { return c.Kind } 49 | 50 | // Validate returns an error if the cron is invalid. 51 | func (c Cron) Validate() error { 52 | switch { 53 | case c.Spec.Branch == "": 54 | return errors.New("yaml: invalid cron branch") 55 | default: 56 | return nil 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /yaml/cron_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | import "testing" 8 | 9 | func TestCronUnmarshal(t *testing.T) { 10 | diff, err := diff("testdata/cron.yml") 11 | if err != nil { 12 | t.Error(err) 13 | } 14 | if diff != "" { 15 | t.Error("Failed to parse cron") 16 | t.Log(diff) 17 | } 18 | } 19 | 20 | func TestCronValidate(t *testing.T) { 21 | cron := new(Cron) 22 | cron.Spec.Branch = "master" 23 | if err := cron.Validate(); err != nil { 24 | t.Error(err) 25 | return 26 | } 27 | 28 | cron.Spec.Branch = "" 29 | if err := cron.Validate(); err == nil { 30 | t.Errorf("Expect invalid cron error") 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /yaml/env.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package yaml 16 | 17 | type ( 18 | // Variable represents an environment variable that 19 | // can be defined as a string literal or as a reference 20 | // to a secret. 21 | Variable struct { 22 | Value string `json:"value,omitempty"` 23 | Secret string `json:"from_secret,omitempty" yaml:"from_secret"` 24 | } 25 | 26 | // variable is a tempoary type used to unmarshal 27 | // variables with references to secrets. 28 | variable struct { 29 | Value string 30 | Secret string `yaml:"from_secret"` 31 | } 32 | ) 33 | 34 | // UnmarshalYAML implements yaml unmarshalling. 35 | func (v *Variable) UnmarshalYAML(unmarshal func(interface{}) error) error { 36 | d := new(variable) 37 | err := unmarshal(&d.Value) 38 | if err != nil { 39 | err = unmarshal(d) 40 | } 41 | v.Value = d.Value 42 | v.Secret = d.Secret 43 | return err 44 | } 45 | 46 | // MarshalYAML implements yaml marshalling. 47 | func (v *Variable) MarshalYAML() (interface{}, error) { 48 | if v.Secret != "" { 49 | m := map[string]interface{}{} 50 | m["from_secret"] = v.Secret 51 | return m, nil 52 | } 53 | if v.Value != "" { 54 | return v.Value, nil 55 | } 56 | return nil, nil 57 | } 58 | -------------------------------------------------------------------------------- /yaml/env_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/buildkite/yaml" 11 | ) 12 | 13 | func TestEnv(t *testing.T) { 14 | tests := []struct { 15 | yaml string 16 | value string 17 | from string 18 | }{ 19 | { 20 | yaml: "bar", 21 | value: "bar", 22 | }, 23 | { 24 | yaml: "from_secret: username", 25 | from: "username", 26 | }, 27 | } 28 | for _, test := range tests { 29 | in := []byte(test.yaml) 30 | out := new(Variable) 31 | err := yaml.Unmarshal(in, out) 32 | if err != nil { 33 | t.Error(err) 34 | return 35 | } 36 | if got, want := out.Value, test.value; got != want { 37 | t.Errorf("Want variable value %q, got %q", want, got) 38 | } 39 | if got, want := out.Secret, test.from; got != want { 40 | t.Errorf("Want variable from_secret %q, got %q", want, got) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /yaml/linter/config.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package linter 16 | 17 | import ( 18 | "errors" 19 | 20 | "github.com/drone/drone-yaml/yaml" 21 | ) 22 | 23 | // ErrDuplicatePipelineName is returned when two Pipeline 24 | // resources have the same name. 25 | var ErrDuplicatePipelineName = errors.New("linter: duplicate pipeline names") 26 | 27 | // ErrMissingPipelineDependency is returned when a Pipeline 28 | // defines dependencies that are invlid or unknown. 29 | var ErrMissingPipelineDependency = errors.New("linter: invalid or unknown pipeline dependency") 30 | 31 | // ErrCyclicalPipelineDependency is returned when a Pipeline 32 | // defines a cyclical dependency, which would result in an 33 | // infinite execution loop. 34 | var ErrCyclicalPipelineDependency = errors.New("linter: cyclical pipeline dependency detected") 35 | 36 | // ErrPipelineSelfDependency is returned when a Pipeline 37 | // defines a dependency on itself. 38 | var ErrPipelineSelfDependency = errors.New("linter: pipeline cannot have a dependency on itself") 39 | 40 | // Manifest performs lint operations for a manifest. 41 | func Manifest(manifest *yaml.Manifest, trusted bool) error { 42 | return checkPipelines(manifest, trusted) 43 | } 44 | 45 | func checkPipelines(manifest *yaml.Manifest, trusted bool) error { 46 | names := map[string]struct{}{} 47 | for _, resource := range manifest.Resources { 48 | switch v := resource.(type) { 49 | case *yaml.Pipeline: 50 | _, ok := names[v.Name] 51 | if ok { 52 | return ErrDuplicatePipelineName 53 | } 54 | names[v.Name] = struct{}{} 55 | err := checkPipelineDeps(v, names) 56 | if err != nil { 57 | return err 58 | } 59 | if (v.Kind == "pipeline" || v.Kind == "") && (v.Type == "" || v.Type == "docker") { 60 | err = checkPlatform(v.Platform) 61 | if err != nil { 62 | return err 63 | } 64 | } 65 | } 66 | } 67 | return nil 68 | } 69 | 70 | func checkPipelineDeps(pipeline *yaml.Pipeline, deps map[string]struct{}) error { 71 | for _, dep := range pipeline.DependsOn { 72 | _, ok := deps[dep] 73 | if !ok { 74 | return ErrMissingPipelineDependency 75 | } 76 | if pipeline.Name == dep { 77 | return ErrPipelineSelfDependency 78 | } 79 | } 80 | return nil 81 | } 82 | -------------------------------------------------------------------------------- /yaml/linter/config_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package linter 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/drone/drone-yaml/yaml" 11 | ) 12 | 13 | func TestManifest(t *testing.T) { 14 | tests := []struct { 15 | path string 16 | trusted bool 17 | invalid bool 18 | message string 19 | }{ 20 | { 21 | path: "testdata/simple.yml", 22 | trusted: false, 23 | invalid: false, 24 | }, 25 | { 26 | path: "testdata/invalid_os.yml", 27 | trusted: false, 28 | invalid: true, 29 | message: "linter: unsupported os: openbsd", 30 | }, 31 | { 32 | path: "testdata/invalid_arch.yml", 33 | trusted: false, 34 | invalid: true, 35 | message: "linter: unsupported architecture: s390x", 36 | }, 37 | { 38 | path: "testdata/duplicate_name.yml", 39 | trusted: false, 40 | invalid: true, 41 | message: "linter: duplicate pipeline names", 42 | }, 43 | { 44 | path: "testdata/missing_dep.yml", 45 | trusted: false, 46 | invalid: true, 47 | message: "linter: invalid or unknown pipeline dependency", 48 | }, 49 | } 50 | for _, test := range tests { 51 | t.Run(test.path, func(t *testing.T) { 52 | manifest, err := yaml.ParseFile(test.path) 53 | if err != nil { 54 | t.Logf("yaml: %s", test.path) 55 | t.Error(err) 56 | return 57 | } 58 | 59 | err = Manifest(manifest, test.trusted) 60 | if err == nil && test.invalid == true { 61 | t.Logf("yaml: %s", test.path) 62 | t.Errorf("Expect lint error") 63 | return 64 | } 65 | 66 | if err != nil && test.invalid == false { 67 | t.Logf("yaml: %s", test.path) 68 | t.Errorf("Expect lint error is nil, got %s", err) 69 | return 70 | } 71 | 72 | if err == nil { 73 | return 74 | } 75 | 76 | if got, want := err.Error(), test.message; got != want { 77 | t.Logf("yaml: %s", test.path) 78 | t.Errorf("Want message %q, got %q", want, got) 79 | return 80 | } 81 | }) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /yaml/linter/testdata/duplicate_name.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | steps: 6 | - name: build 7 | image: golang 8 | commands: 9 | - go build 10 | - go test 11 | 12 | --- 13 | kind: pipeline 14 | name: default 15 | 16 | steps: 17 | - name: build 18 | image: golang 19 | commands: 20 | - go build 21 | - go test 22 | 23 | ... -------------------------------------------------------------------------------- /yaml/linter/testdata/duplicate_step.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | steps: 6 | - name: build 7 | image: golang 8 | commands: 9 | - go build 10 | - go test 11 | 12 | - name: build 13 | image: golang 14 | commands: 15 | - go build 16 | - go test -------------------------------------------------------------------------------- /yaml/linter/testdata/duplicate_step_service.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | steps: 6 | - name: test 7 | image: golang 8 | commands: 9 | - go build 10 | - go test 11 | 12 | services: 13 | - name: test 14 | image: redis 15 | -------------------------------------------------------------------------------- /yaml/linter/testdata/invalid_arch.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: linux 4 | 5 | platform: 6 | os: linux 7 | arch: s390x 8 | 9 | steps: 10 | - name: build 11 | image: golang 12 | commands: 13 | - go build 14 | - go test 15 | -------------------------------------------------------------------------------- /yaml/linter/testdata/invalid_os.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: linux 4 | 5 | platform: 6 | os: openbsd 7 | 8 | steps: 9 | - name: build 10 | image: golang 11 | commands: 12 | - go build 13 | - go test 14 | -------------------------------------------------------------------------------- /yaml/linter/testdata/missing_build_image.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: linux 4 | 5 | steps: 6 | - name: test 7 | build: {} 8 | 9 | -------------------------------------------------------------------------------- /yaml/linter/testdata/missing_dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: amd64 4 | 5 | steps: 6 | - name: test 7 | image: golang 8 | commands: 9 | - go build 10 | - go test 11 | 12 | services: 13 | - name: database 14 | image: redis 15 | ports: 16 | - 6379 17 | 18 | --- 19 | kind: pipeline 20 | name: arm 21 | 22 | platform: 23 | arch: arm 24 | 25 | steps: 26 | - name: test 27 | image: golang 28 | commands: 29 | - go build 30 | - go test 31 | 32 | depends_on: 33 | - foo 34 | ... -------------------------------------------------------------------------------- /yaml/linter/testdata/missing_image.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: linux 4 | 5 | steps: 6 | - name: test 7 | commands: 8 | - go build 9 | - go test 10 | -------------------------------------------------------------------------------- /yaml/linter/testdata/missing_name.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: linux 4 | 5 | steps: 6 | - image: golang 7 | commands: 8 | - go build 9 | - go test 10 | -------------------------------------------------------------------------------- /yaml/linter/testdata/pipeline_device.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: linux 4 | 5 | steps: 6 | - name: test 7 | image: golang 8 | commands: 9 | - go build 10 | - go test 11 | devices: 12 | - name: data 13 | path: /dev/xvda 14 | 15 | services: 16 | - name: database 17 | image: redis 18 | ports: 19 | - 6379 20 | -------------------------------------------------------------------------------- /yaml/linter/testdata/pipeline_dns.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: linux 4 | 5 | steps: 6 | - name: test 7 | image: golang 8 | commands: 9 | - go build 10 | - go test 11 | dns: 12 | - 8.8.8.8 13 | -------------------------------------------------------------------------------- /yaml/linter/testdata/pipeline_dns_search.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: linux 4 | 5 | steps: 6 | - name: test 7 | image: golang 8 | commands: 9 | - go build 10 | - go test 11 | dns_search: 12 | - dc1.example.com 13 | - dc2.example.com 14 | -------------------------------------------------------------------------------- /yaml/linter/testdata/pipeline_extra_hosts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: linux 4 | 5 | steps: 6 | - name: test 7 | image: golang 8 | commands: 9 | - go build 10 | - go test 11 | extra_hosts: 12 | - "somehost:162.242.195.82" 13 | - "otherhost:50.31.209.229" 14 | -------------------------------------------------------------------------------- /yaml/linter/testdata/pipeline_network_mode.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: linux 4 | 5 | steps: 6 | - name: test 7 | image: golang 8 | commands: 9 | - go build 10 | - go test 11 | network_mode: host 12 | -------------------------------------------------------------------------------- /yaml/linter/testdata/pipeline_port_host.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: linux 4 | 5 | steps: 6 | - name: database 7 | image: redis 8 | detach: true 9 | ports: 10 | - port: 6379 11 | host: 6379 12 | 13 | - name: test 14 | image: golang 15 | commands: 16 | - go build 17 | - go test 18 | -------------------------------------------------------------------------------- /yaml/linter/testdata/pipeline_privileged.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: linux 4 | 5 | steps: 6 | - name: test 7 | image: golang 8 | commands: 9 | - go build 10 | - go test 11 | privileged: true 12 | 13 | services: 14 | - name: database 15 | image: redis 16 | ports: 17 | - 6379 18 | -------------------------------------------------------------------------------- /yaml/linter/testdata/pipeline_volume_invalid_name.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: linux 4 | 5 | steps: 6 | - name: test 7 | image: docker 8 | volumes: 9 | - name: _docker_socket 10 | path: /var/run/docker.sock 11 | commands: 12 | - docker system prune 13 | 14 | -------------------------------------------------------------------------------- /yaml/linter/testdata/service_device.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: linux 4 | 5 | steps: 6 | - name: test 7 | image: golang 8 | commands: 9 | - go build 10 | - go test 11 | 12 | services: 13 | - name: database 14 | image: redis 15 | ports: 16 | - 6379 17 | devices: 18 | - name: data 19 | path: /dev/xvda 20 | -------------------------------------------------------------------------------- /yaml/linter/testdata/service_port_host.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: linux 4 | 5 | steps: 6 | - name: test 7 | image: golang 8 | commands: 9 | - go build 10 | - go test 11 | 12 | services: 13 | - name: database 14 | image: redis 15 | ports: 16 | - port: 6379 17 | host: 6379 18 | -------------------------------------------------------------------------------- /yaml/linter/testdata/simple.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: amd64 4 | 5 | steps: 6 | - name: build 7 | image: golang 8 | commands: 9 | - go build 10 | 11 | - name: test 12 | image: golang 13 | commands: 14 | - go test 15 | 16 | services: 17 | - name: database 18 | image: redis 19 | ports: 20 | - 6379 21 | 22 | --- 23 | kind: pipeline 24 | name: arm 25 | 26 | platform: 27 | arch: arm 28 | 29 | steps: 30 | - name: test 31 | image: golang 32 | commands: 33 | - go build 34 | - go test 35 | 36 | depends_on: 37 | - amd64 38 | ... -------------------------------------------------------------------------------- /yaml/linter/testdata/volume_empty_dir.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: linux 4 | 5 | steps: 6 | - name: test 7 | image: golang 8 | commands: 9 | - go build 10 | - go test 11 | 12 | services: 13 | - name: database 14 | image: redis 15 | ports: 16 | - 6379 17 | 18 | volumes: 19 | - name: vol 20 | temp: {} 21 | -------------------------------------------------------------------------------- /yaml/linter/testdata/volume_empty_dir_memory.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: linux 4 | 5 | steps: 6 | - name: test 7 | image: golang 8 | commands: 9 | - go build 10 | - go test 11 | 12 | services: 13 | - name: database 14 | image: redis 15 | ports: 16 | - 6379 17 | 18 | volumes: 19 | - name: vol 20 | temp: 21 | medium: memory 22 | -------------------------------------------------------------------------------- /yaml/linter/testdata/volume_host_path.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: linux 4 | 5 | steps: 6 | - name: test 7 | image: golang 8 | commands: 9 | - go build 10 | - go test 11 | 12 | services: 13 | - name: database 14 | image: redis 15 | ports: 16 | - 6379 17 | 18 | volumes: 19 | - name: vol 20 | host: 21 | path: /any/path/it/will/be/replaced 22 | -------------------------------------------------------------------------------- /yaml/linter/testdata/volume_invalid_name.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: linux 4 | 5 | steps: 6 | - name: test 7 | image: golang 8 | commands: 9 | - go build 10 | - go test 11 | 12 | services: 13 | - name: database 14 | image: redis 15 | ports: 16 | - 6379 17 | 18 | volumes: 19 | - name: _workspace 20 | temp: {} 21 | -------------------------------------------------------------------------------- /yaml/manifest_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | import ( 8 | "encoding/json" 9 | "io/ioutil" 10 | "testing" 11 | 12 | "github.com/google/go-cmp/cmp" 13 | ) 14 | 15 | func TestManifestUnmarshal(t *testing.T) { 16 | diff, err := diff("testdata/manifest.yml") 17 | if err != nil { 18 | t.Error(err) 19 | } 20 | if diff != "" { 21 | t.Error("Failed to parse manifest with multiple entries") 22 | t.Log(diff) 23 | } 24 | } 25 | 26 | func diff(file string) (string, error) { 27 | a, err := ParseFile(file) 28 | if err != nil { 29 | return "", err 30 | } 31 | d, err := ioutil.ReadFile(file + ".golden") 32 | if err != nil { 33 | return "", err 34 | } 35 | b := new(Manifest) 36 | err = json.Unmarshal(d, b) 37 | if err != nil { 38 | return "", err 39 | } 40 | return cmp.Diff(a, b), nil 41 | } 42 | -------------------------------------------------------------------------------- /yaml/param.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package yaml 16 | 17 | type ( 18 | // Parameter represents an configuration parameter that 19 | // can be defined as a literal or as a reference 20 | // to a secret. 21 | Parameter struct { 22 | Value interface{} `json:"value,omitempty"` 23 | Secret string `json:"from_secret,omitempty" yaml:"from_secret"` 24 | } 25 | 26 | // parameter is a tempoary type used to unmarshal 27 | // parameters with references to secrets. 28 | parameter struct { 29 | Secret string `yaml:"from_secret"` 30 | } 31 | ) 32 | 33 | // UnmarshalYAML implements yaml unmarshalling. 34 | func (p *Parameter) UnmarshalYAML(unmarshal func(interface{}) error) error { 35 | d := new(parameter) 36 | err := unmarshal(d) 37 | if err == nil && d.Secret != "" { 38 | p.Secret = d.Secret 39 | return nil 40 | } 41 | var i interface{} 42 | err = unmarshal(&i) 43 | p.Value = i 44 | return err 45 | } 46 | 47 | // MarshalYAML implements yaml marshalling. 48 | func (p *Parameter) MarshalYAML() (interface{}, error) { 49 | if p.Secret != "" { 50 | m := map[string]interface{}{} 51 | m["from_secret"] = p.Secret 52 | return m, nil 53 | } 54 | if p.Value != "" { 55 | return p.Value, nil 56 | } 57 | return nil, nil 58 | } 59 | -------------------------------------------------------------------------------- /yaml/param_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/buildkite/yaml" 11 | ) 12 | 13 | func TestParam(t *testing.T) { 14 | tests := []struct { 15 | yaml string 16 | value interface{} 17 | from string 18 | }{ 19 | { 20 | yaml: "bar", 21 | value: "bar", 22 | }, 23 | { 24 | yaml: "from_secret: username", 25 | from: "username", 26 | }, 27 | } 28 | for _, test := range tests { 29 | in := []byte(test.yaml) 30 | out := new(Parameter) 31 | err := yaml.Unmarshal(in, out) 32 | if err != nil { 33 | t.Error(err) 34 | return 35 | } 36 | if got, want := out.Value, test.value; got != want { 37 | t.Errorf("Want value %q, got %q", want, got) 38 | } 39 | if got, want := out.Secret, test.from; got != want { 40 | t.Errorf("Want from_secret %q, got %q", want, got) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /yaml/parse_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | import ( 8 | "fmt" 9 | "testing" 10 | 11 | "github.com/google/go-cmp/cmp" 12 | ) 13 | 14 | func TestParseRaw(t *testing.T) { 15 | tests := []struct { 16 | data string 17 | want []*RawResource 18 | }{ 19 | // 20 | // empty document returns nil resources. 21 | // 22 | { 23 | data: "", 24 | want: nil, 25 | }, 26 | // 27 | // single document files. 28 | // 29 | { 30 | data: "kind: pipeline\nfoo: bar", 31 | want: []*RawResource{ 32 | {Kind: "pipeline", Data: []byte("kind: pipeline\nfoo: bar\n")}, 33 | }, 34 | }, 35 | { 36 | data: "kind: pipeline\nfoo: bar\n", 37 | want: []*RawResource{ 38 | {Kind: "pipeline", Data: []byte("kind: pipeline\nfoo: bar\n")}, 39 | }, 40 | }, 41 | { 42 | data: "---\nkind: pipeline\nfoo: bar\n...", 43 | want: []*RawResource{ 44 | {Kind: "pipeline", Data: []byte("kind: pipeline\nfoo: bar\n")}, 45 | }, 46 | }, 47 | { 48 | data: "---\nkind: pipeline\nfoo: bar\n...\n", 49 | want: []*RawResource{ 50 | {Kind: "pipeline", Data: []byte("kind: pipeline\nfoo: bar\n")}, 51 | }, 52 | }, 53 | // 54 | // multi-document files. 55 | // 56 | { 57 | data: "kind: a\nb: c\n---\nkind: d\ne: f", 58 | want: []*RawResource{ 59 | {Kind: "a", Data: []byte("kind: a\nb: c\n")}, 60 | {Kind: "d", Data: []byte("kind: d\ne: f\n")}, 61 | }, 62 | }, 63 | { 64 | data: "---\nkind: a\nb: c\n---\nkind: d\ne: f\n", 65 | want: []*RawResource{ 66 | {Kind: "a", Data: []byte("kind: a\nb: c\n")}, 67 | {Kind: "d", Data: []byte("kind: d\ne: f\n")}, 68 | }, 69 | }, 70 | { 71 | data: "---\nkind: a\nb: c\n---\nkind: d\ne: f\n...", 72 | want: []*RawResource{ 73 | {Kind: "a", Data: []byte("kind: a\nb: c\n")}, 74 | {Kind: "d", Data: []byte("kind: d\ne: f\n")}, 75 | }, 76 | }, 77 | { 78 | data: "---\nkind: a\nb: c\n---\nkind: d\ne: f\n...\n", 79 | want: []*RawResource{ 80 | {Kind: "a", Data: []byte("kind: a\nb: c\n")}, 81 | {Kind: "d", Data: []byte("kind: d\ne: f\n")}, 82 | }, 83 | }, 84 | } 85 | 86 | for i, test := range tests { 87 | name := fmt.Sprint(i) 88 | t.Run(name, func(t *testing.T) { 89 | got, err := ParseRawString(test.data) 90 | if err != nil { 91 | t.Error(err) 92 | } 93 | if diff := cmp.Diff(got, test.want); diff != "" { 94 | t.Fail() 95 | t.Log(diff) 96 | } 97 | }) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /yaml/pipeline_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | import "testing" 8 | 9 | func TestPipelineUnmarshal(t *testing.T) { 10 | diff, err := diff("testdata/pipeline.yml") 11 | if err != nil { 12 | t.Error(err) 13 | } 14 | if diff != "" { 15 | t.Error("Failed to parse pipeline") 16 | t.Log(diff) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /yaml/port.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package yaml 16 | 17 | type ( 18 | 19 | // Port represents a network port in a single container. 20 | Port struct { 21 | Port int `json:"port,omitempty"` 22 | Host int `json:"host,omitempty"` 23 | Protocol string `json:"protocol,omitempty"` 24 | } 25 | 26 | port struct { 27 | Port int 28 | Host int 29 | Protocol string 30 | } 31 | ) 32 | 33 | // UnmarshalYAML implements yaml unmarshalling. 34 | func (p *Port) UnmarshalYAML(unmarshal func(interface{}) error) error { 35 | out := new(port) 36 | err := unmarshal(&out.Port) 37 | if err != nil { 38 | err = unmarshal(&out) 39 | } 40 | p.Port = out.Port 41 | p.Host = out.Host 42 | p.Protocol = out.Protocol 43 | return err 44 | } 45 | -------------------------------------------------------------------------------- /yaml/port_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/buildkite/yaml" 11 | ) 12 | 13 | func TestPort(t *testing.T) { 14 | tests := []struct { 15 | yaml string 16 | port int 17 | host int 18 | protocol string 19 | }{ 20 | { 21 | yaml: "80", 22 | port: 80, 23 | }, 24 | { 25 | yaml: "{ port: 80, host: 8080, protocol: TCP }", 26 | port: 80, 27 | host: 8080, 28 | protocol: "TCP", 29 | }, 30 | } 31 | for _, test := range tests { 32 | in := []byte(test.yaml) 33 | out := new(Port) 34 | err := yaml.Unmarshal(in, out) 35 | if err != nil { 36 | t.Error(err) 37 | return 38 | } 39 | if got, want := out.Port, test.port; got != want { 40 | t.Errorf("Want Port %d, got %d", want, got) 41 | } 42 | if got, want := out.Host, test.host; got != want { 43 | t.Errorf("Want Host %d, got %d", want, got) 44 | } 45 | if got, want := out.Protocol, test.protocol; got != want { 46 | t.Errorf("Want Host %s, got %s", want, got) 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /yaml/pretty/container_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package pretty 6 | -------------------------------------------------------------------------------- /yaml/pretty/cron.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package pretty 16 | 17 | import "github.com/drone/drone-yaml/yaml" 18 | 19 | // helper function pretty prints the cron resource. 20 | func printCron(w writer, v *yaml.Cron) { 21 | w.WriteString("---") 22 | w.WriteTagValue("version", v.Version) 23 | w.WriteTagValue("kind", v.Kind) 24 | w.WriteTagValue("name", v.Name) 25 | printSpec(w, v) 26 | w.WriteByte('\n') 27 | w.WriteByte('\n') 28 | } 29 | 30 | // helper function pretty prints the spec block. 31 | func printSpec(w writer, v *yaml.Cron) { 32 | w.WriteTag("spec") 33 | 34 | w.IndentIncrease() 35 | w.WriteTagValue("schedule", v.Spec.Schedule) 36 | w.WriteTagValue("branch", v.Spec.Branch) 37 | if hasDeployment(v) { 38 | printDeploy(w, v) 39 | } 40 | w.IndentDecrease() 41 | } 42 | 43 | // helper function pretty prints the deploy block. 44 | func printDeploy(w writer, v *yaml.Cron) { 45 | w.WriteTag("deployment") 46 | w.IndentIncrease() 47 | w.WriteTagValue("target", v.Spec.Deploy.Target) 48 | w.IndentDecrease() 49 | } 50 | 51 | // helper function returns true if the deployment 52 | // object is empty. 53 | func hasDeployment(v *yaml.Cron) bool { 54 | return v.Spec.Deploy.Target != "" 55 | } 56 | -------------------------------------------------------------------------------- /yaml/pretty/cron_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package pretty 6 | 7 | import "testing" 8 | 9 | func TestCron(t *testing.T) { 10 | ok, err := diff("testdata/cron.yml") 11 | if err != nil { 12 | t.Error(err) 13 | } else if !ok { 14 | t.Errorf("Unepxected formatting") 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /yaml/pretty/pretty.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package pretty 16 | 17 | import ( 18 | "io" 19 | 20 | "github.com/drone/drone-yaml/yaml" 21 | ) 22 | 23 | // Print pretty prints the manifest. 24 | func Print(w io.Writer, v *yaml.Manifest) { 25 | state := new(baseWriter) 26 | for _, r := range v.Resources { 27 | switch t := r.(type) { 28 | case *yaml.Cron: 29 | printCron(state, t) 30 | case *yaml.Secret: 31 | printSecret(state, t) 32 | case *yaml.Signature: 33 | printSignature(state, t) 34 | case *yaml.Pipeline: 35 | printPipeline(state, t) 36 | } 37 | } 38 | state.WriteString("...") 39 | state.WriteByte('\n') 40 | w.Write(state.Bytes()) 41 | } 42 | -------------------------------------------------------------------------------- /yaml/pretty/pretty_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package pretty 6 | 7 | import ( 8 | "bytes" 9 | "io/ioutil" 10 | "testing" 11 | 12 | "github.com/drone/drone-yaml/yaml" 13 | ) 14 | 15 | // 16 | // 17 | // 18 | 19 | func TestPrintManifest(t *testing.T) { 20 | ok, err := diff("testdata/manifest.yml") 21 | if err != nil { 22 | t.Error(err) 23 | } else if !ok { 24 | t.Errorf("Unepxected formatting") 25 | } 26 | } 27 | 28 | func diff(file string) (bool, error) { 29 | manifest, err := yaml.ParseFile(file) 30 | if err != nil { 31 | return false, err 32 | } 33 | golden, err := ioutil.ReadFile(file + ".golden") 34 | if err != nil { 35 | return false, err 36 | } 37 | 38 | buf := new(bytes.Buffer) 39 | Print(buf, manifest) 40 | 41 | equal := bytes.Equal(buf.Bytes(), golden) 42 | if !equal { 43 | println(">>>") 44 | println(buf.String()) 45 | } 46 | return equal, nil 47 | } 48 | -------------------------------------------------------------------------------- /yaml/pretty/secret.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package pretty 16 | 17 | import ( 18 | "sort" 19 | "strings" 20 | 21 | "github.com/drone/drone-yaml/yaml" 22 | ) 23 | 24 | // TODO consider "!!binary |" for secret value 25 | 26 | // helper function to pretty prints the signature resource. 27 | func printSecret(w writer, v *yaml.Secret) { 28 | w.WriteString("---") 29 | w.WriteTagValue("version", v.Version) 30 | w.WriteTagValue("kind", v.Kind) 31 | w.WriteTagValue("type", v.Type) 32 | 33 | if len(v.Data) > 0 { 34 | w.WriteTagValue("name", v.Name) 35 | printData(w, v.Data) 36 | } 37 | if isSecretGetEmpty(v.Get) == false { 38 | w.WriteTagValue("name", v.Name) 39 | w.WriteByte('\n') 40 | printGet(w, v.Get) 41 | } 42 | w.WriteByte('\n') 43 | w.WriteByte('\n') 44 | } 45 | 46 | // helper function prints the get block. 47 | func printGet(w writer, v yaml.SecretGet) { 48 | w.WriteTag("get") 49 | w.IndentIncrease() 50 | w.WriteTagValue("path", v.Path) 51 | w.WriteTagValue("name", v.Name) 52 | w.WriteTagValue("key", v.Key) 53 | w.IndentDecrease() 54 | } 55 | 56 | // helper function prints the external data. 57 | func printExternalData(w writer, d map[string]yaml.ExternalData) { 58 | var keys []string 59 | for k := range d { 60 | keys = append(keys, k) 61 | } 62 | sort.Strings(keys) 63 | 64 | w.WriteTag("external_data") 65 | w.IndentIncrease() 66 | for _, k := range keys { 67 | v := d[k] 68 | w.WriteTag(k) 69 | w.IndentIncrease() 70 | w.WriteTagValue("path", v.Path) 71 | w.WriteTagValue("name", v.Name) 72 | w.IndentDecrease() 73 | } 74 | w.IndentDecrease() 75 | } 76 | 77 | func printData(w writer, d string) { 78 | w.WriteTag("data") 79 | w.WriteByte(' ') 80 | w.WriteByte('>') 81 | w.IndentIncrease() 82 | d = spaceReplacer.Replace(d) 83 | for _, s := range chunk(d, 60) { 84 | w.WriteByte('\n') 85 | w.Indent() 86 | w.WriteString(s) 87 | } 88 | w.IndentDecrease() 89 | } 90 | 91 | // replace spaces and newlines. 92 | var spaceReplacer = strings.NewReplacer(" ", "", "\n", "") 93 | 94 | // helper function returns true if the secret get 95 | // object is empty. 96 | func isSecretGetEmpty(v yaml.SecretGet) bool { 97 | return v.Key == "" && 98 | v.Name == "" && 99 | v.Path == "" 100 | } 101 | -------------------------------------------------------------------------------- /yaml/pretty/secret_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package pretty 6 | 7 | import "testing" 8 | 9 | func TestSecret(t *testing.T) { 10 | ok, err := diff("testdata/secret.yml") 11 | if err != nil { 12 | t.Error(err) 13 | } else if !ok { 14 | t.Errorf("Unepxected formatting") 15 | } 16 | } 17 | 18 | func TestGetSecret(t *testing.T) { 19 | ok, err := diff("testdata/secret_get.yml") 20 | if err != nil { 21 | t.Error(err) 22 | } else if !ok { 23 | t.Errorf("Unepxected formatting") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /yaml/pretty/signature.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package pretty 16 | 17 | import ( 18 | "github.com/drone/drone-yaml/yaml" 19 | ) 20 | 21 | // helper function pretty prints the signature resource. 22 | func printSignature(w writer, v *yaml.Signature) { 23 | w.WriteString("---") 24 | w.WriteTagValue("version", v.Version) 25 | w.WriteTagValue("kind", v.Kind) 26 | w.WriteTagValue("hmac", v.Hmac) 27 | w.WriteByte('\n') 28 | w.WriteByte('\n') 29 | } 30 | -------------------------------------------------------------------------------- /yaml/pretty/signature_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package pretty 6 | 7 | import "testing" 8 | 9 | func TestSignature(t *testing.T) { 10 | ok, err := diff("testdata/signature.yml") 11 | if err != nil { 12 | t.Error(err) 13 | } else if !ok { 14 | t.Errorf("Unepxected formatting") 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/cron.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | name: nightly 3 | kind: cron 4 | 5 | spec: 6 | branch: master 7 | 8 | schedule: 1 * * * * 9 | 10 | deployment: 11 | 12 | target: production 13 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/cron.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | version: 1 3 | kind: cron 4 | name: nightly 5 | spec: 6 | schedule: 1 * * * * 7 | branch: master 8 | deployment: 9 | target: production 10 | 11 | ... 12 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | clone: 6 | disable: true 7 | depth: 50 8 | 9 | platform: 10 | os: linux 11 | arch: arm64 12 | variant: 7 13 | version: 1803 14 | 15 | steps: 16 | - name: build 17 | image: golang 18 | commands: 19 | - go get 20 | - go build 21 | 22 | - name: test 23 | image: golang 24 | commands: 25 | - go test -v 26 | 27 | trigger: 28 | status: 29 | - success 30 | branch: 31 | - master 32 | - develop 33 | 34 | depends_on: 35 | - foo 36 | - bar 37 | 38 | --- 39 | kind: secret 40 | name: username 41 | data: N2NmYjA3ODQwNTY1ODFlY2E5MGJmOWI1NDk0NDFhMTEK 42 | 43 | --- 44 | kind: cron 45 | name: nightly 46 | 47 | spec: 48 | schedule: "1 * * * *" 49 | branch: master 50 | deployment: 51 | target: production 52 | 53 | --- 54 | kind: signature 55 | hmac: N2NmYjA3ODQwNTY1ODFlY2E5MGJmOWI1NDk0NDFhMTEK 56 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/manifest.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: arm64 8 | variant: 7 9 | version: 1803 10 | 11 | clone: 12 | depth: 50 13 | disable: true 14 | 15 | steps: 16 | - name: build 17 | image: golang 18 | commands: 19 | - go get 20 | - go build 21 | 22 | - name: test 23 | image: golang 24 | commands: 25 | - go test -v 26 | 27 | trigger: 28 | branch: 29 | - master 30 | - develop 31 | status: 32 | - success 33 | 34 | depends_on: 35 | - foo 36 | - bar 37 | 38 | --- 39 | kind: secret 40 | name: username 41 | data: > 42 | N2NmYjA3ODQwNTY1ODFlY2E5MGJmOWI1NDk0NDFhMTEK 43 | 44 | --- 45 | kind: cron 46 | name: nightly 47 | spec: 48 | schedule: 1 * * * * 49 | branch: master 50 | deployment: 51 | target: production 52 | 53 | --- 54 | kind: signature 55 | hmac: N2NmYjA3ODQwNTY1ODFlY2E5MGJmOWI1NDk0NDFhMTEK 56 | 57 | ... 58 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | clone: 6 | disable: true 7 | depth: 50 8 | 9 | platform: 10 | os: windows 11 | arch: arm64 12 | variant: 7 13 | version: 1803 14 | 15 | workspace: 16 | base: /go 17 | path: src/github.com/octocat/hello-world 18 | 19 | steps: 20 | - name: test_build 21 | build: 22 | image: drone/drone 23 | context: . 24 | args: 25 | foo: bar 26 | baz: boo 27 | labels: 28 | qux: qoo 29 | cache_from: 30 | - alpine 31 | - golang 32 | 33 | - name: test_push 34 | push: 35 | image: drone/drone 36 | 37 | - name: test_commands 38 | image: drone/drone 39 | pull: always 40 | shell: bash 41 | commands: 42 | - go get 43 | - go test 44 | failure: ignore 45 | 46 | - name: test_volumes 47 | image: docker 48 | commands: 49 | - docker build 50 | - docker test 51 | environment: 52 | DOCKER_HOST: /var/run/docker.sock 53 | privileged: true 54 | volumes: 55 | - name: docker 56 | path: /var/run/docker.sock 57 | 58 | - name: test_dns 59 | image: alpine 60 | commands: 61 | - ping google.com 62 | dns: 63 | - 8.8.8.8 64 | dns_search: 65 | - dc1.example.com 66 | - dc2.example.com 67 | extra_hosts: 68 | - "somehost:162.242.195.82" 69 | - "otherhost:50.31.209.229" 70 | 71 | # - name: test_privileged 72 | # image: alpine 73 | # commands: 74 | # - ls /proc 75 | # privileged: true 76 | 77 | # - name: test_devices 78 | # image: alpine 79 | # devices: 80 | # - name: xvda 81 | # path: /dev/xvda 82 | 83 | - name: test_env_secrets 84 | image: alpine 85 | environment: 86 | GOOS: linux 87 | GOARCH: amd64 88 | SSH_KEY: 89 | from_secret: username 90 | commands: 91 | - go get 92 | - go build 93 | 94 | - name: test_when 95 | image: alpine 96 | depends_on: 97 | - foo 98 | - bar 99 | when: 100 | branch: 101 | - master 102 | - develop 103 | status: 104 | - success 105 | ref: 106 | include: 107 | - refs/tags/* 108 | exclude: 109 | - refs/tags/feature-* 110 | 111 | services: 112 | - name: test_entrypoint 113 | image: reids:latest 114 | entrypoint: 115 | - /bin/redis-server 116 | ports: 117 | - 6379 118 | 119 | # - name: test_command 120 | # image: reids:latest 121 | # command: 122 | # - --port 123 | # - 6380 124 | # ports: 125 | # - 6380 126 | 127 | # - name: test_working_dir 128 | # image: redis:latest 129 | # working_dir: /data 130 | # ports: 131 | # - port: 6379 132 | # host: 6380 133 | # protocol: TCP 134 | 135 | # volumes: 136 | # - name: docker 137 | # host: 138 | # path: /var/run/docker.sock 139 | # - name: temp 140 | # temp: {} 141 | 142 | # trigger: 143 | # branch: 144 | # - master 145 | # - develop 146 | # status: 147 | # - success 148 | 149 | # depends_on: 150 | # - foo 151 | # - bar 152 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline.yml.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drone/drone-yaml/01fb17858c9bdf157c62f96fdd438e74c060b3b7/yaml/pretty/testdata/pipeline.yml.golden -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_build_long.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: test_build 11 | build: 12 | image: octocat/hello-world 13 | context: . 14 | args: 15 | foo: bar 16 | baz: boo 17 | labels: 18 | qux: qoo 19 | cache_from: 20 | - alpine 21 | - golang 22 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_build_long.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: test_build 11 | build: 12 | image: octocat/hello-world 13 | args: 14 | baz: boo 15 | foo: bar 16 | cache_from: 17 | - alpine 18 | - golang 19 | context: . 20 | labels: 21 | qux: qoo 22 | 23 | ... 24 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_build_short.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | steps: 6 | - name: test_build 7 | build: octocat/hello-world -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_build_short.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: test_build 11 | build: octocat/hello-world 12 | 13 | ... 14 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_clone_depth.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | clone: 6 | depth: 50 7 | 8 | steps: 9 | - name: test 10 | image: golang 11 | commands: 12 | - go build 13 | - go test -v 14 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_clone_depth.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | clone: 10 | depth: 50 11 | 12 | steps: 13 | - name: test 14 | image: golang 15 | commands: 16 | - go build 17 | - go test -v 18 | 19 | ... 20 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_clone_disable.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | clone: 6 | disable: true 7 | 8 | steps: 9 | - name: webhook 10 | image: alpine 11 | commands: 12 | - curl -x POST http://abc.com:80/~smith/home.html 13 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_clone_disable.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | clone: 10 | disable: true 11 | 12 | steps: 13 | - name: webhook 14 | image: alpine 15 | commands: 16 | - curl -x POST http://abc.com:80/~smith/home.html 17 | 18 | ... 19 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_clone_skip_verify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | clone: 6 | skip_verify: true 7 | 8 | steps: 9 | - name: webhook 10 | image: alpine 11 | commands: 12 | - curl -x POST http://abc.com:80/~smith/home.html 13 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_clone_skip_verify.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | clone: 10 | skip_verify: true 11 | 12 | steps: 13 | - name: webhook 14 | image: alpine 15 | commands: 16 | - curl -x POST http://abc.com:80/~smith/home.html 17 | 18 | ... 19 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_concurrency.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | concurrency: 6 | limit: 1 7 | 8 | steps: 9 | - name: webhook 10 | image: alpine 11 | commands: 12 | - curl -x POST http://abc.com:80/~smith/home.html 13 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_concurrency.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | concurrency: 10 | limit: 1 11 | 12 | steps: 13 | - name: webhook 14 | image: alpine 15 | commands: 16 | - curl -x POST http://abc.com:80/~smith/home.html 17 | 18 | ... 19 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_depends.yml: -------------------------------------------------------------------------------- 1 | kind: pipeline 2 | name: default 3 | 4 | steps: 5 | - name: build 6 | image: golang 7 | commands: 8 | - go build 9 | 10 | - name: test 11 | image: golang 12 | commands: 13 | - go build 14 | - go test -v 15 | depends_on: 16 | - build 17 | 18 | depends_on: 19 | - foo 20 | - bar 21 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_depends.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: build 11 | image: golang 12 | commands: 13 | - go build 14 | 15 | - name: test 16 | image: golang 17 | commands: 18 | - go build 19 | - go test -v 20 | depends_on: 21 | - build 22 | 23 | depends_on: 24 | - foo 25 | - bar 26 | 27 | ... 28 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_image_pull_secrets.yml: -------------------------------------------------------------------------------- 1 | kind: pipeline 2 | name: default 3 | 4 | steps: 5 | - name: build 6 | image: golang 7 | commands: 8 | - go build 9 | 10 | image_pull_secrets: 11 | - dockerhub 12 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_image_pull_secrets.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: build 11 | image: golang 12 | commands: 13 | - go build 14 | 15 | image_pull_secrets: 16 | - dockerhub 17 | 18 | ... 19 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_node.yml: -------------------------------------------------------------------------------- 1 | kind: pipeline 2 | name: default 3 | 4 | steps: 5 | - name: build 6 | image: golang 7 | commands: 8 | - go build 9 | 10 | - name: test 11 | image: golang 12 | commands: 13 | - go build 14 | - go test -v 15 | depends_on: 16 | - build 17 | 18 | node: 19 | disk: ssd 20 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_node.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: build 11 | image: golang 12 | commands: 13 | - go build 14 | 15 | - name: test 16 | image: golang 17 | commands: 18 | - go build 19 | - go test -v 20 | depends_on: 21 | - build 22 | 23 | node: 24 | disk: ssd 25 | 26 | ... 27 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_ports.yml: -------------------------------------------------------------------------------- 1 | kind: pipeline 2 | name: default 3 | 4 | steps: 5 | - name: redis 6 | pull: always 7 | detatch: true 8 | image: redis:latest 9 | ports: 10 | - 6379 11 | - port: 6379 12 | host: 6379 13 | protocol: TCP 14 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_ports.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: redis 11 | pull: always 12 | image: redis:latest 13 | ports: 14 | - 6379 15 | - port: 6379 16 | host: 6379 17 | protocol: TCP 18 | 19 | ... 20 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_push.yml: -------------------------------------------------------------------------------- 1 | kind: pipeline 2 | name: default 3 | 4 | steps: 5 | - name: test_build 6 | push: octocat/hello-world -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_push.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: test_build 11 | push: octocat/hello-world 12 | 13 | ... 14 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_resources.yml: -------------------------------------------------------------------------------- 1 | kind: pipeline 2 | name: default 3 | 4 | steps: 5 | - name: build 6 | image: golang 7 | commands: 8 | - go build 9 | resources: 10 | limits: 11 | cpu: 2 12 | memory: '100Mi' 13 | requests: 14 | cpu: 1 15 | memory: '50Mi' 16 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_resources.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: build 11 | image: golang 12 | commands: 13 | - go build 14 | resources: 15 | limits: 16 | cpu: 2 17 | memory: 100MiB 18 | requests: 19 | cpu: 1 20 | memory: 50MiB 21 | 22 | ... 23 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_settings.yml: -------------------------------------------------------------------------------- 1 | kind: pipeline 2 | name: default 3 | 4 | steps: 5 | - name: notify 6 | image: plugins/slack 7 | settings: 8 | token: 9 | from_secret: token 10 | root: general 11 | dry_run: false 12 | labels: 13 | - foo 14 | - bar 15 | - baz 16 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_settings.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: notify 11 | image: plugins/slack 12 | settings: 13 | dry_run: false 14 | labels: 15 | - foo 16 | - bar 17 | - baz 18 | root: general 19 | token: 20 | from_secret: token 21 | 22 | ... 23 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_trigger.yml: -------------------------------------------------------------------------------- 1 | kind: pipeline 2 | name: default 3 | 4 | steps: 5 | - name: test 6 | image: golang 7 | commands: 8 | - go build 9 | - go test -v 10 | 11 | trigger: 12 | branch: 13 | - master 14 | - develop 15 | event: 16 | include: 17 | - push 18 | - pull_request 19 | exclude: 20 | - tag 21 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_trigger.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: test 11 | image: golang 12 | commands: 13 | - go build 14 | - go test -v 15 | 16 | trigger: 17 | branch: 18 | - master 19 | - develop 20 | event: 21 | include: 22 | - push 23 | - pull_request 24 | exclude: 25 | - tag 26 | 27 | ... 28 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_volumes.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | steps: 6 | - name: compile 7 | image: golang 8 | commands: 9 | - go test 10 | - go build 11 | 12 | - name: build 13 | image: docker 14 | commands: 15 | - docker build . 16 | volumes: 17 | - name: sock 18 | path: /var/run/docker.sock 19 | 20 | volumes: 21 | - name: temp 22 | temp: 23 | medium: memory 24 | - name: empty 25 | temp: {} 26 | - name: sock 27 | host: 28 | path: /var/run/docker.sock 29 | 30 | depends_on: 31 | - foo 32 | - bar 33 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_volumes.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: compile 11 | image: golang 12 | commands: 13 | - go test 14 | - go build 15 | 16 | - name: build 17 | image: docker 18 | commands: 19 | - docker build . 20 | volumes: 21 | - name: sock 22 | path: /var/run/docker.sock 23 | 24 | volumes: 25 | - name: temp 26 | temp: 27 | medium: memory 28 | - name: empty 29 | temp: {} 30 | - name: sock 31 | host: 32 | path: /var/run/docker.sock 33 | 34 | depends_on: 35 | - foo 36 | - bar 37 | 38 | ... 39 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_workspace.yml: -------------------------------------------------------------------------------- 1 | kind: pipeline 2 | name: default 3 | 4 | workspace: 5 | base: /go 6 | path: src/github.com/octocat/hello-world 7 | 8 | steps: 9 | - name: test 10 | image: golang 11 | commands: 12 | - go build 13 | - go test -v 14 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/pipeline_workspace.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | workspace: 10 | base: /go 11 | path: src/github.com/octocat/hello-world 12 | 13 | steps: 14 | - name: test 15 | image: golang 16 | commands: 17 | - go build 18 | - go test -v 19 | 20 | ... 21 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/registry.yml: -------------------------------------------------------------------------------- 1 | kind: registry 2 | type: encrypted 3 | data: 4 | index.docker.io: N2NmYjA3ODQwNTY1ODFlY2E5MGJmOWI1NDk0NDFhMTEK 5 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/registry.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: registry 3 | type: encrypted 4 | data: 5 | index.docker.io: > 6 | N2NmYjA3ODQwNTY1ODFlY2E5MGJmOWI1NDk0NDFhMTEK 7 | 8 | ... 9 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/secret.yml: -------------------------------------------------------------------------------- 1 | kind: secret 2 | name: username 3 | 4 | data: N2NmYjA3ODQwNTY1ODFlY2E5MGJmOWI1NDk0NDFhMTEK 5 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/secret.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: secret 3 | name: username 4 | data: > 5 | N2NmYjA3ODQwNTY1ODFlY2E5MGJmOWI1NDk0NDFhMTEK 6 | 7 | ... 8 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/secret_get.yml: -------------------------------------------------------------------------------- 1 | kind: secret 2 | name: username 3 | get: 4 | path: secret/data/docker 5 | name: username 6 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/secret_get.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: secret 3 | name: username 4 | 5 | get: 6 | path: secret/data/docker 7 | name: username 8 | 9 | ... 10 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/services.yml: -------------------------------------------------------------------------------- 1 | kind: pipeline 2 | name: default 3 | 4 | steps: 5 | - name: test 6 | image: golang 7 | commands: 8 | - go build 9 | - go test -v 10 | 11 | services: 12 | - name: redis 13 | image: redis:localhost 14 | ports: 15 | - 6379 16 | entrypoint: 17 | - /bin/redis-server 18 | command: 19 | - --port 20 | - 6380 21 | - name: mysql 22 | image: mysql:latest 23 | ports: 24 | - 3306 25 | environment: 26 | MYSQL_USERNAME: root 27 | MYSQL_PASSWORD: 28 | from_secret: password 29 | 30 | depends_on: 31 | - foo 32 | - bar 33 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/services.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | steps: 10 | - name: test 11 | image: golang 12 | commands: 13 | - go build 14 | - go test -v 15 | 16 | services: 17 | - name: redis 18 | image: redis:localhost 19 | entrypoint: 20 | - /bin/redis-server 21 | command: 22 | - --port 23 | - 6380 24 | ports: 25 | - 6379 26 | 27 | - name: mysql 28 | image: mysql:latest 29 | environment: 30 | MYSQL_PASSWORD: 31 | from_secret: password 32 | MYSQL_USERNAME: root 33 | ports: 34 | - 3306 35 | 36 | depends_on: 37 | - foo 38 | - bar 39 | 40 | ... 41 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/signature.yml: -------------------------------------------------------------------------------- 1 | kind: signature 2 | hmac: N2NmYjA3ODQwNTY1ODFlY2E5MGJmOWI1NDk0NDFhMTEK 3 | -------------------------------------------------------------------------------- /yaml/pretty/testdata/signature.yml.golden: -------------------------------------------------------------------------------- 1 | --- 2 | kind: signature 3 | hmac: N2NmYjA3ODQwNTY1ODFlY2E5MGJmOWI1NDk0NDFhMTEK 4 | 5 | ... 6 | -------------------------------------------------------------------------------- /yaml/pretty/writer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright the Drone Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package pretty 16 | 17 | import ( 18 | "strings" 19 | "testing" 20 | 21 | "github.com/buildkite/yaml" 22 | ) 23 | 24 | // this unit tests pretty prints a complex yaml structure 25 | // to ensure we have common use cases covered. 26 | func TestWriteComplexValue(t *testing.T) { 27 | block := map[interface{}]interface{}{} 28 | err := yaml.Unmarshal([]byte(testComplexValue), &block) 29 | if err != nil { 30 | t.Error(err) 31 | return 32 | } 33 | 34 | b := new(baseWriter) 35 | writeValue(b, block) 36 | got, want := b.String(), strings.TrimSpace(testComplexValue) 37 | if got != want { 38 | t.Errorf("Unexpected block format") 39 | println(got) 40 | println("---") 41 | println(want) 42 | } 43 | } 44 | 45 | var testComplexValue = ` 46 | a: b 47 | c: 48 | - d 49 | - e 50 | f: 51 | g: h 52 | i: 53 | - j 54 | - k 55 | - l: m 56 | o: p 57 | q: 58 | - r 59 | - s: ~ 60 | - {} 61 | - [] 62 | - ~ 63 | t: {} 64 | u: [] 65 | v: 1 66 | w: true 67 | x: ~ 68 | z: "#y" 69 | zz: "\nz\n" 70 | "{z}": z` 71 | -------------------------------------------------------------------------------- /yaml/push.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package yaml 16 | 17 | type ( 18 | // Push configures a Docker push. 19 | Push struct { 20 | Image string `json:"image,omitempty"` 21 | } 22 | 23 | // push is a tempoary type used to unmarshal 24 | // the Push struct when long format is used. 25 | push struct { 26 | Image string `json:"image,omitempty"` 27 | } 28 | ) 29 | 30 | // UnmarshalYAML implements yaml unmarshalling. 31 | func (p *Push) UnmarshalYAML(unmarshal func(interface{}) error) error { 32 | d := new(push) 33 | err := unmarshal(&d.Image) 34 | if err != nil { 35 | err = unmarshal(d) 36 | } 37 | p.Image = d.Image 38 | return err 39 | } 40 | -------------------------------------------------------------------------------- /yaml/push_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/buildkite/yaml" 11 | ) 12 | 13 | func TestPush(t *testing.T) { 14 | tests := []struct { 15 | yaml string 16 | image string 17 | }{ 18 | { 19 | yaml: "foo", 20 | image: "foo", 21 | }, 22 | { 23 | yaml: "{ image: foo }", 24 | image: "foo", 25 | }, 26 | } 27 | for _, test := range tests { 28 | in := []byte(test.yaml) 29 | out := new(Push) 30 | err := yaml.Unmarshal(in, out) 31 | if err != nil { 32 | t.Error(err) 33 | return 34 | } 35 | if got, want := out.Image, test.image; got != want { 36 | t.Errorf("Want Image %q, got %q", want, got) 37 | } 38 | } 39 | } 40 | 41 | func TestPushError(t *testing.T) { 42 | in := []byte("[]") 43 | out := new(Push) 44 | err := yaml.Unmarshal(in, out) 45 | if err == nil { 46 | t.Errorf("Expect unmarshal error") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /yaml/registry.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package yaml 16 | 17 | import "errors" 18 | 19 | type ( 20 | // Registry is a resource that provides encrypted 21 | // registry credentials and pointers to external 22 | // registry credentials (e.g. from vault). 23 | Registry struct { 24 | Version string `json:"version,omitempt"` 25 | Kind string `json:"kind,omitempty"` 26 | Type string `json:"type,omitempty"` 27 | 28 | Data map[string]string `json:"data,omitempty"` 29 | } 30 | ) 31 | 32 | // GetVersion returns the resource version. 33 | func (r *Registry) GetVersion() string { return r.Version } 34 | 35 | // GetKind returns the resource kind. 36 | func (r *Registry) GetKind() string { return r.Kind } 37 | 38 | // Validate returns an error if the registry is invalid. 39 | func (r *Registry) Validate() error { 40 | if len(r.Data) == 0 { 41 | return errors.New("yaml: invalid registry resource") 42 | } 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /yaml/registry_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | import "testing" 8 | 9 | func TestRegistryUnmarshal(t *testing.T) { 10 | diff, err := diff("testdata/registry.yml") 11 | if err != nil { 12 | t.Error(err) 13 | } 14 | if diff != "" { 15 | t.Error("Failed to parse registry") 16 | t.Log(diff) 17 | } 18 | } 19 | 20 | func TestRegistryValidate(t *testing.T) { 21 | registry := new(Registry) 22 | 23 | registry.Data = map[string]string{"index.drone.io": ""} 24 | if err := registry.Validate(); err != nil { 25 | t.Error(err) 26 | return 27 | } 28 | 29 | registry.Data = map[string]string{} 30 | if err := registry.Validate(); err == nil { 31 | t.Errorf("Expect invalid registry error") 32 | } 33 | 34 | registry.Data = nil 35 | if err := registry.Validate(); err == nil { 36 | t.Errorf("Expect invalid registry error") 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /yaml/secret.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package yaml 16 | 17 | import "errors" 18 | 19 | // TODO(bradrydzewski) deprecate Secret 20 | // TODO(bradrydzewski) deprecate ExternalData 21 | 22 | type ( 23 | // Secret is a resource that provides encrypted data 24 | // and pointers to external data (i.e. from vault). 25 | Secret struct { 26 | Version string `json:"version,omitempty"` 27 | Kind string `json:"kind,omitempty"` 28 | Type string `json:"type,omitempty"` 29 | Name string `json:"name,omitempty"` 30 | 31 | Data string `json:"data,omitempty"` 32 | Get SecretGet `json:"get,omitempty"` 33 | } 34 | 35 | // SecretGet defines a request to get a secret from 36 | // an external sevice at the specified path, or with the 37 | // specified name. 38 | SecretGet struct { 39 | Path string `json:"path,omitempty"` 40 | Name string `json:"name,omitempty"` 41 | Key string `json:"key,omitempty"` 42 | } 43 | 44 | // ExternalData defines the path and name of external 45 | // data located in an external or remote storage system. 46 | ExternalData struct { 47 | Path string `json:"path,omitempty"` 48 | Name string `json:"name,omitempty"` 49 | } 50 | ) 51 | 52 | // GetVersion returns the resource version. 53 | func (s *Secret) GetVersion() string { return s.Version } 54 | 55 | // GetKind returns the resource kind. 56 | func (s *Secret) GetKind() string { return s.Kind } 57 | 58 | // Validate returns an error if the secret is invalid. 59 | func (s *Secret) Validate() error { 60 | if len(s.Data) == 0 && len(s.Get.Path) == 0 && len(s.Get.Name) == 0 { 61 | return errors.New("yaml: invalid secret resource") 62 | } 63 | return nil 64 | } 65 | -------------------------------------------------------------------------------- /yaml/secret_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | import "testing" 8 | 9 | func TestSecretUnmarshal(t *testing.T) { 10 | diff, err := diff("testdata/secret.yml") 11 | if err != nil { 12 | t.Error(err) 13 | } 14 | if diff != "" { 15 | t.Error("Failed to parse secret") 16 | t.Log(diff) 17 | } 18 | } 19 | 20 | func TestSecretValidate(t *testing.T) { 21 | secret := new(Secret) 22 | 23 | secret.Data = "some-data" 24 | if err := secret.Validate(); err != nil { 25 | t.Error(err) 26 | return 27 | } 28 | 29 | secret.Get.Path = "secret/data/docker" 30 | if err := secret.Validate(); err != nil { 31 | t.Error(err) 32 | return 33 | } 34 | 35 | secret.Data = "" 36 | secret.Get.Path = "" 37 | if err := secret.Validate(); err == nil { 38 | t.Errorf("Expect invalid secret error") 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /yaml/signature.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone IO, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package yaml 16 | 17 | import "errors" 18 | 19 | type ( 20 | // Signature is a resource that provides an hmac 21 | // signature of combined resources. This signature 22 | // can be used to validate authenticity and prevent 23 | // tampering. 24 | Signature struct { 25 | Version string `json:"version,omitempty"` 26 | Kind string `json:"kind"` 27 | Hmac string `json:"hmac"` 28 | } 29 | ) 30 | 31 | // GetVersion returns the resource version. 32 | func (s *Signature) GetVersion() string { return s.Version } 33 | 34 | // GetKind returns the resource kind. 35 | func (s *Signature) GetKind() string { return s.Kind } 36 | 37 | // Validate returns an error if the signature is invalid. 38 | func (s Signature) Validate() error { 39 | if s.Hmac == "" { 40 | return errors.New("yaml: invalid signature. missing hash") 41 | } 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /yaml/signature_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Drone.IO Inc. All rights reserved. 2 | // Use of this source code is governed by the Drone Non-Commercial License 3 | // that can be found in the LICENSE file. 4 | 5 | package yaml 6 | 7 | import "testing" 8 | 9 | func TestSignatureUnmarshal(t *testing.T) { 10 | diff, err := diff("testdata/signature.yml") 11 | if err != nil { 12 | t.Error(err) 13 | } 14 | if diff != "" { 15 | t.Error("Failed to parse signature") 16 | t.Log(diff) 17 | } 18 | } 19 | 20 | func TestSignatureValidate(t *testing.T) { 21 | sig := Signature{Hmac: "1234"} 22 | if err := sig.Validate(); err != nil { 23 | t.Error(err) 24 | return 25 | } 26 | 27 | sig.Hmac = "" 28 | if err := sig.Validate(); err == nil { 29 | t.Errorf("Expect invalid signature error") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /yaml/signer/testdata/invalid_signature.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | pipeline: 6 | - name: build 7 | image: golang 8 | commands: 9 | - go build 10 | - go test 11 | 12 | --- 13 | kind: secret 14 | data: 15 | password: YjgwNDc4ZDY4NmQzNzQzYjNkYmUwYmE3YjMwOTM2OWUK 16 | username: N2NmYjA3ODQwNTY1ODFlY2E5MGJmOWI1NDk0NDFhMTEK 17 | 18 | --- 19 | kind: signature 20 | hmac: 8a4f92a79a5eabb67ba5389c77b4abe58472870783ae1e7293e943aee314aedc 21 | 22 | ... 23 | -------------------------------------------------------------------------------- /yaml/signer/testdata/missing_signature.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | pipeline: 6 | - name: build 7 | image: golang 8 | commands: 9 | - go build 10 | - go test 11 | 12 | ... 13 | -------------------------------------------------------------------------------- /yaml/signer/testdata/signed.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | pipeline: 6 | - name: build 7 | image: golang 8 | commands: 9 | - go build 10 | - go test 11 | 12 | --- 13 | kind: secret 14 | data: 15 | password: YjgwNDc4ZDY4NmQzNzQzYjNkYmUwYmE3YjMwOTM2OWUK 16 | username: N2NmYjA3ODQwNTY1ODFlY2E5MGJmOWI1NDk0NDFhMTEK 17 | 18 | --- 19 | kind: signature 20 | hmac: 389cf92a7472870783a9a5ea77b4abe58a4bb67ba58e1e7293e943aee314aedc 21 | 22 | ... 23 | -------------------------------------------------------------------------------- /yaml/testdata/cron.yml: -------------------------------------------------------------------------------- 1 | kind: cron 2 | name: nightly 3 | 4 | spec: 5 | schedule: "1 * * * *" 6 | branch: master 7 | deployment: 8 | target: production 9 | -------------------------------------------------------------------------------- /yaml/testdata/cron.yml.golden: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "kind": "cron", 4 | "name": "nightly", 5 | "spec": { 6 | "schedule": "1 * * * *", 7 | "branch": "master", 8 | "deployment": { 9 | "target": "production" 10 | } 11 | } 12 | } 13 | ] -------------------------------------------------------------------------------- /yaml/testdata/manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | --- 4 | kind: registry 5 | --- 6 | kind: secret 7 | --- 8 | kind: cron 9 | --- 10 | kind: signature 11 | hmac: N2NmYjA3ODQwNTY1ODFlY2E5MGJmOWI1NDk0NDFhMTEK 12 | ... -------------------------------------------------------------------------------- /yaml/testdata/manifest.yml.golden: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "kind": "pipeline" 4 | }, 5 | { 6 | "kind": "registry" 7 | }, 8 | { 9 | "kind": "secret" 10 | }, 11 | { 12 | "kind": "cron" 13 | }, 14 | { 15 | "kind": "signature", 16 | "hmac": "N2NmYjA3ODQwNTY1ODFlY2E5MGJmOWI1NDk0NDFhMTEK" 17 | } 18 | ] -------------------------------------------------------------------------------- /yaml/testdata/pipeline.yml: -------------------------------------------------------------------------------- 1 | kind: pipeline 2 | name: default 3 | 4 | platform: 5 | os: windows 6 | arch: arm64 7 | variant: 7 8 | version: 1803 9 | 10 | workspace: 11 | base: /go 12 | path: src/github.com/drone/go-yaml 13 | 14 | clone: 15 | disable: true 16 | depth: 50 17 | 18 | steps: 19 | - name: test 20 | image: golang 21 | pull: always 22 | shell: bash 23 | commands: 24 | - go get 25 | - go test 26 | environment: 27 | GOOS: windows 28 | GOARCH: arm 29 | privileged: true 30 | when: 31 | branch: 32 | - master 33 | - develop 34 | 35 | - name: build 36 | build: 37 | image: octocat/hello-world 38 | context: . 39 | dockerfile: Dockerfile 40 | labels: 41 | foo: bar 42 | baz: qux 43 | cache_from: 44 | - octocat/knife-spoon 45 | 46 | - name: push 47 | push: octocat/hello-world 48 | 49 | services: 50 | - name: redis 51 | image: redis:latest 52 | detach: true 53 | entrypoint: [ /bin/redis-server ] 54 | command: [ --port, 6380 ] 55 | ports: 56 | - 6380 57 | working_dir: /data 58 | volumes: 59 | - name: data 60 | path: /data 61 | failure: ignore 62 | environment: 63 | REDIS_USERNAME: foo 64 | REDIS_PASSWORD: bar 65 | 66 | volumes: 67 | - name: data 68 | temp: {} 69 | - name: other 70 | host: 71 | path: /tmp/data 72 | 73 | trigger: 74 | branch: 75 | include: 76 | - master 77 | - develop 78 | 79 | depends_on: 80 | - foo 81 | - bar 82 | -------------------------------------------------------------------------------- /yaml/testdata/pipeline.yml.golden: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "kind": "pipeline", 4 | "name": "default", 5 | "clone": { 6 | "disable": true, 7 | "depth": 50 8 | }, 9 | "depends_on": [ 10 | "foo", 11 | "bar" 12 | ], 13 | "platform": { 14 | "os": "windows", 15 | "arch": "arm64", 16 | "variant": "7", 17 | "version": "1803" 18 | }, 19 | "services": [ 20 | { 21 | "command": [ 22 | "--port", 23 | "6380" 24 | ], 25 | "detach": true, 26 | "entrypoint": [ 27 | "/bin/redis-server" 28 | ], 29 | "environment": { 30 | "REDIS_PASSWORD": { 31 | "value": "bar" 32 | }, 33 | "REDIS_USERNAME": { 34 | "value": "foo" 35 | } 36 | }, 37 | "failure": "ignore", 38 | "image": "redis:latest", 39 | "name": "redis", 40 | "ports": [ 41 | { 42 | "port": 6380 43 | } 44 | ], 45 | "volumes": [ 46 | { 47 | "name": "data", 48 | "path": "/data" 49 | } 50 | ], 51 | "working_dir": "/data" 52 | } 53 | ], 54 | "steps": [ 55 | { 56 | "commands": [ 57 | "go get", 58 | "go test" 59 | ], 60 | "environment": { 61 | "GOARCH": { 62 | "value": "arm" 63 | }, 64 | "GOOS": { 65 | "value": "windows" 66 | } 67 | }, 68 | "image": "golang", 69 | "name": "test", 70 | "privileged": true, 71 | "pull": "always", 72 | "shell": "bash", 73 | "when": { 74 | "branch": { 75 | "include": [ 76 | "master", 77 | "develop" 78 | ] 79 | } 80 | } 81 | }, 82 | { 83 | "build": { 84 | "cache_from": [ 85 | "octocat/knife-spoon" 86 | ], 87 | "context": ".", 88 | "dockerfile": "Dockerfile", 89 | "image": "octocat/hello-world", 90 | "labels": { 91 | "baz": "qux", 92 | "foo": "bar" 93 | } 94 | }, 95 | "name": "build" 96 | }, 97 | { 98 | "name": "push", 99 | "push": { 100 | "image": "octocat/hello-world" 101 | } 102 | } 103 | ], 104 | "trigger": { 105 | "branch": { 106 | "include": [ 107 | "master", 108 | "develop" 109 | ] 110 | } 111 | }, 112 | "volumes": [ 113 | { 114 | "name": "data", 115 | "temp": {} 116 | }, 117 | { 118 | "name": "other", 119 | "host": { 120 | "path": "/tmp/data" 121 | } 122 | } 123 | ], 124 | "workspace": { 125 | "base": "/go", 126 | "path": "src/github.com/drone/go-yaml" 127 | } 128 | } 129 | ] -------------------------------------------------------------------------------- /yaml/testdata/registry.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: registry 3 | type: encrypted 4 | data: 5 | index.docker.io: N2NmYjA3ODQwNTY1ODFlY2E5MGJmOWI1NDk0NDFhMTEK 6 | -------------------------------------------------------------------------------- /yaml/testdata/registry.yml.golden: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "kind": "registry", 4 | "type": "encrypted", 5 | "data": { 6 | "index.docker.io": "N2NmYjA3ODQwNTY1ODFlY2E5MGJmOWI1NDk0NDFhMTEK" 7 | } 8 | } 9 | ] -------------------------------------------------------------------------------- /yaml/testdata/secret.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: secret 3 | name: username 4 | 5 | data: b2N0b2NhdA== 6 | -------------------------------------------------------------------------------- /yaml/testdata/secret.yml.golden: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "kind": "secret", 4 | "name": "username", 5 | "data": "b2N0b2NhdA==" 6 | } 7 | ] -------------------------------------------------------------------------------- /yaml/testdata/signature.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: signature 3 | hmac: N2NmYjA3ODQwNTY1ODFlY2E5MGJmOWI1NDk0NDFhMTEK 4 | -------------------------------------------------------------------------------- /yaml/testdata/signature.yml.golden: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "kind": "signature", 4 | "hmac": "N2NmYjA3ODQwNTY1ODFlY2E5MGJmOWI1NDk0NDFhMTEK" 5 | } 6 | ] -------------------------------------------------------------------------------- /yaml/unit.go: -------------------------------------------------------------------------------- 1 | // Copyright the Drone Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package yaml 16 | 17 | import ( 18 | "github.com/docker/go-units" 19 | ) 20 | 21 | // BytesSize stores a human-readable size in bytes, 22 | // kibibytes, mebibytes, gibibytes, or tebibytes 23 | // (eg. "44kiB", "17MiB"). 24 | type BytesSize int64 25 | 26 | // UnmarshalYAML implements yaml unmarshalling. 27 | func (b *BytesSize) UnmarshalYAML(unmarshal func(interface{}) error) error { 28 | var intType int64 29 | if err := unmarshal(&intType); err == nil { 30 | *b = BytesSize(intType) 31 | return nil 32 | } 33 | 34 | var stringType string 35 | if err := unmarshal(&stringType); err != nil { 36 | return err 37 | } 38 | 39 | intType, err := units.RAMInBytes(stringType) 40 | if err == nil { 41 | *b = BytesSize(intType) 42 | } 43 | return err 44 | } 45 | 46 | // String returns a human-readable size in bytes, 47 | // kibibytes, mebibytes, gibibytes, or tebibytes 48 | // (eg. "44kiB", "17MiB"). 49 | func (b BytesSize) String() string { 50 | return units.BytesSize(float64(b)) 51 | } 52 | -------------------------------------------------------------------------------- /yaml/unit_test.go: -------------------------------------------------------------------------------- 1 | // Copyright the Drone Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package yaml 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/buildkite/yaml" 21 | ) 22 | 23 | func TestBytesSize(t *testing.T) { 24 | tests := []struct { 25 | yaml string 26 | size int64 27 | text string 28 | }{ 29 | { 30 | yaml: "1KiB", 31 | size: 1024, 32 | text: "1KiB", 33 | }, 34 | { 35 | yaml: "100Mi", 36 | size: 104857600, 37 | text: "100MiB", 38 | }, 39 | { 40 | yaml: "1024", 41 | size: 1024, 42 | text: "1KiB", 43 | }, 44 | } 45 | for _, test := range tests { 46 | in := []byte(test.yaml) 47 | out := BytesSize(0) 48 | err := yaml.Unmarshal(in, &out) 49 | if err != nil { 50 | t.Error(err) 51 | return 52 | } 53 | if got, want := int64(out), test.size; got != want { 54 | t.Errorf("Want byte size %d, got %d", want, got) 55 | } 56 | if got, want := out.String(), test.text; got != want { 57 | t.Errorf("Want byte text %s, got %s", want, got) 58 | } 59 | } 60 | } 61 | --------------------------------------------------------------------------------