buildkite-builder 1.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|