buildkite-builder 1.0.0.beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +0 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +56 -0
- data/LICENSE.txt +21 -0
- data/README.md +44 -0
- data/Rakefile +6 -0
- data/bin/buildkite-builder +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/buildkite-builder.gemspec +28 -0
- data/lib/buildkite-builder.rb +14 -0
- data/lib/buildkite/builder.rb +54 -0
- data/lib/buildkite/builder/commands.rb +49 -0
- data/lib/buildkite/builder/commands/abstract.rb +64 -0
- data/lib/buildkite/builder/commands/files.rb +25 -0
- data/lib/buildkite/builder/commands/preview.rb +29 -0
- data/lib/buildkite/builder/definition.rb +24 -0
- data/lib/buildkite/builder/file_resolver.rb +59 -0
- data/lib/buildkite/builder/github.rb +71 -0
- data/lib/buildkite/builder/loaders.rb +12 -0
- data/lib/buildkite/builder/loaders/abstract.rb +40 -0
- data/lib/buildkite/builder/loaders/manifests.rb +23 -0
- data/lib/buildkite/builder/loaders/processors.rb +37 -0
- data/lib/buildkite/builder/loaders/templates.rb +25 -0
- data/lib/buildkite/builder/logging_utils.rb +24 -0
- data/lib/buildkite/builder/manifest.rb +89 -0
- data/lib/buildkite/builder/manifest/rule.rb +51 -0
- data/lib/buildkite/builder/processors.rb +9 -0
- data/lib/buildkite/builder/processors/abstract.rb +76 -0
- data/lib/buildkite/builder/rainbow.rb +9 -0
- data/lib/buildkite/builder/runner.rb +114 -0
- data/lib/buildkite/env.rb +52 -0
- data/lib/buildkite/pipelines.rb +13 -0
- data/lib/buildkite/pipelines/api.rb +119 -0
- data/lib/buildkite/pipelines/attributes.rb +137 -0
- data/lib/buildkite/pipelines/command.rb +59 -0
- data/lib/buildkite/pipelines/helpers.rb +43 -0
- data/lib/buildkite/pipelines/helpers/block.rb +18 -0
- data/lib/buildkite/pipelines/helpers/command.rb +21 -0
- data/lib/buildkite/pipelines/helpers/depends_on.rb +13 -0
- data/lib/buildkite/pipelines/helpers/key.rb +13 -0
- data/lib/buildkite/pipelines/helpers/label.rb +18 -0
- data/lib/buildkite/pipelines/helpers/plugins.rb +24 -0
- data/lib/buildkite/pipelines/helpers/retry.rb +20 -0
- data/lib/buildkite/pipelines/helpers/skip.rb +15 -0
- data/lib/buildkite/pipelines/helpers/soft_fail.rb +15 -0
- data/lib/buildkite/pipelines/helpers/timeout_in_minutes.rb +17 -0
- data/lib/buildkite/pipelines/pipeline.rb +129 -0
- data/lib/buildkite/pipelines/plugin.rb +23 -0
- data/lib/buildkite/pipelines/steps.rb +15 -0
- data/lib/buildkite/pipelines/steps/abstract.rb +26 -0
- data/lib/buildkite/pipelines/steps/block.rb +20 -0
- data/lib/buildkite/pipelines/steps/command.rb +30 -0
- data/lib/buildkite/pipelines/steps/input.rb +20 -0
- data/lib/buildkite/pipelines/steps/skip.rb +28 -0
- data/lib/buildkite/pipelines/steps/trigger.rb +22 -0
- data/lib/buildkite/pipelines/steps/wait.rb +18 -0
- data/lib/vendor/rainbow/Changelog.md +101 -0
- data/lib/vendor/rainbow/Gemfile +30 -0
- data/lib/vendor/rainbow/LICENSE +20 -0
- data/lib/vendor/rainbow/README.markdown +225 -0
- data/lib/vendor/rainbow/Rakefile +11 -0
- data/lib/vendor/rainbow/lib/rainbow.rb +13 -0
- data/lib/vendor/rainbow/lib/rainbow/color.rb +150 -0
- data/lib/vendor/rainbow/lib/rainbow/ext/string.rb +64 -0
- data/lib/vendor/rainbow/lib/rainbow/global.rb +25 -0
- data/lib/vendor/rainbow/lib/rainbow/null_presenter.rb +100 -0
- data/lib/vendor/rainbow/lib/rainbow/presenter.rb +144 -0
- data/lib/vendor/rainbow/lib/rainbow/refinement.rb +14 -0
- data/lib/vendor/rainbow/lib/rainbow/string_utils.rb +22 -0
- data/lib/vendor/rainbow/lib/rainbow/version.rb +5 -0
- data/lib/vendor/rainbow/lib/rainbow/wrapper.rb +22 -0
- data/lib/vendor/rainbow/lib/rainbow/x11_color_names.rb +153 -0
- data/lib/vendor/rainbow/rainbow.gemspec +23 -0
- metadata +126 -0
@@ -0,0 +1,137 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
module Buildkite
|
6
|
+
module Pipelines
|
7
|
+
module Attributes
|
8
|
+
def self.included(base)
|
9
|
+
base.extend(ClassMethods)
|
10
|
+
end
|
11
|
+
|
12
|
+
def get(attr)
|
13
|
+
attributes[validate(attr)]
|
14
|
+
end
|
15
|
+
|
16
|
+
def set(attr, value)
|
17
|
+
attributes[validate(attr)] = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def has?(attr)
|
21
|
+
# Don't validate for has? calls.
|
22
|
+
attributes.key?(attr.to_s)
|
23
|
+
end
|
24
|
+
|
25
|
+
def permits?(attr)
|
26
|
+
self.class.permits?(attr)
|
27
|
+
end
|
28
|
+
|
29
|
+
def append(attr, value)
|
30
|
+
ensure_array_value(attr)
|
31
|
+
get(attr).push(*[value].flatten)
|
32
|
+
value
|
33
|
+
end
|
34
|
+
|
35
|
+
def prepend(attr, value)
|
36
|
+
ensure_array_value(attr)
|
37
|
+
get(attr).unshift(*[value].flatten)
|
38
|
+
value
|
39
|
+
end
|
40
|
+
|
41
|
+
def permitted_attributes
|
42
|
+
self.class.permitted_attributes
|
43
|
+
end
|
44
|
+
|
45
|
+
def unset(attr)
|
46
|
+
attributes.delete(validate(attr))
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_h
|
50
|
+
permitted_attributes.each_with_object({}) do |attr, hash|
|
51
|
+
hash[attr] = get(attr) if has?(attr)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
module ClassMethods
|
56
|
+
def inherited(subclass)
|
57
|
+
subclass.permitted_attributes.merge(permitted_attributes)
|
58
|
+
end
|
59
|
+
|
60
|
+
def permitted_attributes
|
61
|
+
@permitted_attributes ||= Set.new
|
62
|
+
end
|
63
|
+
|
64
|
+
def permits?(attr)
|
65
|
+
@permitted_attributes.include?(attr.to_s)
|
66
|
+
end
|
67
|
+
|
68
|
+
def attribute(attribute_name, **options)
|
69
|
+
unless permitted_attributes.add?(attribute_name.to_s)
|
70
|
+
raise "Step already defined attribute: #{attribute_name}"
|
71
|
+
end
|
72
|
+
|
73
|
+
method_name = options.fetch(:as, attribute_name)
|
74
|
+
|
75
|
+
# Define the main helper method that sets or appends the attribute value.
|
76
|
+
define_method(method_name) do |*value|
|
77
|
+
if value.empty?
|
78
|
+
get(attribute_name)
|
79
|
+
elsif options[:append]
|
80
|
+
append(attribute_name, *value)
|
81
|
+
else
|
82
|
+
set(attribute_name, *value)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Define a helper method that is equivalent to `||=` or `Set#add?`. It will
|
87
|
+
# set the attribute iff it hasn't been already set. It will return true/false
|
88
|
+
# for whether or not the value was set.
|
89
|
+
define_method("#{method_name}?") do |*args|
|
90
|
+
if args.empty?
|
91
|
+
raise ArgumentError, "`#{method_name}?` must be called with arguments"
|
92
|
+
elsif has?(method_name)
|
93
|
+
false
|
94
|
+
else
|
95
|
+
public_send(method_name, *args)
|
96
|
+
true
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
if options[:append]
|
101
|
+
# If this attribute appends by default, then provide a bang(!) helper method
|
102
|
+
# that allows you to clear and set the value in one go.
|
103
|
+
define_method("#{method_name}!") do |*args|
|
104
|
+
unset(attribute_name)
|
105
|
+
public_send(method_name, *args)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
Helpers.prepend_attribute_helper(self, attribute_name)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def attributes
|
116
|
+
@attributes ||= {}
|
117
|
+
end
|
118
|
+
|
119
|
+
def ensure_array_value(attr)
|
120
|
+
if has?(attr)
|
121
|
+
set(attr, [get(attr)]) unless get(attr).is_a?(Array)
|
122
|
+
else
|
123
|
+
set(attr, [])
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def validate(attr)
|
128
|
+
attr = attr.to_s
|
129
|
+
unless permits?(attr)
|
130
|
+
raise "Attribute not permitted on #{self.class.name} step: #{attr}"
|
131
|
+
end
|
132
|
+
|
133
|
+
attr
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Buildkite
|
4
|
+
module Pipelines
|
5
|
+
class Command
|
6
|
+
BIN_PATH = 'buildkite-agent'
|
7
|
+
|
8
|
+
def self.pipeline!(*args)
|
9
|
+
abort unless pipeline(*args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.pipeline(subcommand, *args)
|
13
|
+
new(:pipeline, subcommand, *args).run
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.artifact!(*args)
|
17
|
+
abort unless artifact(*args)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.artifact(subcommand, *args)
|
21
|
+
new(:artifact, subcommand, *args).run
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(command, subcommand, *args)
|
25
|
+
@command = command.to_s
|
26
|
+
@subcommand = subcommand.to_s
|
27
|
+
@options = extract_options(args)
|
28
|
+
@args = transform_args(args)
|
29
|
+
end
|
30
|
+
|
31
|
+
def run
|
32
|
+
system(*to_a)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def to_a
|
38
|
+
command = [BIN_PATH, @command, @subcommand]
|
39
|
+
command.concat(@options.to_a.flatten)
|
40
|
+
command.concat(@args)
|
41
|
+
end
|
42
|
+
|
43
|
+
def extract_options(args)
|
44
|
+
return {} unless args.first.is_a?(Hash)
|
45
|
+
|
46
|
+
args.shift.tap do |options|
|
47
|
+
options.transform_keys! do |key|
|
48
|
+
"--#{key.to_s.tr('_', '-')}"
|
49
|
+
end
|
50
|
+
options.transform_values!(&:to_s)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def transform_args(args)
|
55
|
+
args.map!(&:to_s)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Buildkite
|
4
|
+
module Pipelines
|
5
|
+
module Helpers
|
6
|
+
ATTRIBUTE_HELPERS = {
|
7
|
+
block: :Block,
|
8
|
+
command: :Command,
|
9
|
+
depends_on: :DependsOn,
|
10
|
+
key: :Key,
|
11
|
+
label: :Label,
|
12
|
+
plugins: :Plugins,
|
13
|
+
retry: :Retry,
|
14
|
+
skip: :Skip,
|
15
|
+
soft_fail: :SoftFail,
|
16
|
+
timeout_in_minutes: :TimeoutInMinutes,
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
ATTRIBUTE_HELPERS.each do |name, mod|
|
20
|
+
autoload mod, File.expand_path("helpers/#{name}", __dir__)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.prepend_attribute_helper(step_class, attribute)
|
24
|
+
if ATTRIBUTE_HELPERS[attribute]
|
25
|
+
step_class.prepend(const_get(ATTRIBUTE_HELPERS[attribute]))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.sanitize(obj)
|
30
|
+
case obj
|
31
|
+
when Hash
|
32
|
+
obj.transform_keys(&:to_s).transform_values { |value| sanitize(value) }
|
33
|
+
when Array
|
34
|
+
obj.map { |value| sanitize(value) }
|
35
|
+
when Symbol, Pathname
|
36
|
+
obj.to_s
|
37
|
+
else
|
38
|
+
obj
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Buildkite
|
4
|
+
module Pipelines
|
5
|
+
module Helpers
|
6
|
+
module Block
|
7
|
+
def block(*args, emoji: nil)
|
8
|
+
if emoji
|
9
|
+
emoji = Array(emoji).map { |name| ":#{name}:" }.join
|
10
|
+
args[0] = [emoji, args.first].compact.join(' ')
|
11
|
+
end
|
12
|
+
|
13
|
+
super(*args)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Buildkite
|
4
|
+
module Pipelines
|
5
|
+
module Helpers
|
6
|
+
module Command
|
7
|
+
def command(*values)
|
8
|
+
return super if values.empty?
|
9
|
+
|
10
|
+
values.flatten.each do |value|
|
11
|
+
if value == :noop
|
12
|
+
super('true')
|
13
|
+
else
|
14
|
+
super(value)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Buildkite
|
4
|
+
module Pipelines
|
5
|
+
module Helpers
|
6
|
+
module Label
|
7
|
+
def label(*args, emoji: nil)
|
8
|
+
if emoji
|
9
|
+
emoji = Array(emoji).map { |name| ":#{name}:" }.join
|
10
|
+
args[0] = [emoji, args.first].compact.join(' ')
|
11
|
+
end
|
12
|
+
|
13
|
+
super(*args)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Buildkite
|
4
|
+
module Pipelines
|
5
|
+
module Helpers
|
6
|
+
module Plugins
|
7
|
+
def plugin(plugin_name, options = nil)
|
8
|
+
plugin_name = plugin_name.to_s
|
9
|
+
@plugins ||= {}
|
10
|
+
|
11
|
+
if @plugins.key?(plugin_name)
|
12
|
+
raise ArgumentError, "Plugin already used for command step: #{plugin_name}"
|
13
|
+
end
|
14
|
+
|
15
|
+
uri, version = pipeline.plugins.fetch(plugin_name)
|
16
|
+
new_plugin = Plugin.new(uri, version, options)
|
17
|
+
@plugins[plugin_name] = new_plugin
|
18
|
+
|
19
|
+
plugins(new_plugin.to_h)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Buildkite
|
4
|
+
module Pipelines
|
5
|
+
module Helpers
|
6
|
+
module Retry
|
7
|
+
def automatically_retry(status:, limit:)
|
8
|
+
retry_value = get(:retry)&.[](:automatic)
|
9
|
+
|
10
|
+
unless retry_value.is_a?(Array)
|
11
|
+
retry_value = []
|
12
|
+
self.retry(automatic: retry_value)
|
13
|
+
end
|
14
|
+
|
15
|
+
retry_value.push(exit_status: status, limit: limit)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Buildkite
|
4
|
+
module Pipelines
|
5
|
+
module Helpers
|
6
|
+
module Skip
|
7
|
+
# A helper method to see if the step is skipped.
|
8
|
+
# Skipped steps are `true` or a non-empty string.
|
9
|
+
def skipped?
|
10
|
+
get(:skip) != false && !get(:skip).to_s.empty?
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Buildkite
|
4
|
+
module Pipelines
|
5
|
+
module Helpers
|
6
|
+
module TimeoutInMinutes
|
7
|
+
def timeout(*args)
|
8
|
+
timeout_in_minutes(*args)
|
9
|
+
end
|
10
|
+
|
11
|
+
def timeout?(*args)
|
12
|
+
timeout_in_minutes?(*args)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Buildkite
|
6
|
+
module Pipelines
|
7
|
+
class Pipeline
|
8
|
+
attr_reader :steps
|
9
|
+
attr_reader :plugins
|
10
|
+
attr_reader :templates
|
11
|
+
|
12
|
+
def initialize(definition = nil, &block)
|
13
|
+
@env = {}
|
14
|
+
@steps = []
|
15
|
+
@plugins = {}
|
16
|
+
@templates = {}
|
17
|
+
@processors = []
|
18
|
+
|
19
|
+
instance_eval(&definition) if definition
|
20
|
+
instance_eval(&block) if block_given?
|
21
|
+
end
|
22
|
+
|
23
|
+
[
|
24
|
+
Steps::Block,
|
25
|
+
Steps::Command,
|
26
|
+
Steps::Input,
|
27
|
+
Steps::Trigger,
|
28
|
+
].each do |type|
|
29
|
+
define_method(type.to_sym) do |template = nil, &block|
|
30
|
+
add(type, template, &block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def env(*args)
|
35
|
+
if args.empty?
|
36
|
+
@env
|
37
|
+
elsif args.first.is_a?(Hash)
|
38
|
+
@env.merge!(args.first.transform_keys(&:to_s))
|
39
|
+
else
|
40
|
+
raise ArgumentError, 'value must be hash'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def skip(template = nil, &block)
|
45
|
+
step = add(Steps::Skip, template, &block)
|
46
|
+
# A skip step has a nil/noop command.
|
47
|
+
step.command(nil)
|
48
|
+
# Always set the skip attribute if it's in a falsey state.
|
49
|
+
step.skip(true) if !step.get(:skip) || step.skip.empty?
|
50
|
+
step
|
51
|
+
end
|
52
|
+
|
53
|
+
def wait(attributes = {}, &block)
|
54
|
+
step = add(Steps::Wait, &block)
|
55
|
+
step.wait(nil)
|
56
|
+
attributes.each do |key, value|
|
57
|
+
step.set(key, value)
|
58
|
+
end
|
59
|
+
step
|
60
|
+
end
|
61
|
+
|
62
|
+
def plugin(name, uri, version)
|
63
|
+
name = name.to_s
|
64
|
+
|
65
|
+
if plugins.key?(name)
|
66
|
+
raise ArgumentError, "Plugin already defined: #{name}"
|
67
|
+
end
|
68
|
+
|
69
|
+
@plugins[name] = [uri, version]
|
70
|
+
end
|
71
|
+
|
72
|
+
def template(name, &definition)
|
73
|
+
name = name.to_s
|
74
|
+
|
75
|
+
if templates.key?(name)
|
76
|
+
raise ArgumentError, "Template already defined: #{name}"
|
77
|
+
elsif !block_given?
|
78
|
+
raise ArgumentError, 'Template definition block must be given'
|
79
|
+
end
|
80
|
+
|
81
|
+
@templates[name.to_s] = definition
|
82
|
+
end
|
83
|
+
|
84
|
+
def processors(*processor_classes)
|
85
|
+
unless processor_classes.empty?
|
86
|
+
@processors.clear
|
87
|
+
|
88
|
+
processor_classes.flatten.each do |processor|
|
89
|
+
unless processor < Buildkite::Builder::Processors::Abstract
|
90
|
+
raise "#{processor} must inherit from Buildkite::Builder::Processors::Abstract"
|
91
|
+
end
|
92
|
+
|
93
|
+
@processors << processor
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
@processors
|
98
|
+
end
|
99
|
+
|
100
|
+
def to_h
|
101
|
+
pipeline = {}
|
102
|
+
if env.any?
|
103
|
+
pipeline[:env] = env
|
104
|
+
end
|
105
|
+
pipeline[:steps] = steps.map(&:to_h)
|
106
|
+
|
107
|
+
Helpers.sanitize(pipeline)
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_yaml
|
111
|
+
YAML.dump(to_h)
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def add(step_class, template = nil, &block)
|
117
|
+
steps.push(step_class.new(self, find_template(template), &block)).last
|
118
|
+
end
|
119
|
+
|
120
|
+
def find_template(name)
|
121
|
+
return unless name
|
122
|
+
|
123
|
+
templates[name.to_s] || begin
|
124
|
+
raise ArgumentError, "Template not defined: #{name}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|