buildkite-builder 2.4.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4c4c2623f11f578f2839830abd97f02de9189aca48838b51af749a2ed63b5d43
4
- data.tar.gz: 80ad9118885ca08b62b9d849f5bde06d60ab8ed13095921a4bf017c1b23754f1
3
+ metadata.gz: bef719c816c5d64756bbcd041ca2a3fabe8fe59bff47cc154948207ce64a672f
4
+ data.tar.gz: f25762b1422e540f395526df9d6082e1d5908ea0b9b290bf025a365b56c317ff
5
5
  SHA512:
6
- metadata.gz: faf4b6d95c5ce06432dec04ddc53df0b6ca4a0e8e0986fe5798b02678513847f1bc1dc6cfa4e3a81d603d0aad5db55e6b7f7ece5f21801fb92e52ae8e6895d68
7
- data.tar.gz: 8c134596ebac502f43c0da347dbe36f5db527534b50781623cd3ea0b2e69a8ff6ecb9bfc1cd9ec697b616bcfe7c3804bc728a9c6a70d5c71aecfc4d772fa9931
6
+ metadata.gz: c00f3fba91b34cf907c99ff839612270ef8aecb754c988af52226e979d5e743a508963d1d06d9957b529a2e3ba47cf6ba0951ec33290b8581dc9937b4178033a
7
+ data.tar.gz: 6e62bff8ecdb50b72bdb3afb8fd6fa1385f17ef1852b607f5273570cab1d396ee65d8908a78604d48ccb7a564ad4cb0f9dbff6b94b7a016cfd9ee881e1d4098a
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 3.1.0
2
+ * Add subpipeline support to save triggered pipeline's YML definition beforehand to artifacts and pass down the file to an ENV for pipeline setup.
3
+
4
+ ## 3.0.0
5
+ * Remove manifest features to prevent Github API dependency and simplify the gem to focus on Buildkite features.
6
+
7
+ ## 2.4.1
8
+ * Fix pipeline upload as artifact logic.
9
+
1
10
  ## 2.4.0
2
11
  * Upload custom pipeline artifacts in a single command.
3
12
  * Only upload the pipeline as an artifact when the pipeline upload fails.
data/README.md CHANGED
@@ -48,7 +48,7 @@ At its core, BKB is really just a YAML builder. This tool allows you to scale yo
48
48
  - Perform pre-build code/diff analysis to determine whether or not to to add a step to the pipeline.
49
49
  - Reorder pipeline steps dynamically.
50
50
  - Augment your pipeline steps with BKB processors.
51
-
51
+
52
52
  ### Pipeline Files
53
53
 
54
54
  Your repo can contain as many pipeline definitions as you'd like. By convention, pipeline file structure are as such:
@@ -74,9 +74,9 @@ Buildkite::Builder.pipeline do
74
74
  label "Rspec", emoji: :rspec
75
75
  command "bundle exec rspec"
76
76
  end
77
-
77
+
78
78
  wait
79
-
79
+
80
80
  trigger do
81
81
  trigger "deploy-pipeline"
82
82
  end
@@ -127,7 +127,7 @@ You can then include the template into the the pipeline once or as many time as
127
127
  ```ruby
128
128
  Buildkite::Builder.pipeline do
129
129
  command(:rspec)
130
-
130
+
131
131
  # Reuse and agument templates on the fly.
132
132
  command(:rspec) do
133
133
  label "Run RSpec again!"
@@ -135,6 +135,36 @@ Buildkite::Builder.pipeline do
135
135
  end
136
136
  ```
137
137
 
138
+ ### Subpipeline
139
+
140
+ While triggering another pipeline, you can predefine subpipeline's steps using `pipeline(NAME_OF_SUBPIPELINE)` in the main pipeline's `pipeline.rb` file and share with main pipeline's plugins and templates definition.
141
+
142
+ `.buildkite/pipelines/pipeline-triggerer/pipeline.rb`
143
+
144
+ ```ruby
145
+ Buildkite::Builder.pipeline do
146
+ pipeline('rspec-pipeline') do
147
+ command do
148
+ label "Run RSpec in separate pipeline"
149
+ end
150
+ end
151
+ end
152
+ ```
153
+
154
+ Inside your Buildkite pipeline setup, you can do the following:
155
+
156
+ In `https://buildkite.com/your-org/rspec-pipeline/steps`
157
+
158
+ ```yaml
159
+ steps:
160
+ - label: ":pipeline:"
161
+ commands:
162
+ - buildkite-agent artifact download $BKB_SUBPIPELINE_FILE . --build $BUILDKITE_TRIGGERED_FROM_BUILD_ID
163
+ - buildkite-agent pipeline upload $BKB_SUBPIPELINE_FILE
164
+ ```
165
+
166
+ This will upload the pregenerated `pipeline.yml` to `rspec-pipeline`.
167
+
138
168
  ## Development
139
169
 
140
170
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.4.0
1
+ 3.1.0
@@ -5,7 +5,6 @@ module Buildkite
5
5
  module Commands
6
6
  using Rainbow
7
7
  COMMANDS = {
8
- 'files' => :Files,
9
8
  'preview' => :Preview,
10
9
  'run' => :Run
11
10
  }.freeze
@@ -9,7 +9,7 @@ module Buildkite
9
9
  @data.each_with_object({}) do |(key, value), hash|
10
10
  value = value.respond_to?(:to_definition) ? value.to_definition : value
11
11
 
12
- next if value.empty?
12
+ next if value.nil? || value.empty?
13
13
 
14
14
  hash[key] = value
15
15
  end
@@ -0,0 +1,96 @@
1
+ require 'securerandom'
2
+
3
+ module Buildkite
4
+ module Builder
5
+ module Extensions
6
+ class SubPipelines < Extension
7
+ class Pipeline
8
+ include Buildkite::Pipelines::Attributes
9
+
10
+ attr_reader :data, :name
11
+
12
+ attribute :depends_on, append: true
13
+ attribute :key
14
+
15
+ def self.to_sym
16
+ name.split('::').last.downcase.to_sym
17
+ end
18
+
19
+ def initialize(name, steps, &block)
20
+ @name = name
21
+ @data = Data.new
22
+ @data.steps = StepCollection.new(
23
+ steps.templates,
24
+ steps.plugins
25
+ )
26
+ @data.notify = []
27
+ @data.env = {}
28
+
29
+ @dsl = Dsl.new(self)
30
+ @dsl.extend(Extensions::Steps)
31
+ @dsl.extend(Extensions::Notify)
32
+ @dsl.extend(Extensions::Env)
33
+ instance_eval(&block) if block_given?
34
+ self
35
+ end
36
+
37
+ def to_h
38
+ attributes = super
39
+ attributes.merge(data.to_definition)
40
+ end
41
+
42
+ def method_missing(method_name, *args, **kwargs, &_block)
43
+ @dsl.public_send(method_name, *args, **kwargs, &_block)
44
+ end
45
+
46
+ def pipeline_yml
47
+ @pipeline_yml ||= "tmp/buildkite-builder/#{SecureRandom.urlsafe_base64}.yml"
48
+ end
49
+ end
50
+
51
+ def prepare
52
+ context.data.pipelines = PipelineCollection.new(context.artifacts)
53
+ end
54
+
55
+ dsl do
56
+ def pipeline(name, template = nil, &block)
57
+ raise "Subpipeline must have a name" if name.empty?
58
+ raise "Subpipeline does not allow nested in another Subpipeline" if context.is_a?(Buildkite::Builder::Extensions::SubPipelines::Pipeline)
59
+ sub_pipeline = Buildkite::Builder::Extensions::SubPipelines::Pipeline.new(name, context.data.steps, &block)
60
+
61
+ context.data.pipelines.add(sub_pipeline)
62
+
63
+ if template
64
+ # Use predefined template
65
+ step = context.data.steps.add(Pipelines::Steps::Trigger, template)
66
+
67
+ if step.build.nil?
68
+ step.build(env: { BKB_SUBPIPELINE_FILE: sub_pipeline.pipeline_yml })
69
+ else
70
+ step.build[:env].merge!(BKB_SUBPIPELINE_FILE: sub_pipeline.pipeline_yml)
71
+ end
72
+ else
73
+ # Generic trigger step
74
+ context.data.steps.add(Pipelines::Steps::Trigger, key: "subpipeline_#{name}_#{context.data.pipelines.count}") do |context|
75
+ key context[:key]
76
+ label name.capitalize
77
+ trigger name
78
+ build(
79
+ message: '${BUILDKITE_MESSAGE}',
80
+ commit: '${BUILDKITE_COMMIT}',
81
+ branch: '${BUILDKITE_BRANCH}',
82
+ env: {
83
+ BUILDKITE_PULL_REQUEST: '${BUILDKITE_PULL_REQUEST}',
84
+ BUILDKITE_PULL_REQUEST_BASE_BRANCH: '${BUILDKITE_PULL_REQUEST_BASE_BRANCH}',
85
+ BUILDKITE_PULL_REQUEST_REPO: '${BUILDKITE_PULL_REQUEST_REPO}',
86
+ BKB_SUBPIPELINE_FILE: sub_pipeline.pipeline_yml
87
+ }
88
+ )
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -6,6 +6,7 @@ module Buildkite
6
6
  autoload :Env, File.expand_path('extensions/env', __dir__)
7
7
  autoload :Lib, File.expand_path('extensions/lib', __dir__)
8
8
  autoload :Notify, File.expand_path('extensions/notify', __dir__)
9
+ autoload :SubPipelines, File.expand_path('extensions/sub_pipelines', __dir__)
9
10
  autoload :Steps, File.expand_path('extensions/steps', __dir__)
10
11
  autoload :Use, File.expand_path('extensions/use', __dir__)
11
12
  end
@@ -4,7 +4,6 @@ module Buildkite
4
4
  module Builder
5
5
  module Loaders
6
6
  autoload :Abstract, File.expand_path('loaders/abstract', __dir__)
7
- autoload :Manifests, File.expand_path('loaders/manifests', __dir__)
8
7
  autoload :Templates, File.expand_path('loaders/templates', __dir__)
9
8
  autoload :Extensions, File.expand_path('loaders/extensions', __dir__)
10
9
  end
@@ -37,7 +37,7 @@ module Buildkite
37
37
  use(Extensions::Env)
38
38
  use(Extensions::Notify)
39
39
  use(Extensions::Steps)
40
- load_manifests
40
+ use(Extensions::SubPipelines)
41
41
  end
42
42
 
43
43
  def upload
@@ -52,9 +52,10 @@ module Buildkite
52
52
  file.write(contents)
53
53
 
54
54
  logger.info "+++ :pipeline: Uploading pipeline"
55
- unless Buildkite::Pipelines::Command.pipeline!(:upload, file.path)
55
+ unless Buildkite::Pipelines::Command.pipeline(:upload, file.path)
56
56
  logger.info "Pipeline upload failed, saving as artifact…"
57
57
  Buildkite::Pipelines::Command.artifact!(:upload, file.path)
58
+ abort
58
59
  end
59
60
  logger.info "+++ :toolbox: Setting job meta-data to #{Buildkite.env.job_id.color(:yellow)}"
60
61
  Buildkite::Pipelines::Command.meta_data!(:set, Builder::META_DATA.fetch(:job), Buildkite.env.job_id)
@@ -81,12 +82,6 @@ module Buildkite
81
82
 
82
83
  attr_reader :extensions
83
84
 
84
- def load_manifests
85
- Loaders::Manifests.load(root).each do |name, asset|
86
- Manifest[name] = asset
87
- end
88
- end
89
-
90
85
  def upload_artifacts
91
86
  return if artifacts.empty?
92
87
 
@@ -0,0 +1,39 @@
1
+ require "forwardable"
2
+
3
+ module Buildkite
4
+ module Builder
5
+ class PipelineCollection
6
+ extend Forwardable
7
+
8
+ attr_reader :pipelines
9
+
10
+ def_delegator :@pipelines, :count
11
+
12
+ def initialize(artifacts)
13
+ @artifacts = artifacts
14
+ @pipelines = []
15
+ end
16
+
17
+ def add(pipeline)
18
+ unless pipeline.is_a?(Buildkite::Builder::Extensions::SubPipelines::Pipeline)
19
+ raise "`#{pipeline}` must be a Buildkite::Builder::Extensions::SubPipelines::Pipeline"
20
+ end
21
+
22
+ pipelines << pipeline
23
+ end
24
+
25
+ def to_definition
26
+ # Instead of generates pipeline.yml, subpipelines save generated file to artifacts
27
+ pipelines.each do |pipeline|
28
+ file = Pathname.new(pipeline.pipeline_yml)
29
+ file.dirname.mkpath
30
+ file.write(YAML.dump(Pipelines::Helpers.sanitize(pipeline.to_h)))
31
+
32
+ @artifacts << file
33
+ end
34
+
35
+ nil
36
+ end
37
+ end
38
+ end
39
+ end
@@ -13,16 +13,14 @@ module Buildkite
13
13
  autoload :Extension, File.expand_path('builder/extension', __dir__)
14
14
  autoload :ExtensionManager, File.expand_path('builder/extension_manager', __dir__)
15
15
  autoload :Extensions, File.expand_path('builder/extensions', __dir__)
16
- autoload :FileResolver, File.expand_path('builder/file_resolver', __dir__)
17
- autoload :Github, File.expand_path('builder/github', __dir__)
18
16
  autoload :Loaders, File.expand_path('builder/loaders', __dir__)
19
17
  autoload :LoggingUtils, File.expand_path('builder/logging_utils', __dir__)
20
- autoload :Manifest, File.expand_path('builder/manifest', __dir__)
21
18
  autoload :Processors, File.expand_path('builder/processors', __dir__)
22
19
  autoload :Rainbow, File.expand_path('builder/rainbow', __dir__)
23
20
  autoload :Plugin, File.expand_path('builder/plugin', __dir__)
24
21
  autoload :PluginCollection, File.expand_path('builder/plugin_collection', __dir__)
25
22
  autoload :StepCollection, File.expand_path('builder/step_collection', __dir__)
23
+ autoload :PipelineCollection, File.expand_path('builder/pipeline_collection', __dir__)
26
24
  autoload :TemplateManager, File.expand_path('builder/template_manager', __dir__)
27
25
  autoload :PluginManager, File.expand_path('builder/plugin_manager', __dir__)
28
26
 
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: 2.4.0
4
+ version: 3.1.0
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: 2022-01-08 00:00:00.000000000 Z
12
+ date: 2022-06-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rainbow
@@ -116,7 +116,6 @@ files:
116
116
  - lib/buildkite/builder.rb
117
117
  - lib/buildkite/builder/commands.rb
118
118
  - lib/buildkite/builder/commands/abstract.rb
119
- - lib/buildkite/builder/commands/files.rb
120
119
  - lib/buildkite/builder/commands/preview.rb
121
120
  - lib/buildkite/builder/commands/run.rb
122
121
  - lib/buildkite/builder/data.rb
@@ -129,19 +128,16 @@ files:
129
128
  - lib/buildkite/builder/extensions/lib.rb
130
129
  - lib/buildkite/builder/extensions/notify.rb
131
130
  - lib/buildkite/builder/extensions/steps.rb
131
+ - lib/buildkite/builder/extensions/sub_pipelines.rb
132
132
  - lib/buildkite/builder/extensions/use.rb
133
- - lib/buildkite/builder/file_resolver.rb
134
- - lib/buildkite/builder/github.rb
135
133
  - lib/buildkite/builder/group.rb
136
134
  - lib/buildkite/builder/loaders.rb
137
135
  - lib/buildkite/builder/loaders/abstract.rb
138
136
  - lib/buildkite/builder/loaders/extensions.rb
139
- - lib/buildkite/builder/loaders/manifests.rb
140
137
  - lib/buildkite/builder/loaders/templates.rb
141
138
  - lib/buildkite/builder/logging_utils.rb
142
- - lib/buildkite/builder/manifest.rb
143
- - lib/buildkite/builder/manifest/rule.rb
144
139
  - lib/buildkite/builder/pipeline.rb
140
+ - lib/buildkite/builder/pipeline_collection.rb
145
141
  - lib/buildkite/builder/plugin.rb
146
142
  - lib/buildkite/builder/plugin_collection.rb
147
143
  - lib/buildkite/builder/plugin_manager.rb
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Buildkite
4
- module Builder
5
- module Commands
6
- class Files < Abstract
7
- private
8
-
9
- self.description = 'Outputs files that match the specified manifest.'
10
-
11
- def run
12
- manifests = Loaders::Manifests.load(pipeline_path)
13
- puts manifests[options[:manifest]].files.sort.join("\n")
14
- end
15
-
16
- def parse_options(opts)
17
- opts.on('--manifest MANIFEST', 'The manifest to use') do |manifest|
18
- options[:manifest] = manifest
19
- end
20
- end
21
- end
22
- end
23
- end
24
- end
@@ -1,59 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'open3'
4
- require 'set'
5
-
6
- module Buildkite
7
- module Builder
8
- class FileResolver
9
- @cache = true
10
-
11
- attr_reader :modified_files
12
-
13
- class << self
14
- attr_accessor :cache
15
-
16
- def resolve(reset = false)
17
- @resolve = nil if !cache || reset
18
- @resolve ||= new
19
- end
20
- end
21
-
22
- def initialize
23
- @modified_files = Set.new(pull_request? ? files_from_pull_request.sort! : files_from_git.sort!)
24
- end
25
-
26
- private
27
-
28
- def files_from_pull_request
29
- Github.pull_request_files.map { |f| f.fetch('filename') }
30
- end
31
-
32
- def files_from_git
33
- if Buildkite.env
34
- changed_files = command("git diff-tree --no-commit-id --name-only -r #{Buildkite.env.commit}")
35
- else
36
- default_branch = command('git symbolic-ref refs/remotes/origin/HEAD').strip
37
- changed_files = command("git diff --name-only #{default_branch}")
38
- changed_files << command('git diff --name-only')
39
- end
40
-
41
- changed_files.split.uniq.sort
42
- end
43
-
44
- def pull_request?
45
- Buildkite.env&.pull_request
46
- end
47
-
48
- def command(cmd)
49
- output, status = Open3.capture2(*cmd.split)
50
-
51
- if status.success?
52
- output
53
- else
54
- raise "Command failed (exit #{status.exitstatus}): #{cmd}"
55
- end
56
- end
57
- end
58
- end
59
- end
@@ -1,71 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
- require 'net/http'
5
- require 'uri'
6
-
7
- module Buildkite
8
- module Builder
9
- class Github
10
- BASE_URI = URI('https://api.github.com').freeze
11
- ACCEPT_HEADER = 'application/vnd.github.v3+json'
12
- LINK_HEADER = 'link'
13
- NEXT_LINK_REGEX = /<(?<uri>.+)>; rel="next"/.freeze
14
- REPO_REGEX = /github\.com(?::|\/)(.*?)(?:\.git)?\z/.freeze
15
- PER_PAGE = 100
16
-
17
- def self.pull_request_files
18
- new.pull_request_files
19
- end
20
-
21
- def initialize(env = ENV)
22
- @env = env
23
- end
24
-
25
- def pull_request_files
26
- files = []
27
- next_uri = URI.join(BASE_URI, "repos/#{repo}/pulls/#{pull_request_number}/files?per_page=#{PER_PAGE}")
28
-
29
- while next_uri
30
- response = request(next_uri)
31
- files.concat(JSON.parse(response.body))
32
- next_uri = parse_next_uri(response)
33
- end
34
-
35
- files
36
- end
37
-
38
- private
39
-
40
- def repo
41
- Buildkite.env.repo[REPO_REGEX, 1]
42
- end
43
-
44
- def token
45
- @env.fetch('GITHUB_API_TOKEN')
46
- end
47
-
48
- def pull_request_number
49
- Buildkite.env.pull_request
50
- end
51
-
52
- def request(uri)
53
- Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
54
- request = Net::HTTP::Get.new(uri)
55
- request['Authorization'] = "token #{token}"
56
- request['Accept'] = ACCEPT_HEADER
57
-
58
- http.request(request)
59
- end
60
- end
61
-
62
- def parse_next_uri(response)
63
- links = response[LINK_HEADER]
64
- return unless links
65
-
66
- matches = links.match(NEXT_LINK_REGEX)
67
- URI.parse(matches[:uri]) if matches
68
- end
69
- end
70
- end
71
- end
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Buildkite
4
- module Builder
5
- module Loaders
6
- class Manifests < Abstract
7
- MANIFESTS_PATH = Pathname.new('manifests').freeze
8
-
9
- def load
10
- return unless manifests_path.directory?
11
-
12
- manifests_path.children.map do |file|
13
- add(file.basename, Manifest.new(Buildkite::Builder.root, file.readlines))
14
- end
15
- end
16
-
17
- def manifests_path
18
- root.join(MANIFESTS_PATH)
19
- end
20
- end
21
- end
22
- end
23
- end
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'set'
4
- require 'pathname'
5
-
6
- module Buildkite
7
- module Builder
8
- class Manifest::Rule
9
- GLOB_OPTIONS = File::FNM_PATHNAME | File::FNM_DOTMATCH | File::FNM_EXTGLOB
10
-
11
- attr_reader :exclude
12
- attr_reader :glob
13
-
14
- def initialize(root, pattern)
15
- @root = Pathname.new(root)
16
- @exclude = false
17
- @glob = @root
18
-
19
- if pattern[0] == '!'
20
- @exclude = true
21
- pattern = pattern[1..-1]
22
- end
23
-
24
- if pattern.start_with?('/')
25
- pattern = pattern[1..-1]
26
- else
27
- @glob = @glob.join('**')
28
- end
29
-
30
- @glob = @glob.join(pattern).to_s
31
- end
32
-
33
- def files
34
- @files ||= begin
35
- matched = Dir.glob(glob, GLOB_OPTIONS)
36
- matched.map! { |file| Pathname.new(file) }
37
- matched.reject!(&:directory?)
38
- matched.map! { |file| file.relative_path_from(Builder.root) }
39
- Set.new(matched.sort!)
40
- end
41
- end
42
-
43
- def match?(file)
44
- file = Pathname.new(file)
45
- file = @root.join(file) unless file.absolute?
46
-
47
- File.fnmatch?(glob, file.to_s, GLOB_OPTIONS)
48
- end
49
- end
50
- end
51
- end
@@ -1,88 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'digest/md5'
4
- require 'pathname'
5
-
6
- module Buildkite
7
- module Builder
8
- class Manifest
9
- autoload :Rule, File.expand_path('manifest/rule', __dir__)
10
-
11
- class << self
12
- def resolve(root, patterns)
13
- new(root, Array(patterns)).modified?
14
- end
15
-
16
- def manifests
17
- @manifests ||= {}
18
- end
19
-
20
- def [](name)
21
- manifests[name.to_s]
22
- end
23
-
24
- def []=(name, manifest)
25
- name = name.to_s
26
- if manifests.key?(name)
27
- raise ArgumentError, "manifest #{name} already exists"
28
- end
29
-
30
- manifests[name] = manifest
31
- end
32
- end
33
-
34
- attr_reader :root
35
-
36
- def initialize(root, patterns)
37
- @root = Pathname.new(root)
38
- @root = Buildkite::Builder.root.join(@root) unless @root.absolute?
39
- @patterns = patterns.map(&:to_s)
40
- end
41
-
42
- def modified?
43
- # DO NOT intersect FileResolver with manifest files. If the manifest is
44
- # large, the operation can be expensive. It's always cheaper to loop
45
- # through the changed files and compare them against the rules.
46
- unless defined?(@modified)
47
- @modified = FileResolver.resolve.modified_files.any? do |file|
48
- file = Buildkite::Builder.root.join(file)
49
- inclusion_rules.any? { |rule| rule.match?(file) } &&
50
- exclusion_rules.none? { |rule| rule.match?(file) }
51
- end
52
- end
53
-
54
- @modified
55
- end
56
-
57
- def files
58
- @files ||= (inclusion_rules.map(&:files).reduce(Set.new, :merge) - exclusion_rules.map(&:files).reduce(Set.new, :merge)).sort.to_set
59
- end
60
-
61
- def digest
62
- @digest ||= begin
63
- digests = files.map { |file| Digest::MD5.file(Buildkite::Builder.root.join(file)).hexdigest }
64
- Digest::MD5.hexdigest(digests.join)
65
- end
66
- end
67
-
68
- private
69
-
70
- def rules
71
- @rules ||= @patterns.each_with_object([]) do |pattern, rules|
72
- pattern = pattern.strip
73
- unless pattern.match?(/\A(#|\z)/)
74
- rules << Rule.new(root, pattern)
75
- end
76
- end
77
- end
78
-
79
- def inclusion_rules
80
- @inclusion_rules ||= rules.reject(&:exclude)
81
- end
82
-
83
- def exclusion_rules
84
- @exclusion_rules ||= rules.select(&:exclude)
85
- end
86
- end
87
- end
88
- end