rspec-pipeline 0.4.0 → 0.4.1

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: '0994ec575ad37311fc3cef61fedf84e2eced1e7d5b7069717b387838c0f804de'
4
- data.tar.gz: 003f757294cc81f56042d4efd31d0c765d5fe040a8db32b4cd785fee6c401def
3
+ metadata.gz: f14148b4cb5d701e8e2807e9022233f137b98d215f4f4653a0c4c1f4dc79b2fb
4
+ data.tar.gz: 7eb882d5a10c2b37f7385cde684ace19dcf2d43adca80cdca001601592fa3ee7
5
5
  SHA512:
6
- metadata.gz: cfe45a6df025111cbacd21d6b63c7540f3df06900ed04e0085b4c97df57f52ffd920b0defceedbb5519bf523bcc6b11d9c8868804ba60c1c4da7040dd3359d3a
7
- data.tar.gz: 2851300fcb763ade4649d1f8546ab4df971795b3c5d3d60540bfee6a30b057be7685b211107d699ea676822c84ca37cadf4292d66be4632baa7d3b434894d4ab
6
+ metadata.gz: b3bae63d085cdb5324eb9f98e1b8f172f76f75acfd859aff88e54b88798f0a529f13fa3a8c03f88fad62a396a66f43428efed5b0ed0b24eb0bb1560d8889ac9d
7
+ data.tar.gz: 82a39c6fb1001234c702da8ae800e91e3fb18d3e9218b24d9e30ae63313d87452fa57fc2167418aed08dffbdd884d8a3db313ea4dedb5d219042d5913a2fff52
data/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # CHANGELOG
2
+
3
+ ## [0.1.0] - 2021-10-18
4
+
5
+ - Initial release
6
+ - Verify stage and job dependecy
7
+ - Verify when something is limited to run from a specific branch
8
+ - Verify when a step is limited to run when a previous step failed or got succeeded
9
+
10
+ ## [0.2.0] - 2021-10-30
11
+ - Evaluate and render local templates
12
+ - Validate template parameters
13
+
14
+ ## [0.3.0] - 2120-10-31
15
+
16
+ - Evaluate and render remote templates by fetching remote repositories using git
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Thiago Marinho
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # RSpec tests for your pipelines
2
+
3
+ Create tests for your pipelines to validate dependencies between blocks and other characteristics.
4
+
5
+ > Only Azure YAML Pipelines are supported at the moment.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'rspec-pipeline'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ ```shell
18
+ $ bundle install
19
+ ```
20
+
21
+ Or install it yourself as:
22
+
23
+ ```shell
24
+ $ gem install rspec-pipeline
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ Add this at the beginning of your spec_helper.rb:
30
+
31
+ ```ruby
32
+ require 'rspec-pipeline'
33
+ ```
34
+
35
+ When writing your test you can use the keywords `pipeline`, `stage`, `job` and `step` as example groups, and the example's `subject` will be the corresponding item, filtered by its description. For example:
36
+
37
+ ```ruby
38
+ pipeline 'pipeline.yml' do
39
+ stage 'DeployToDev' do
40
+ it { is_expected.to run_after('Build') }
41
+ end
42
+ end
43
+ ```
44
+
45
+ The test will pass if your pipeline is defined like this:
46
+
47
+ ```yaml
48
+ stages:
49
+ - stage: Build
50
+ jobs:
51
+ - job: Build
52
+ steps:
53
+ - bash: echo "Build"
54
+
55
+ - stage: DeployToDev
56
+ dependsOn: Build
57
+ jobs:
58
+ - job: B1
59
+ steps:
60
+ - bash: echo "Deploy to Dev"
61
+ ```
62
+
63
+ Please go to the `examples` folder to see a few more use cases.
64
+
65
+ ### About remote templates
66
+
67
+ Note that if you try to use a template from a remote git repository you need to have clone permission and your git credentials configured if it is a private repository.
68
+
69
+ ## Development
70
+
71
+ 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.
72
+
73
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
74
+
75
+ ## Contributing
76
+
77
+ Bug reports and pull requests are welcome on GitHub at https://github.com/thiagomarinho/rspec-pipeline.
78
+
79
+ ## License
80
+
81
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'rspec-pipeline'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ shared_context 'pipeline context', type: :pipeline do
4
+ let!(:pipeline_name) { self.class.top_level_description }
5
+ end
6
+
7
+ shared_context 'stage context', type: :stage do
8
+ subject { stage(RSpec.current_example.metadata[:example_group][:description]) }
9
+ end
10
+
11
+ shared_context 'job context', type: :job do
12
+ subject do
13
+ job(RSpec.current_example.metadata[:example_group][:parent_example_group][:description],
14
+ RSpec.current_example.metadata[:example_group][:description])
15
+ end
16
+ end
17
+
18
+ shared_context 'step context', type: :step do
19
+ subject do
20
+ step(RSpec.current_example.metadata[:example_group][:parent_example_group][:parent_example_group][:description],
21
+ RSpec.current_example.metadata[:example_group][:parent_example_group][:description],
22
+ RSpec.current_example.metadata[:example_group][:description])
23
+ end
24
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+
5
+ module RSpec::Pipeline # rubocop:disable Style/ClassAndModuleChildren
6
+ # This class configures and loads fixtures. It this moment, the only fixtures we have are repositories usually
7
+ # utilized for remote templates.
8
+ class FixtureLoader
9
+ def self.load(verbose: true)
10
+ ['spec', File.join('spec', 'fixtures')].each { |dir| safe_mkdir(dir, verbose: verbose) }
11
+
12
+ RSpec.configuration.fixtures_repos.each do |repo_definition|
13
+ if repo_definition[:path]
14
+ safe_cp_r(repo_definition[:path], File.join('spec', 'fixtures', repo_definition[:name]), verbose: verbose)
15
+ elsif repo_definition[:repo]
16
+ safe_git_clone(repo_definition, verbose: verbose)
17
+ else
18
+ warn "!! invalid fixture repository definition: #{repo_definition}"
19
+ end
20
+ end
21
+ end
22
+
23
+ def self.safe_mkdir(dir, verbose: true)
24
+ if File.exist? dir
25
+ warn "!! #{dir} already exists and is not a directory" unless File.directory? dir
26
+ else
27
+ begin
28
+ FileUtils.mkdir dir
29
+ rescue Errno::EEXIST => e
30
+ raise e unless File.directory? dir
31
+ end
32
+
33
+ puts " + #{dir}/" if verbose
34
+ end
35
+ end
36
+
37
+ def self.safe_cp_r(src, dest, verbose: true) # rubocop:disable Metrics/MethodLength
38
+ if File.exist? dest
39
+ warn "!! #{dest} already exists and is not a directory" unless File.directory? dest
40
+ elsif !File.exist? src
41
+ warn "!! #{src} not found"
42
+ else
43
+ begin
44
+ FileUtils.cp_r src, dest
45
+ rescue Errno::EEXIST => e
46
+ raise e unless File.directory? dest
47
+ end
48
+
49
+ puts " + #{dest}/" if verbose
50
+ end
51
+ end
52
+
53
+ def self.safe_git_clone(repo_definition, verbose: true) # rubocop:disable Metrics/MethodLength
54
+ dest = File.join('spec', 'fixtures', repo_definition[:name])
55
+
56
+ if File.exist? dest
57
+ if File.directory? dest
58
+ if File.exist? "#{dest}/.git"
59
+ git_pull_and_checkout(repo_definition, dest)
60
+ else
61
+ warn "!! #{dest} already exists and is not a git repo"
62
+ end
63
+ else
64
+ warn "!! #{dest} already exists and is not a directory"
65
+ end
66
+ else
67
+ `git clone #{repo_definition[:repo]} #{dest}`
68
+
69
+ puts " + #{dest}/" if verbose
70
+ end
71
+ end
72
+
73
+ def self.git_pull_and_checkout(repo_definition, dest)
74
+ `git -C #{dest} pull`
75
+
76
+ ref_to_checkout = (repo_definition[:ref] || 'main')
77
+ `git -C #{dest} checkout #{ref_to_checkout}`
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,2 @@
1
+ # frozen_string_literal: true
2
+ # TODO
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'pipeline_loader'
4
+ require_relative 'template_loader'
5
+
6
+ def pipeline
7
+ pipeline = RSpec::Pipeline::PipelineLoader.new(pipeline_name)
8
+
9
+ pipeline.load
10
+ end
11
+
12
+ def stage(name)
13
+ stage = pipeline['stages'].find { |item| item['stage'] == name }
14
+
15
+ raise "Stage not found: #{name}" if stage.nil?
16
+
17
+ stage['blockType'] = 'Stage'
18
+ stage['blockName'] = stage['stage']
19
+
20
+ stage
21
+ end
22
+
23
+ def job(stage_name, name)
24
+ job = stage(stage_name)['jobs'].find { |item| item['job'] == name }
25
+
26
+ raise "Job not found: #{name}" if job.nil?
27
+
28
+ job['blockType'] = 'Job'
29
+ job['blockName'] = job['job']
30
+
31
+ job
32
+ end
33
+
34
+ def step(stage_name, job_name, name)
35
+ job_to_search = job(stage_name, job_name)
36
+ raise "Job #{job_name} doesn't have steps" unless job_to_search['steps']
37
+
38
+ step = job_to_search['steps'].find { |item| item['name'] == name }
39
+ raise "Step not found: #{name} (available: #{job_to_search['steps'].map { |item| item['name'] }})" if step.nil?
40
+
41
+ step['blockType'] = 'Step'
42
+ step['blockName'] = step['name']
43
+
44
+ step
45
+ end
46
+
47
+ # events
48
+ def push_to_branch(name)
49
+ {
50
+ 'type' => :push,
51
+ 'branch' => name,
52
+ 'condition' => "eq(variables['build.sourceBranch'], 'refs/heads/#{name}'"
53
+ }
54
+ end
55
+
56
+ def push_to_master
57
+ push_to_branch 'master'
58
+ end
59
+
60
+ def pull_request
61
+ # TODO
62
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec::Matchers.define :run_after do |thing|
4
+ match do |actual|
5
+ if actual['dependsOn'].is_a?(Array)
6
+ actual['dependsOn'].include?(thing)
7
+ else
8
+ actual['dependsOn'] == thing
9
+ end
10
+ end
11
+
12
+ failure_message do |_event|
13
+ "#{actual['blockType']} \"#{actual['blockName']}\" was expected to depend on #{actual['blockType']} \"#{thing}\"" \
14
+ "but depends on \"#{actual['dependsOn']}\" instead."
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec::Matchers.define :run_always do
4
+ match do |actual|
5
+ actual['condition'].include?('always()')
6
+ end
7
+
8
+ failure_message do |_event|
9
+ 'Failure!'
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec::Matchers.define :run_on_failure do
4
+ match do |actual|
5
+ actual['condition'].include?('failure()')
6
+ end
7
+
8
+ failure_message do |_event|
9
+ 'Failure!'
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec::Matchers.define :run_on_success do
4
+ match do |actual|
5
+ !actual['condition'] || actual['condition'].include?('succeeded()')
6
+ end
7
+
8
+ failure_message do |_event|
9
+ "Expected condition to include succeeded() but the actual value is #{actual['condition']} instead"
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec::Matchers.define :run_on_success_or_failure do
4
+ match do |actual|
5
+ actual['condition'].include?('succeededOrFailed()')
6
+ end
7
+
8
+ failure_message do |_event|
9
+ 'Failure!'
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec::Matchers.define :run_when do |event|
4
+ match do |actual|
5
+ actual['condition'].include?(event['condition'])
6
+ end
7
+
8
+ failure_message do |_event|
9
+ 'Failure!'
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/pipeline/matcher/run_when'
4
+ require 'rspec/pipeline/matcher/run_on_success'
5
+ require 'rspec/pipeline/matcher/run_on_failure'
6
+ require 'rspec/pipeline/matcher/run_on_success_or_failure'
7
+ require 'rspec/pipeline/matcher/run_always'
8
+ require 'rspec/pipeline/matcher/run_after'
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module RSpec::Pipeline # rubocop:disable Style/ClassAndModuleChildren
6
+ # This class loads the pipeline from a YAML file, also calling required methods to
7
+ # evaluate templates.
8
+ class PipelineLoader
9
+ def initialize(pipeline_name)
10
+ @pipeline_name = pipeline_name
11
+ @template_loader = TemplateLoader.new
12
+ end
13
+
14
+ def load
15
+ pipeline = YAML.load_file(@pipeline_name)
16
+
17
+ pipeline['stages'] = evaluate_stages(pipeline)
18
+
19
+ pipeline['stages'].each do |stage|
20
+ stage['jobs'] = evaluate_jobs(stage, pipeline)
21
+
22
+ stage['jobs'].each do |job|
23
+ job['steps'] = evaluate_steps(job, stage, pipeline)
24
+ end
25
+ end
26
+
27
+ pipeline
28
+ end
29
+
30
+ private
31
+
32
+ def evaluate_stages(pipeline)
33
+ templated_items = pipeline['stages'].map do |item|
34
+ if item['template']
35
+ load_template(item, 'stages', [pipeline])
36
+ else
37
+ item
38
+ end
39
+ end
40
+
41
+ templated_items.flatten.compact
42
+ end
43
+
44
+ def evaluate_jobs(stage, pipeline)
45
+ templated_items = stage['jobs'].map do |item|
46
+ if item['template']
47
+ load_template(item, 'jobs', [stage, pipeline])
48
+ else
49
+ item
50
+ end
51
+ end
52
+
53
+ templated_items.flatten.compact
54
+ end
55
+
56
+ def evaluate_steps(job, stage, pipeline)
57
+ templated_steps = job['steps'].map do |step|
58
+ if step['template']
59
+ load_template(step, 'steps', [job, stage, pipeline])
60
+ else
61
+ step
62
+ end
63
+ end
64
+
65
+ templated_steps.flatten.compact
66
+ end
67
+
68
+ def load_template(template, type, context)
69
+ template_name, template_repo = parse_template_metadata(template, context)
70
+
71
+ @template_loader.load(type, template_name, template_repo, template['parameters'])
72
+ end
73
+
74
+ def parse_template_metadata(template, context)
75
+ if template['template'].include? '@'
76
+ template['template'].split '@'
77
+ else
78
+ item_with_metadata = context.detect { |item| item['_meta'] && item['_meta']['repo'] }
79
+
80
+ if item_with_metadata
81
+ [template['template'], item_with_metadata['_meta']['repo']]
82
+ else
83
+ [template['template'], '.']
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ # TODO: handle parameter default values
6
+ # TODO: validate parameters
7
+ # - throw SyntaxError: parameter used but not defined
8
+ module RSpec::Pipeline # rubocop:disable Style/ClassAndModuleChildren
9
+ # This class loads and validates templates from YAML files.
10
+ class TemplateLoader
11
+ def initialize
12
+ @repo_stack = ['.']
13
+ end
14
+
15
+ def load(type, template, repo, parameters) # rubocop:disable Metrics/MethodLength
16
+ template_content = load_template_file(repo, template)
17
+
18
+ parameters&.each do |key, value|
19
+ template_content = template_content.gsub("${{ parameters.#{key} }}", value)
20
+ end
21
+
22
+ yaml_template = YAML.safe_load(template_content)
23
+
24
+ validate_parameters_not_found(parameters, yaml_template)
25
+ validate_required_parameters_not_defined(parameters, yaml_template)
26
+
27
+ yaml_template[type]&.map do |item|
28
+ item['_meta'] = {
29
+ 'repo' => repo
30
+ }
31
+ end
32
+
33
+ yaml_template[type]
34
+ end
35
+
36
+ private
37
+
38
+ def load_template_file(repo, template)
39
+ template_file = if repo == '.'
40
+ template
41
+ else
42
+ "spec/fixtures/#{repo}/#{template}"
43
+ end
44
+
45
+ raise "Expected to have a template file called #{template_file}" unless File.exist? template_file
46
+
47
+ File.read template_file
48
+ end
49
+
50
+ def validate_parameters_not_found(parameters, yaml_template)
51
+ parameters_not_found = (parameters || {}).keys -
52
+ (yaml_template['parameters'] || []).map { |parameter| parameter['name'] }
53
+
54
+ unless parameters_not_found.empty? # rubocop:disable Style/GuardClause
55
+ raise 'These parameters have been defined when you called the template but ' \
56
+ "they are not in the template: #{parameters_not_found.join(', ')}"
57
+ end
58
+ end
59
+
60
+ def validate_required_parameters_not_defined(parameters, yaml_template)
61
+ required_parameters_not_defined = (yaml_template['parameters'] || []).map { |parameter| parameter['name'] } -
62
+ (parameters || {}).keys
63
+
64
+ unless required_parameters_not_defined.empty? # rubocop:disable Style/GuardClause
65
+ raise "These parameters doesn't have default values but are not being defined when you called the " \
66
+ "template: #{required_parameters_not_defined.join(', ')}"
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module Pipeline
5
+ VERSION = '0.4.1'
6
+ end
7
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'pipeline/version'
4
+
5
+ module RSpec::Pipeline # rubocop:disable Style/ClassAndModuleChildren
6
+ class Error < StandardError; end
7
+ end
8
+
9
+ RSpec.configure do |config|
10
+ config.alias_example_group_to :pipeline, detailed: true, type: :pipeline, include_shared: true
11
+ config.alias_example_group_to :stage, detailed: true, type: :stage, include_shared: true
12
+ config.alias_example_group_to :job, detailed: true, type: :job, include_shared: true
13
+ config.alias_example_group_to :step, detailed: true, type: :step, include_shared: true
14
+
15
+ config.expect_with :rspec do |c|
16
+ c.syntax = :expect
17
+ end
18
+
19
+ config.add_setting :setup_fixtures, default: true
20
+ config.add_setting :fixtures_repos, default: []
21
+
22
+ config.before(:all) do
23
+ RSpec::Pipeline::FixtureLoader.load if RSpec.configuration.setup_fixtures? &&
24
+ RSpec.configuration.fixtures_repos.any?
25
+ end
26
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/pipeline'
4
+
5
+ require 'rspec/pipeline/matcher'
6
+ require 'rspec/pipeline/helper'
7
+ require 'rspec/pipeline/context'
8
+ require 'rspec/pipeline/fixture_loader'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-pipeline
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thiago Marinho
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-10-31 00:00:00.000000000 Z
11
+ date: 2021-11-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: byebug
@@ -30,7 +30,28 @@ email:
30
30
  executables: []
31
31
  extensions: []
32
32
  extra_rdoc_files: []
33
- files: []
33
+ files:
34
+ - CHANGELOG.md
35
+ - LICENSE.txt
36
+ - README.md
37
+ - bin/console
38
+ - bin/setup
39
+ - lib/rspec-pipeline.rb
40
+ - lib/rspec/pipeline.rb
41
+ - lib/rspec/pipeline/context.rb
42
+ - lib/rspec/pipeline/fixture_loader.rb
43
+ - lib/rspec/pipeline/helper.rb
44
+ - lib/rspec/pipeline/helper/type.rb
45
+ - lib/rspec/pipeline/matcher.rb
46
+ - lib/rspec/pipeline/matcher/run_after.rb
47
+ - lib/rspec/pipeline/matcher/run_always.rb
48
+ - lib/rspec/pipeline/matcher/run_on_failure.rb
49
+ - lib/rspec/pipeline/matcher/run_on_success.rb
50
+ - lib/rspec/pipeline/matcher/run_on_success_or_failure.rb
51
+ - lib/rspec/pipeline/matcher/run_when.rb
52
+ - lib/rspec/pipeline/pipeline_loader.rb
53
+ - lib/rspec/pipeline/template_loader.rb
54
+ - lib/rspec/pipeline/version.rb
34
55
  homepage: https://github.com/thiagomarinho/rspec-pipeline
35
56
  licenses:
36
57
  - MIT