buildkite-builder 1.3.1 → 2.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/VERSION +1 -1
  4. data/lib/buildkite/builder.rb +10 -1
  5. data/lib/buildkite/builder/commands.rb +2 -2
  6. data/lib/buildkite/builder/commands/abstract.rb +4 -4
  7. data/lib/buildkite/builder/commands/files.rb +6 -7
  8. data/lib/buildkite/builder/commands/preview.rb +1 -1
  9. data/lib/buildkite/builder/commands/run.rb +1 -1
  10. data/lib/buildkite/builder/data.rb +41 -0
  11. data/lib/buildkite/builder/dsl.rb +21 -0
  12. data/lib/buildkite/builder/extension.rb +54 -0
  13. data/lib/buildkite/builder/extension_manager.rb +55 -0
  14. data/lib/buildkite/builder/extensions.rb +12 -0
  15. data/lib/buildkite/builder/extensions/env.rb +21 -0
  16. data/lib/buildkite/builder/extensions/notify.rb +21 -0
  17. data/lib/buildkite/builder/extensions/steps.rb +60 -0
  18. data/lib/buildkite/builder/extensions/use.rb +13 -0
  19. data/lib/buildkite/builder/github.rb +1 -1
  20. data/lib/buildkite/builder/group.rb +42 -0
  21. data/lib/buildkite/builder/loaders.rb +1 -1
  22. data/lib/buildkite/builder/loaders/{processors.rb → extensions.rb} +9 -9
  23. data/lib/buildkite/builder/{context.rb → pipeline.rb} +40 -49
  24. data/lib/buildkite/builder/plugin_registry.rb +27 -0
  25. data/lib/buildkite/builder/step_collection.rb +43 -0
  26. data/lib/buildkite/builder/template_registry.rb +27 -0
  27. data/lib/buildkite/pipelines.rb +0 -1
  28. data/lib/buildkite/pipelines/attributes.rb +1 -1
  29. data/lib/buildkite/pipelines/helpers/command.rb +1 -5
  30. data/lib/buildkite/pipelines/helpers/plugins.rb +1 -1
  31. data/lib/buildkite/pipelines/step_context.rb +0 -4
  32. data/lib/buildkite/pipelines/steps/abstract.rb +4 -4
  33. metadata +19 -9
  34. data/lib/buildkite/builder/processors.rb +0 -9
  35. data/lib/buildkite/builder/processors/abstract.rb +0 -76
  36. data/lib/buildkite/pipelines/pipeline.rb +0 -140
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 669f98cc6170037177ae0c80008c0a39909cf9467d333eab77c81d97bb26d94d
4
- data.tar.gz: 2574e9e06b8140029edde690bd588f1171417810056f53fcc57225a061c8cbed
3
+ metadata.gz: 671edc2f2b7a65af8d8c8d1390485c9591fb4814c1f9d1acb49e82fd3d648c27
4
+ data.tar.gz: 78f6374f0b352172d61185ed5bb9c9054dbf20581001d634d5474ffdaf13afdb
5
5
  SHA512:
6
- metadata.gz: 64284b828cd1af1768b1354af0bddf2505c203124797117d74af8fce6d59e9342dc4668dc84a462d4569da00a6628006f773cda753c90782ff718a712c9d1413
7
- data.tar.gz: d97d496f48f63060aa769d07952c648f4ed31303c070164b8e8754ce761bba9475f60b2bcf1bc7bdd1b96e7e008658a4823f0dda56b976974b20060a8ae6249e
6
+ metadata.gz: 8be88ac275466dc0dec40db8ce78f752b488b568ed7b32403977e53905fc4fd8518298716cb1864a5bb9ab6a51ea3ed3572051a839f215c76cf7b6e6efdbf218
7
+ data.tar.gz: 013a0a8e0ea3a56671088a1ec32994d456e83f49fd64b1b8d802b4fce53f54048581d10f21db56d213a8f9057797f494627b14e7390df83bc0b19ee6aceefb19
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 1.5.0
2
+ * Merge `BuildKite::Builder::Context` and `BuildKite::Pipelines::Pipeline` to `BuildKite::Builder::Pipeline` (#37)
3
+
4
+ ## 1.4.1
5
+ * Fix the Github API Builder to account for Buildkite having both `.git` and no file exention repository URIs (#33)
6
+
7
+ ## 1.4.0
8
+ * Fix the `files` command. You now pass in the manifest with the `--manifest` CLI argument.
9
+
1
10
  ## 1.3.1
2
11
  * Expose `data` from `StepContext` to `Step`
3
12
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.1
1
+ 2.0.0.beta2
@@ -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,9 @@ 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 :StepCollection, File.expand_path('builder/step_collection', __dir__)
24
+ autoload :TemplateRegistry, File.expand_path('builder/template_registry', __dir__)
25
+ autoload :PluginRegistry, File.expand_path('builder/plugin_registry', __dir__)
17
26
 
18
27
  BUILDKITE_DIRECTORY_NAME = Pathname.new('.buildkite').freeze
19
28
 
@@ -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|
@@ -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,14 +9,13 @@ module Buildkite
9
9
  self.description = 'Outputs files that match the specified manifest.'
10
10
 
11
11
  def run
12
- pipeline, manifest = ARGV.first.to_s.split('/')
13
- if !pipeline || !manifest
14
- raise 'You must specify a pipeline and a manifest (eg "mypipeline/mymanifest")'
15
- end
12
+ manifests = Loaders::Manifests.load(pipeline_path)
13
+ puts manifests[options[:manifest]].files.sort.join("\n")
14
+ end
16
15
 
17
- manifests = Loaders::Manifests.load(pipeline)
18
- manifests[manifest].files.each do |file|
19
- puts file
16
+ def parse_options(opts)
17
+ opts.on('--manifest MANIFEST', 'The manifest to use') do |manifest|
18
+ options[:manifest] = manifest
20
19
  end
21
20
  end
22
21
  end
@@ -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
@@ -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,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,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
+ TemplateRegistry.new(context.root),
8
+ PluginRegistry.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, version)
20
+ context.data.steps.plugins.add(name, uri, version)
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
@@ -11,7 +11,7 @@ module Buildkite
11
11
  ACCEPT_HEADER = 'application/vnd.github.v3+json'
12
12
  LINK_HEADER = 'link'
13
13
  NEXT_LINK_REGEX = /<(?<uri>.+)>; rel="next"/.freeze
14
- REPO_REGEX = /github\.com(?::|\/)(.*)\.git\z/.freeze
14
+ REPO_REGEX = /github\.com(?::|\/)(.*?)(?:\.git)?\z/.freeze
15
15
  PER_PAGE = 100
16
16
 
17
17
  def self.pull_request_files
@@ -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
@@ -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
@@ -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
@@ -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,27 @@
1
+ module Buildkite
2
+ module Builder
3
+ class PluginRegistry
4
+ def initialize
5
+ @plugins = {}
6
+ end
7
+
8
+ def add(name, uri, version)
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, version]
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 TemplateRegistry
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
@@ -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__)
@@ -84,7 +84,7 @@ module Buildkite
84
84
  end
85
85
 
86
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
87
+ # set the attribute if it hasn't been already set. It will return true/false
88
88
  # for whether or not the value was set.
89
89
  define_method("#{method_name}?") do |*args|
90
90
  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
@@ -12,7 +12,7 @@ module Buildkite
12
12
  raise ArgumentError, "Plugin already used for command step: #{plugin_name}"
13
13
  end
14
14
 
15
- uri, version = pipeline.plugins.fetch(plugin_name)
15
+ uri, version = step_collection.plugins.fetch(plugin_name)
16
16
  new_plugin = Plugin.new(uri, version, options)
17
17
  @plugins[plugin_name] = new_plugin
18
18
 
@@ -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
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.3.1
4
+ version: 2.0.0.beta2
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-03-02 00:00:00.000000000 Z
12
+ date: 2021-08-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sorted_set
@@ -133,21 +133,32 @@ 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.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
138
146
  - lib/buildkite/builder/file_resolver.rb
139
147
  - lib/buildkite/builder/github.rb
148
+ - lib/buildkite/builder/group.rb
140
149
  - lib/buildkite/builder/loaders.rb
141
150
  - lib/buildkite/builder/loaders/abstract.rb
151
+ - lib/buildkite/builder/loaders/extensions.rb
142
152
  - lib/buildkite/builder/loaders/manifests.rb
143
- - lib/buildkite/builder/loaders/processors.rb
144
153
  - lib/buildkite/builder/loaders/templates.rb
145
154
  - lib/buildkite/builder/logging_utils.rb
146
155
  - lib/buildkite/builder/manifest.rb
147
156
  - lib/buildkite/builder/manifest/rule.rb
148
- - lib/buildkite/builder/processors.rb
149
- - lib/buildkite/builder/processors/abstract.rb
157
+ - lib/buildkite/builder/pipeline.rb
158
+ - lib/buildkite/builder/plugin_registry.rb
150
159
  - lib/buildkite/builder/rainbow.rb
160
+ - lib/buildkite/builder/step_collection.rb
161
+ - lib/buildkite/builder/template_registry.rb
151
162
  - lib/buildkite/env.rb
152
163
  - lib/buildkite/pipelines.rb
153
164
  - lib/buildkite/pipelines/api.rb
@@ -164,7 +175,6 @@ files:
164
175
  - lib/buildkite/pipelines/helpers/skip.rb
165
176
  - lib/buildkite/pipelines/helpers/soft_fail.rb
166
177
  - lib/buildkite/pipelines/helpers/timeout_in_minutes.rb
167
- - lib/buildkite/pipelines/pipeline.rb
168
178
  - lib/buildkite/pipelines/plugin.rb
169
179
  - lib/buildkite/pipelines/step_context.rb
170
180
  - lib/buildkite/pipelines/steps.rb
@@ -194,9 +204,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
194
204
  version: 2.3.0
195
205
  required_rubygems_version: !ruby/object:Gem::Requirement
196
206
  requirements:
197
- - - ">="
207
+ - - ">"
198
208
  - !ruby/object:Gem::Version
199
- version: '0'
209
+ version: 1.3.1
200
210
  requirements: []
201
211
  rubygems_version: 3.2.2
202
212
  signing_key:
@@ -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,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,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