buildkite-builder 1.4.1 → 2.0.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/VERSION +1 -1
  4. data/lib/buildkite/builder/commands/abstract.rb +4 -4
  5. data/lib/buildkite/builder/commands/preview.rb +1 -1
  6. data/lib/buildkite/builder/commands/run.rb +1 -1
  7. data/lib/buildkite/builder/commands.rb +2 -2
  8. data/lib/buildkite/builder/data.rb +41 -0
  9. data/lib/buildkite/builder/dsl.rb +21 -0
  10. data/lib/buildkite/builder/extension.rb +54 -0
  11. data/lib/buildkite/builder/extension_manager.rb +55 -0
  12. data/lib/buildkite/builder/extensions/env.rb +21 -0
  13. data/lib/buildkite/builder/extensions/notify.rb +21 -0
  14. data/lib/buildkite/builder/extensions/steps.rb +60 -0
  15. data/lib/buildkite/builder/extensions/use.rb +13 -0
  16. data/lib/buildkite/builder/extensions.rb +12 -0
  17. data/lib/buildkite/builder/group.rb +42 -0
  18. data/lib/buildkite/builder/loaders/{processors.rb → extensions.rb} +9 -9
  19. data/lib/buildkite/builder/loaders.rb +1 -1
  20. data/lib/buildkite/builder/{context.rb → pipeline.rb} +40 -49
  21. data/lib/buildkite/builder/plugin.rb +19 -0
  22. data/lib/buildkite/builder/plugin_collection.rb +49 -0
  23. data/lib/buildkite/builder/plugin_manager.rb +27 -0
  24. data/lib/buildkite/builder/step_collection.rb +43 -0
  25. data/lib/buildkite/builder/template_manager.rb +27 -0
  26. data/lib/buildkite/builder.rb +12 -1
  27. data/lib/buildkite/pipelines/attributes.rb +4 -2
  28. data/lib/buildkite/pipelines/helpers/command.rb +1 -5
  29. data/lib/buildkite/pipelines/helpers/plugins.rb +6 -12
  30. data/lib/buildkite/pipelines/step_context.rb +0 -4
  31. data/lib/buildkite/pipelines/steps/abstract.rb +4 -4
  32. data/lib/buildkite/pipelines.rb +0 -1
  33. metadata +21 -12
  34. data/lib/buildkite/builder/dsl/pipeline.rb +0 -0
  35. data/lib/buildkite/builder/processors/abstract.rb +0 -76
  36. data/lib/buildkite/builder/processors.rb +0 -9
  37. data/lib/buildkite/pipelines/pipeline.rb +0 -140
  38. data/lib/buildkite/pipelines/plugin.rb +0 -23
  39. data/lib/buildkite/pipelines/steps/group.rb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9a3cd9ffee7742ded0fca17e0a11a214bd6383bd05c1c6a8101348d45b938bb1
4
- data.tar.gz: f1b32964a3da0acdef53ba9febe034d1e77dc826ccb0b6722ffec1839f0f8126
3
+ metadata.gz: 41faf27b8584ef0a615dfafb727dcac55254a4a2d95107518f550130bc470e0a
4
+ data.tar.gz: 2e778aca984b052a3b4be40c015a969b9fc7d329ad0ca8a28ef9eb605d42457b
5
5
  SHA512:
6
- metadata.gz: d0ecf15f187b6644cf1e1af05c9bf425f17c8f7e2ecf4054b973e977a9feb202db8a63014286a9177b276687f935cf8e62ffc56ea7933503e0355fcc378db6fb
7
- data.tar.gz: 71813fc583c20d14b99f487283f111ffc77bf9ad0f3adfbf97c87d17e13451eb4436f99a53d182952c5e37ee65c20376730d63cf1dec98ae777fbaf49bf8e3be
6
+ metadata.gz: 638f4fe0bf8638a2525b6eea0cc778bb078cdc44d81b23b73dd8c29364404cb0a70bc08441b04ab5eb10f670de33cc1d55ff8e2136684679f4499326b9d77c27
7
+ data.tar.gz: 4408c929ec57aaef0159a1966d7d6dd10d33bc05ce43b382f94a29c12a4eb719418360e4a18717c4324377a424e73b9840186a6b1cc894c368d5ba21e389e59f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## 1.5.0
2
+ * Merge `BuildKite::Builder::Context` and `BuildKite::Pipelines::Pipeline` to `BuildKite::Builder::Pipeline` (#37)
3
+
1
4
  ## 1.4.1
2
5
  * Fix the Github API Builder to account for Buildkite having both `.git` and no file exention repository URIs (#33)
3
6
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.4.1
1
+ 2.0.0.beta4
@@ -8,9 +8,9 @@ module Buildkite
8
8
  class Abstract
9
9
  PIPELINES_DIRECTORY = 'pipelines'
10
10
  POSSIBLE_PIPELINE_PATHS = [
11
- File.join('.buildkite', Context::PIPELINE_DEFINITION_FILE),
12
- File.join('buildkite', Context::PIPELINE_DEFINITION_FILE),
13
- File.join(Context::PIPELINE_DEFINITION_FILE)
11
+ File.join('.buildkite', Pipeline::PIPELINE_DEFINITION_FILE),
12
+ File.join('buildkite', Pipeline::PIPELINE_DEFINITION_FILE),
13
+ File.join(Pipeline::PIPELINE_DEFINITION_FILE)
14
14
  ].freeze
15
15
  POSSIBLE_PIPELINES_PATHS = [
16
16
  File.join('.buildkite', PIPELINES_DIRECTORY),
@@ -91,7 +91,7 @@ module Buildkite
91
91
 
92
92
  def find_root_by_multi_pipeline
93
93
  pipelines_path = POSSIBLE_PIPELINES_PATHS.map { |path| Builder.root.join(path) }.find(&:directory?)
94
-
94
+
95
95
  if pipelines_path
96
96
  if pipeline_slug
97
97
  path = pipelines_path.join(pipeline_slug)
@@ -9,7 +9,7 @@ module Buildkite
9
9
  self.description = 'Outputs the pipeline YAML.'
10
10
 
11
11
  def run
12
- puts Context.build(pipeline_path).pipeline.to_yaml
12
+ puts Pipeline.new(pipeline_path).to_yaml
13
13
  end
14
14
  end
15
15
  end
@@ -16,7 +16,7 @@ module Buildkite
16
16
  # This entrypoint is for running on CI. It expects certain environment
17
17
  # variables to be set. It also uploads the pipeline to Buildkite.
18
18
  log.info "#{'+++ ' if Buildkite.env}🧰 " + 'Buildkite Builder'.color(:springgreen) + " ─ #{relative_pipeline_path.to_s.yellow}"
19
- Context.new(pipeline_path, logger: log).upload
19
+ Pipeline.new(pipeline_path, logger: log).upload
20
20
  end
21
21
 
22
22
  private
@@ -32,10 +32,10 @@ module Buildkite
32
32
  puts <<~HELP
33
33
  #{'SYNOPSIS'.bright}
34
34
  \t#{'buildkite-builder'.bright} COMMAND [OPTIONS] [PIPELINE]
35
-
35
+
36
36
  \t#{'To see available options for specific commands:'.color(:dimgray)}
37
37
  \t#{'buildkite-builder'.bright} COMMAND --help
38
-
38
+
39
39
  #{'COMMANDS'.bright}
40
40
  HELP
41
41
  COMMANDS.each do |command, klass|
@@ -0,0 +1,41 @@
1
+ module Buildkite
2
+ module Builder
3
+ class Data
4
+ def initialize
5
+ @data = Hash.new
6
+ end
7
+
8
+ def to_definition
9
+ @data.each_with_object({}) do |(key, value), hash|
10
+ value = value.respond_to?(:to_definition) ? value.to_definition : value
11
+
12
+ next if value.empty?
13
+
14
+ hash[key] = value
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def method_missing(name, *args, &block)
21
+ if name.end_with?('=')
22
+ name = name.to_s.delete_suffix('=').to_sym
23
+
24
+ if respond_to_missing?(name)
25
+ raise ArgumentError, "Data already contains key '#{name}'"
26
+ else
27
+ return @data[name] = args.first
28
+ end
29
+ elsif respond_to_missing?(name)
30
+ return @data[name]
31
+ end
32
+
33
+ super
34
+ end
35
+
36
+ def respond_to_missing?(name, *)
37
+ @data.key?(name)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Buildkite
4
+ module Builder
5
+ class Dsl
6
+ attr_reader :context
7
+
8
+ def extend(mod)
9
+ if mod < Extension
10
+ super(mod.dsl) if mod.dsl
11
+ else
12
+ super
13
+ end
14
+ end
15
+
16
+ def initialize(context)
17
+ @context = context
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Buildkite
4
+ module Builder
5
+ class Extension
6
+ class << self
7
+ attr_reader :dsl
8
+
9
+ def dsl(&block)
10
+ @dsl = Module.new(&block) if block_given?
11
+ @dsl
12
+ end
13
+ end
14
+
15
+ attr_reader :context
16
+ attr_reader :options
17
+
18
+ def initialize(context, **options)
19
+ @context = context
20
+ @options = options
21
+
22
+ prepare
23
+ end
24
+
25
+ def build
26
+ # Override to provide extra functionality.
27
+ end
28
+
29
+ private
30
+
31
+ def log
32
+ context.logger
33
+ end
34
+
35
+ def buildkite
36
+ @buildkite ||= begin
37
+ unless Buildkite.env
38
+ raise 'Must be in Buildkite environment to access the Buildkite API'
39
+ end
40
+
41
+ Buildkite::Pipelines::Api.new(Buildkite.env.api_token)
42
+ end
43
+ end
44
+
45
+ def prepare
46
+ # Override to provide extra functionality.
47
+ end
48
+
49
+ def pipeline(&block)
50
+ context.dsl.instance_eval(&block)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,55 @@
1
+ module Buildkite
2
+ module Builder
3
+ class ExtensionManager
4
+ include LoggingUtils
5
+ using Rainbow
6
+
7
+ def initialize(context)
8
+ @context = context
9
+ @extensions = []
10
+
11
+ @loader = Loaders::Extensions.load(@context.root)
12
+ end
13
+
14
+ def use(extension, native: false, **args)
15
+ unless extension < Buildkite::Builder::Extension
16
+ raise "#{extension} must subclass Buildkite::Builder::Extension"
17
+ end
18
+
19
+ @extensions.push(extension.new(@context, **args))
20
+ @context.dsl.extend(extension)
21
+ end
22
+
23
+ def build
24
+ @extensions.each do |extension|
25
+ log_build(extension.class.name) { extension.build }
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def log
32
+ @context.logger
33
+ end
34
+
35
+ def log_build(name)
36
+ log.info "\nProcessing ".color(:dimgray) + name.color(:springgreen)
37
+
38
+ results = benchmark('└──'.color(:springgreen) + ' Finished in %s'.color(:dimgray)) do
39
+ formatter = log.formatter
40
+ log.formatter = proc do |_severity, _datetime, _progname, msg|
41
+ '│'.color(:springgreen) + " #{msg}\n"
42
+ end
43
+
44
+ begin
45
+ yield
46
+ ensure
47
+ log.formatter = formatter
48
+ end
49
+ end
50
+
51
+ log.info results
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,21 @@
1
+ module Buildkite
2
+ module Builder
3
+ module Extensions
4
+ class Env < Extension
5
+ def prepare
6
+ context.data.env = {}
7
+ end
8
+
9
+ dsl do
10
+ def env(*args)
11
+ if args.first.is_a?(Hash)
12
+ context.data.env.merge!(args.first.transform_keys(&:to_s))
13
+ else
14
+ raise ArgumentError, 'value must be hash'
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module Buildkite
2
+ module Builder
3
+ module Extensions
4
+ class Notify < Extension
5
+ def prepare
6
+ context.data.notify = []
7
+ end
8
+
9
+ dsl do
10
+ def notify(*args)
11
+ if args.first.is_a?(Hash)
12
+ context.data.notify.push(args.first.transform_keys(&:to_s))
13
+ else
14
+ raise ArgumentError, 'value must be hash'
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,60 @@
1
+ module Buildkite
2
+ module Builder
3
+ module Extensions
4
+ class Steps < Extension
5
+ def prepare
6
+ context.data.steps = StepCollection.new(
7
+ TemplateManager.new(context.root),
8
+ PluginManager.new
9
+ )
10
+ end
11
+
12
+ dsl do
13
+ def group(label = nil, &block)
14
+ raise "Group does not allow nested in another Group" if context.is_a?(Group)
15
+
16
+ context.data.steps.push(Buildkite::Builder::Group.new(label, context.data.steps, &block))
17
+ end
18
+
19
+ def plugin(name, uri)
20
+ context.data.steps.plugins.add(name, uri)
21
+ end
22
+
23
+ def block(template = nil, **args, &block)
24
+ context.data.steps.add(Pipelines::Steps::Block, template, **args, &block)
25
+ end
26
+
27
+ def command(template = nil, **args, &block)
28
+ context.data.steps.add(Pipelines::Steps::Command, template, **args, &block)
29
+ end
30
+
31
+ def input(template = nil, **args, &block)
32
+ context.data.steps.add(Pipelines::Steps::Input, template, **args, &block)
33
+ end
34
+
35
+ def trigger(template = nil, **args, &block)
36
+ context.data.steps.add(Pipelines::Steps::Trigger, template, **args, &block)
37
+ end
38
+
39
+ def skip(template = nil, **args, &block)
40
+ step = context.data.steps.add(Pipelines::Steps::Skip, template, **args, &block)
41
+ # A skip step has a nil/noop command.
42
+ step.command(nil)
43
+ # Always set the skip attribute if it's in a falsey state.
44
+ step.skip(true) if !step.get(:skip) || step.skip.empty?
45
+ step
46
+ end
47
+
48
+ def wait(attributes = {}, &block)
49
+ step = context.data.steps.add(Pipelines::Steps::Wait, &block)
50
+ step.wait(nil)
51
+ attributes.each do |key, value|
52
+ step.set(key, value)
53
+ end
54
+ step
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,13 @@
1
+ module Buildkite
2
+ module Builder
3
+ module Extensions
4
+ class Use < Extension
5
+ dsl do
6
+ def use(extension_class, **args)
7
+ context.use(extension_class, **args)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Buildkite
4
+ module Builder
5
+ module Extensions
6
+ autoload :Env, File.expand_path('extensions/env', __dir__)
7
+ autoload :Notify, File.expand_path('extensions/notify', __dir__)
8
+ autoload :Steps, File.expand_path('extensions/steps', __dir__)
9
+ autoload :Use, File.expand_path('extensions/use', __dir__)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,42 @@
1
+ module Buildkite
2
+ module Builder
3
+ class Group
4
+ include Buildkite::Pipelines::Attributes
5
+
6
+ attr_reader :label
7
+ attr_reader :data
8
+
9
+ attribute :depends_on, append: true
10
+ attribute :key
11
+
12
+ def self.to_sym
13
+ name.split('::').last.downcase.to_sym
14
+ end
15
+
16
+ def initialize(label, steps, &block)
17
+ @label = label
18
+ @data = Data.new
19
+ @data.steps = StepCollection.new(
20
+ steps.templates,
21
+ steps.plugins
22
+ )
23
+ @data.notify = []
24
+
25
+ @dsl = Dsl.new(self)
26
+ @dsl.extend(Extensions::Steps)
27
+ @dsl.extend(Extensions::Notify)
28
+ instance_eval(&block) if block_given?
29
+ self
30
+ end
31
+
32
+ def to_h
33
+ attributes = super
34
+ { group: label }.merge(attributes).merge(data.to_definition)
35
+ end
36
+
37
+ def method_missing(method_name, *args, **kwargs, &_block)
38
+ @dsl.public_send(method_name, *args, **kwargs, &_block)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -5,17 +5,17 @@ require 'pathname'
5
5
  module Buildkite
6
6
  module Builder
7
7
  module Loaders
8
- class Processors < Abstract
9
- PROCESSORS_PATH = Pathname.new('processors').freeze
8
+ class Extensions < Abstract
9
+ EXTENSIONS_PATH = Pathname.new('extensions').freeze
10
10
 
11
11
  def load
12
- load_processors_from_path(global_processors_path)
13
- load_processors_from_path(pipeline_processors_path)
12
+ load_extensions_from_path(global_extensions_path)
13
+ load_extensions_from_path(pipeline_extensions_path)
14
14
  end
15
15
 
16
16
  private
17
17
 
18
- def load_processors_from_path(path)
18
+ def load_extensions_from_path(path)
19
19
  return unless path.directory?
20
20
 
21
21
  path.children.map do |file|
@@ -24,12 +24,12 @@ module Buildkite
24
24
  end
25
25
  end
26
26
 
27
- def global_processors_path
28
- buildkite_path.join(PROCESSORS_PATH)
27
+ def global_extensions_path
28
+ buildkite_path.join(EXTENSIONS_PATH)
29
29
  end
30
30
 
31
- def pipeline_processors_path
32
- root.join(PROCESSORS_PATH)
31
+ def pipeline_extensions_path
32
+ root.join(EXTENSIONS_PATH)
33
33
  end
34
34
  end
35
35
  end
@@ -6,7 +6,7 @@ module Buildkite
6
6
  autoload :Abstract, File.expand_path('loaders/abstract', __dir__)
7
7
  autoload :Manifests, File.expand_path('loaders/manifests', __dir__)
8
8
  autoload :Templates, File.expand_path('loaders/templates', __dir__)
9
- autoload :Processors, File.expand_path('loaders/processors', __dir__)
9
+ autoload :Extensions, File.expand_path('loaders/extensions', __dir__)
10
10
  end
11
11
  end
12
12
  end
@@ -1,59 +1,52 @@
1
1
  require 'logger'
2
2
  require 'tempfile'
3
+ require 'yaml'
4
+ require 'pathname'
5
+ require 'forwardable'
3
6
 
4
7
  module Buildkite
5
8
  module Builder
6
- class Context
9
+ class Pipeline
10
+ extend Forwardable
7
11
  include Definition::Helper
8
12
  include LoggingUtils
9
13
  using Rainbow
10
14
 
11
15
  PIPELINE_DEFINITION_FILE = Pathname.new('pipeline.rb').freeze
12
16
 
13
- attr_reader :logger
14
- attr_reader :root
15
- attr_reader :pipeline
16
- attr_reader :artifacts
17
+ def_delegator :@extensions, :use
17
18
 
18
- def self.build(root, logger: nil)
19
- context = new(root, logger: logger)
20
- context.build
21
- context
22
- end
19
+ attr_reader :logger,
20
+ :root,
21
+ :artifacts,
22
+ :plugins,
23
+ :dsl,
24
+ :data
23
25
 
24
26
  def initialize(root, logger: nil)
25
27
  @root = root
26
28
  @logger = logger || Logger.new(File::NULL)
27
29
  @artifacts = []
28
- end
29
-
30
- def build
31
- results = benchmark("\nDone (%s)".color(:springgreen)) do
32
- unless @pipeline
33
- @pipeline = Pipelines::Pipeline.new
34
-
35
- load_manifests
36
- load_templates
37
- load_processors
38
- load_pipeline
39
- run_processors
40
- end
41
- end
42
- logger.info(results)
43
-
44
- @pipeline
30
+ @plugins = {}
31
+ @dsl = Dsl.new(self)
32
+ @extensions = ExtensionManager.new(self)
33
+ @data = Data.new
34
+
35
+ use(Extensions::Use)
36
+ use(Extensions::Env)
37
+ use(Extensions::Notify)
38
+ use(Extensions::Steps)
39
+ load_manifests
45
40
  end
46
41
 
47
42
  def upload
48
- build unless @pipeline
49
-
50
43
  logger.info '+++ :paperclip: Uploading artifacts'
51
44
  upload_artifacts
52
45
 
53
46
  # Upload the pipeline.
54
47
  Tempfile.create(['pipeline', '.yml']) do |file|
55
48
  file.sync = true
56
- file.write(pipeline.to_yaml)
49
+ file.write(to_yaml)
57
50
 
58
51
  logger.info '+++ :paperclip: Uploading pipeline.yml as artifact'
59
52
  Buildkite::Pipelines::Command.artifact!(:upload, file.path)
@@ -62,27 +55,29 @@ module Buildkite
62
55
  end
63
56
  end
64
57
 
65
- private
66
-
67
- def load_manifests
68
- Loaders::Manifests.load(root).each do |name, asset|
69
- Manifest[name] = asset
58
+ def to_h
59
+ @pipeline_hash ||= begin
60
+ results = benchmark("\nDone (%s)".color(:springgreen)) do
61
+ dsl.instance_eval(&pipeline_definition)
62
+ extensions.build
63
+ end
64
+ logger.info(results)
65
+ # Build the pipeline definition from pipeline data.
66
+ Pipelines::Helpers.sanitize(data.to_definition)
70
67
  end
71
68
  end
72
69
 
73
- def load_templates
74
- Loaders::Templates.load(root).each do |name, asset|
75
- pipeline.template(name, &asset)
76
- end
70
+ def to_yaml
71
+ YAML.dump(to_h)
77
72
  end
78
73
 
79
- def load_processors
80
- Loaders::Processors.load(root)
81
- end
74
+ private
75
+
76
+ attr_reader :extensions
82
77
 
83
- def run_processors
84
- pipeline.processors.each do |processor|
85
- processor.process(self)
78
+ def load_manifests
79
+ Loaders::Manifests.load(root).each do |name, asset|
80
+ Manifest[name] = asset
86
81
  end
87
82
  end
88
83
 
@@ -96,10 +91,6 @@ module Buildkite
96
91
  end
97
92
  end
98
93
 
99
- def load_pipeline
100
- pipeline.instance_eval(&pipeline_definition)
101
- end
102
-
103
94
  def pipeline_definition
104
95
  @pipeline_definition ||= load_definition(root.join(PIPELINE_DEFINITION_FILE), Definition::Pipeline)
105
96
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Buildkite
4
+ module Builder
5
+ class Plugin
6
+ attr_reader :uri, :source, :version, :options
7
+
8
+ def initialize(uri, options = nil)
9
+ @uri = uri
10
+ @source, @version = uri.split('#')
11
+ @options = options
12
+ end
13
+
14
+ def to_h
15
+ Buildkite::Pipelines::Helpers.sanitize(uri => options)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,49 @@
1
+ module Buildkite
2
+ module Builder
3
+ class PluginCollection
4
+ attr_reader :plugin_manager
5
+
6
+ def initialize(plugin_manager)
7
+ @plugin_manager = plugin_manager
8
+ @collection = []
9
+ end
10
+
11
+ def add(resource, options = nil)
12
+ plugin =
13
+ case resource
14
+ when Symbol
15
+ uri = plugin_manager.fetch(resource.to_s)
16
+
17
+ raise ArgumentError, "Plugin `#{resource}` does not exist" unless uri
18
+
19
+ Plugin.new(uri, options)
20
+ when String
21
+ Plugin.new(resource, options)
22
+ when Plugin
23
+ resource
24
+ else
25
+ raise ArgumentError, "Unknown plugin `#{resource.inspect}`"
26
+ end
27
+
28
+ @collection.push(plugin).last
29
+ end
30
+
31
+ def find(source)
32
+ source_string =
33
+ case source
34
+ when String then source
35
+ when Plugin then source.source
36
+ else raise ArgumentError, "Unknown source #{source.inspect}"
37
+ end
38
+
39
+ @collection.select do |plugin|
40
+ plugin.source == source_string
41
+ end
42
+ end
43
+
44
+ def to_definition
45
+ @collection.map(&:to_h)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,27 @@
1
+ module Buildkite
2
+ module Builder
3
+ class PluginManager
4
+ def initialize
5
+ @plugins = {}
6
+ end
7
+
8
+ def add(name, uri)
9
+ name = name.to_s
10
+
11
+ if @plugins.key?(name)
12
+ raise ArgumentError, "Plugin already defined: #{name}"
13
+ end
14
+
15
+ @plugins[name] = uri
16
+ end
17
+
18
+ def fetch(name)
19
+ @plugins[name]
20
+ end
21
+
22
+ def to_definition
23
+ # No-op
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,43 @@
1
+ module Buildkite
2
+ module Builder
3
+ class StepCollection
4
+ attr_reader :templates
5
+ attr_reader :plugins
6
+ attr_reader :steps
7
+
8
+ def initialize(templates, plugins)
9
+ @templates = templates
10
+ @plugins = plugins
11
+ @steps = []
12
+ end
13
+
14
+ def each(*types)
15
+ types = types.flatten
16
+
17
+ @steps.each do |step|
18
+ if types.include?(step.class.to_sym)
19
+ yield step
20
+ elsif step.is_a?(Group)
21
+ step.data.steps.each(*types) do |step|
22
+ yield step
23
+ end
24
+ elsif types.empty?
25
+ yield step
26
+ end
27
+ end
28
+ end
29
+
30
+ def add(step_class, template = nil, **args, &block)
31
+ @steps.push(step_class.new(self, template, **args, &block)).last
32
+ end
33
+
34
+ def push(step)
35
+ @steps.push(step)
36
+ end
37
+
38
+ def to_definition
39
+ @steps.map(&:to_h)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,27 @@
1
+ module Buildkite
2
+ module Builder
3
+ class TemplateManager
4
+ def initialize(root)
5
+ @templates = {}
6
+
7
+ Loaders::Templates.load(root).each do |name, asset|
8
+ @templates[name.to_s] = asset
9
+ end
10
+ end
11
+
12
+ def find(name)
13
+ return unless name
14
+
15
+ unless definition = @templates[name.to_s]
16
+ raise ArgumentError, "Template not defined: #{name}"
17
+ end
18
+
19
+ definition
20
+ end
21
+
22
+ def to_definition
23
+ # No-op
24
+ end
25
+ end
26
+ end
27
+ end
@@ -5,8 +5,14 @@ require 'pathname'
5
5
  module Buildkite
6
6
  module Builder
7
7
  autoload :Commands, File.expand_path('builder/commands', __dir__)
8
- autoload :Context, File.expand_path('builder/context', __dir__)
8
+ autoload :Group, File.expand_path('builder/group', __dir__)
9
+ autoload :Pipeline, File.expand_path('builder/pipeline', __dir__)
9
10
  autoload :Definition, File.expand_path('builder/definition', __dir__)
11
+ autoload :Data, File.expand_path('builder/data', __dir__)
12
+ autoload :Dsl, File.expand_path('builder/dsl', __dir__)
13
+ autoload :Extension, File.expand_path('builder/extension', __dir__)
14
+ autoload :ExtensionManager, File.expand_path('builder/extension_manager', __dir__)
15
+ autoload :Extensions, File.expand_path('builder/extensions', __dir__)
10
16
  autoload :FileResolver, File.expand_path('builder/file_resolver', __dir__)
11
17
  autoload :Github, File.expand_path('builder/github', __dir__)
12
18
  autoload :Loaders, File.expand_path('builder/loaders', __dir__)
@@ -14,6 +20,11 @@ module Buildkite
14
20
  autoload :Manifest, File.expand_path('builder/manifest', __dir__)
15
21
  autoload :Processors, File.expand_path('builder/processors', __dir__)
16
22
  autoload :Rainbow, File.expand_path('builder/rainbow', __dir__)
23
+ autoload :Plugin, File.expand_path('builder/plugin', __dir__)
24
+ autoload :PluginCollection, File.expand_path('builder/plugin_collection', __dir__)
25
+ autoload :StepCollection, File.expand_path('builder/step_collection', __dir__)
26
+ autoload :TemplateManager, File.expand_path('builder/template_manager', __dir__)
27
+ autoload :PluginManager, File.expand_path('builder/plugin_manager', __dir__)
17
28
 
18
29
  BUILDKITE_DIRECTORY_NAME = Pathname.new('.buildkite').freeze
19
30
 
@@ -48,7 +48,9 @@ module Buildkite
48
48
 
49
49
  def to_h
50
50
  permitted_attributes.each_with_object({}) do |attr, hash|
51
- hash[attr] = get(attr) if has?(attr)
51
+ next unless has?(attr)
52
+
53
+ hash[attr] = get(attr).respond_to?(:to_definition) ? get(attr).to_definition : get(attr)
52
54
  end
53
55
  end
54
56
 
@@ -84,7 +86,7 @@ module Buildkite
84
86
  end
85
87
 
86
88
  # 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
89
+ # set the attribute if it hasn't been already set. It will return true/false
88
90
  # for whether or not the value was set.
89
91
  define_method("#{method_name}?") do |*args|
90
92
  if args.empty?
@@ -8,11 +8,7 @@ module Buildkite
8
8
  return super if values.empty?
9
9
 
10
10
  values.flatten.each do |value|
11
- if value == :noop
12
- super('true')
13
- else
14
- super(value)
15
- end
11
+ super(value)
16
12
  end
17
13
  end
18
14
  end
@@ -4,19 +4,13 @@ module Buildkite
4
4
  module Pipelines
5
5
  module Helpers
6
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
7
+ def plugin(name_or_source, options = nil)
8
+ attributes['plugins'] ||= Buildkite::Builder::PluginCollection.new(step_collection.plugins)
9
+ attributes['plugins'].add(name_or_source, options)
10
+ end
18
11
 
19
- plugins(new_plugin.to_h)
12
+ def plugins
13
+ attributes['plugins']
20
14
  end
21
15
  end
22
16
  end
@@ -13,10 +13,6 @@ module Buildkite
13
13
  @data = {}
14
14
  end
15
15
 
16
- def pipeline
17
- step.pipeline
18
- end
19
-
20
16
  def [](key)
21
17
  args[key]
22
18
  end
@@ -11,16 +11,16 @@ module Buildkite
11
11
 
12
12
  def_delegator :@context, :data
13
13
 
14
- attr_reader :pipeline
15
14
  attr_reader :template
15
+ attr_reader :step_collection
16
16
 
17
17
  def self.to_sym
18
18
  name.split('::').last.downcase.to_sym
19
19
  end
20
20
 
21
- def initialize(pipeline, template = nil, **args, &block)
22
- @pipeline = pipeline
23
- @template = template
21
+ def initialize(step_collection, template_name, **args, &block)
22
+ @step_collection = step_collection
23
+ @template = step_collection.templates.find(template_name)
24
24
  @context = StepContext.new(self, **args)
25
25
 
26
26
  instance_exec(@context, &template) if template
@@ -6,7 +6,6 @@ module Buildkite
6
6
  autoload :Attributes, File.expand_path('pipelines/attributes', __dir__)
7
7
  autoload :Command, File.expand_path('pipelines/command', __dir__)
8
8
  autoload :Helpers, File.expand_path('pipelines/helpers', __dir__)
9
- autoload :Pipeline, File.expand_path('pipelines/pipeline', __dir__)
10
9
  autoload :Plugin, File.expand_path('pipelines/plugin', __dir__)
11
10
  autoload :StepContext, File.expand_path('pipelines/step_context', __dir__)
12
11
  autoload :Steps, File.expand_path('pipelines/steps', __dir__)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: buildkite-builder
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.1
4
+ version: 2.0.0.beta4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ngan Pham
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2021-04-13 00:00:00.000000000 Z
12
+ date: 2021-08-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sorted_set
@@ -133,22 +133,34 @@ files:
133
133
  - lib/buildkite/builder/commands/files.rb
134
134
  - lib/buildkite/builder/commands/preview.rb
135
135
  - lib/buildkite/builder/commands/run.rb
136
- - lib/buildkite/builder/context.rb
136
+ - lib/buildkite/builder/data.rb
137
137
  - lib/buildkite/builder/definition.rb
138
- - lib/buildkite/builder/dsl/pipeline.rb
138
+ - lib/buildkite/builder/dsl.rb
139
+ - lib/buildkite/builder/extension.rb
140
+ - lib/buildkite/builder/extension_manager.rb
141
+ - lib/buildkite/builder/extensions.rb
142
+ - lib/buildkite/builder/extensions/env.rb
143
+ - lib/buildkite/builder/extensions/notify.rb
144
+ - lib/buildkite/builder/extensions/steps.rb
145
+ - lib/buildkite/builder/extensions/use.rb
139
146
  - lib/buildkite/builder/file_resolver.rb
140
147
  - lib/buildkite/builder/github.rb
148
+ - lib/buildkite/builder/group.rb
141
149
  - lib/buildkite/builder/loaders.rb
142
150
  - lib/buildkite/builder/loaders/abstract.rb
151
+ - lib/buildkite/builder/loaders/extensions.rb
143
152
  - lib/buildkite/builder/loaders/manifests.rb
144
- - lib/buildkite/builder/loaders/processors.rb
145
153
  - lib/buildkite/builder/loaders/templates.rb
146
154
  - lib/buildkite/builder/logging_utils.rb
147
155
  - lib/buildkite/builder/manifest.rb
148
156
  - lib/buildkite/builder/manifest/rule.rb
149
- - lib/buildkite/builder/processors.rb
150
- - lib/buildkite/builder/processors/abstract.rb
157
+ - lib/buildkite/builder/pipeline.rb
158
+ - lib/buildkite/builder/plugin.rb
159
+ - lib/buildkite/builder/plugin_collection.rb
160
+ - lib/buildkite/builder/plugin_manager.rb
151
161
  - lib/buildkite/builder/rainbow.rb
162
+ - lib/buildkite/builder/step_collection.rb
163
+ - lib/buildkite/builder/template_manager.rb
152
164
  - lib/buildkite/env.rb
153
165
  - lib/buildkite/pipelines.rb
154
166
  - lib/buildkite/pipelines/api.rb
@@ -165,14 +177,11 @@ files:
165
177
  - lib/buildkite/pipelines/helpers/skip.rb
166
178
  - lib/buildkite/pipelines/helpers/soft_fail.rb
167
179
  - lib/buildkite/pipelines/helpers/timeout_in_minutes.rb
168
- - lib/buildkite/pipelines/pipeline.rb
169
- - lib/buildkite/pipelines/plugin.rb
170
180
  - lib/buildkite/pipelines/step_context.rb
171
181
  - lib/buildkite/pipelines/steps.rb
172
182
  - lib/buildkite/pipelines/steps/abstract.rb
173
183
  - lib/buildkite/pipelines/steps/block.rb
174
184
  - lib/buildkite/pipelines/steps/command.rb
175
- - lib/buildkite/pipelines/steps/group.rb
176
185
  - lib/buildkite/pipelines/steps/input.rb
177
186
  - lib/buildkite/pipelines/steps/skip.rb
178
187
  - lib/buildkite/pipelines/steps/trigger.rb
@@ -196,9 +205,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
196
205
  version: 2.3.0
197
206
  required_rubygems_version: !ruby/object:Gem::Requirement
198
207
  requirements:
199
- - - ">="
208
+ - - ">"
200
209
  - !ruby/object:Gem::Version
201
- version: '0'
210
+ version: 1.3.1
202
211
  requirements: []
203
212
  rubygems_version: 3.2.2
204
213
  signing_key:
File without changes
@@ -1,76 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Buildkite
4
- module Builder
5
- module Processors
6
- class Abstract
7
- include LoggingUtils
8
- using Rainbow
9
-
10
- def self.process(context)
11
- new(context).run
12
- end
13
-
14
- def initialize(context)
15
- @context = context
16
- end
17
-
18
- def run
19
- _log_run { process }
20
- end
21
-
22
- private
23
-
24
- attr_reader :context
25
-
26
- def process
27
- raise NotImplementedError
28
- end
29
-
30
- def log
31
- context.logger
32
- end
33
-
34
- def pipeline
35
- context.pipeline
36
- end
37
-
38
- def buildkite
39
- @buildkite ||= begin
40
- unless Buildkite.env
41
- raise 'Must be in Buildkite environment to access the Buildkite API'
42
- end
43
-
44
- Buildkite::Pipelines::Api.new(Buildkite.env.api_token)
45
- end
46
- end
47
-
48
- def pipeline_steps(*types)
49
- steps = pipeline.steps
50
- types = types.flatten
51
- steps = steps.select { |step| types.include?(step.class.to_sym) } if types.any?
52
- steps
53
- end
54
-
55
- def _log_run
56
- log.info "\nProcessing ".color(:dimgray) + self.class.name.color(:springgreen)
57
-
58
- results = benchmark('└──'.color(:springgreen) + ' Finished in %s'.color(:dimgray)) do
59
- formatter = log.formatter
60
- log.formatter = proc do |_severity, _datetime, _progname, msg|
61
- '│'.color(:springgreen) + " #{msg}\n"
62
- end
63
-
64
- begin
65
- yield
66
- ensure
67
- log.formatter = formatter
68
- end
69
- end
70
-
71
- log.info results
72
- end
73
- end
74
- end
75
- end
76
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Buildkite
4
- module Builder
5
- module Processors
6
- autoload :Abstract, File.expand_path('processors/abstract', __dir__)
7
- end
8
- end
9
- end
@@ -1,140 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'yaml'
4
- require 'pathname'
5
-
6
- module Buildkite
7
- module Pipelines
8
- class Pipeline
9
- attr_reader :steps
10
- attr_reader :plugins
11
- attr_reader :templates
12
-
13
- def initialize(definition = nil, &block)
14
- @env = {}
15
- @steps = []
16
- @plugins = {}
17
- @templates = {}
18
- @processors = []
19
- @notify = []
20
-
21
- instance_eval(&definition) if definition
22
- instance_eval(&block) if block_given?
23
- end
24
-
25
- [
26
- Steps::Block,
27
- Steps::Command,
28
- Steps::Input,
29
- Steps::Trigger,
30
- ].each do |type|
31
- define_method(type.to_sym) do |template = nil, **args, &block|
32
- add(type, template, **args, &block)
33
- end
34
- end
35
-
36
- def notify(*args)
37
- if args.empty?
38
- @notify
39
- elsif args.first.is_a?(Hash)
40
- @notify.push(args.first.transform_keys(&:to_s))
41
- else
42
- raise ArgumentError, 'value must be hash'
43
- end
44
- end
45
-
46
- def env(*args)
47
- if args.empty?
48
- @env
49
- elsif args.first.is_a?(Hash)
50
- @env.merge!(args.first.transform_keys(&:to_s))
51
- else
52
- raise ArgumentError, 'value must be hash'
53
- end
54
- end
55
-
56
- def skip(template = nil, **args, &block)
57
- step = add(Steps::Skip, template, **args, &block)
58
- # A skip step has a nil/noop command.
59
- step.command(nil)
60
- # Always set the skip attribute if it's in a falsey state.
61
- step.skip(true) if !step.get(:skip) || step.skip.empty?
62
- step
63
- end
64
-
65
- def wait(attributes = {}, &block)
66
- step = add(Steps::Wait, &block)
67
- step.wait(nil)
68
- attributes.each do |key, value|
69
- step.set(key, value)
70
- end
71
- step
72
- end
73
-
74
- def plugin(name, uri, version)
75
- name = name.to_s
76
-
77
- if plugins.key?(name)
78
- raise ArgumentError, "Plugin already defined: #{name}"
79
- end
80
-
81
- @plugins[name] = [uri, version]
82
- end
83
-
84
- def template(name, &definition)
85
- name = name.to_s
86
-
87
- if templates.key?(name)
88
- raise ArgumentError, "Template already defined: #{name}"
89
- elsif !block_given?
90
- raise ArgumentError, 'Template definition block must be given'
91
- end
92
-
93
- @templates[name.to_s] = definition
94
- end
95
-
96
- def processors(*processor_classes)
97
- unless processor_classes.empty?
98
- @processors.clear
99
-
100
- processor_classes.flatten.each do |processor|
101
- unless processor < Buildkite::Builder::Processors::Abstract
102
- raise "#{processor} must inherit from Buildkite::Builder::Processors::Abstract"
103
- end
104
-
105
- @processors << processor
106
- end
107
- end
108
-
109
- @processors
110
- end
111
-
112
- def to_h
113
- pipeline = {}
114
- pipeline[:env] = env if env.any?
115
- pipeline[:notify] = notify if notify.any?
116
- pipeline[:steps] = steps.map(&:to_h)
117
-
118
- Helpers.sanitize(pipeline)
119
- end
120
-
121
- def to_yaml
122
- YAML.dump(to_h)
123
- end
124
-
125
- private
126
-
127
- def add(step_class, template = nil, **args, &block)
128
- steps.push(step_class.new(self, find_template(template), **args, &block)).last
129
- end
130
-
131
- def find_template(name)
132
- return unless name
133
-
134
- templates[name.to_s] || begin
135
- raise ArgumentError, "Template not defined: #{name}"
136
- end
137
- end
138
- end
139
- end
140
- end
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Buildkite
4
- module Pipelines
5
- class Plugin
6
- attr_reader :uri, :version, :options
7
-
8
- def initialize(uri, version, options = nil)
9
- @uri = uri
10
- @version = version
11
- @options = options
12
- end
13
-
14
- def full_uri
15
- "#{uri}##{version}"
16
- end
17
-
18
- def to_h
19
- Helpers.sanitize(full_uri => options)
20
- end
21
- end
22
- end
23
- end
File without changes